Angular-UIアコーディオン効果のソースコード分析



Angular Ui Accordion Effect Source Code Analysis



/* * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ * Version: 0.14.3 - 2015-10-23 * License: MIT */ angular.module('ui.bootstrap', ['ui.bootstrap.tpls','ui.bootstrap.accordion','ui.bootstrap.collapse']) angular.module('ui.bootstrap.tpls', ['template/accordion/accordion-group.html','template/accordion/accordion.html']) angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) .constant('uibAccordionConfig', {//constant can register an existing variable value as a service closeOthers: true }) .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { // This array keeps track of the accordion groups This.groups = []//Use arrays to save all accordion groups // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to // ensure that all accordion groups are closed, unless other closures are not this.closeOthers = function(openGroup) { //close the function if oneAtATime exists and is true Var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers//Check if $attrs.closeOthers has been defined if (closeOthers) { angular.forEach(this.groups, function(group) { // traverse all accordion groups If (group !== openGroup) { //If there is no open accordion group, it will be closed group.isOpen = false } }) } } // This is called from the accordion-group directive to add itself to the accordion // function to be called when adding an object to the accordion group this.addGroup = function(groupScope) { //added scope var that = this this.groups.push(groupScope) groupScope.$on('$destroy', function(event) {//When the monitor is destroyed, the accordion group is removed. that.removeGroup(groupScope) }) } // This is called from the accordion-group directive when to remove itself //To call when removing the accordion group, you need to remove the corresponding item in the array this.removeGroup = function(group) { var index = this.groups.indexOf(group) if (index !== -1) { this.groups.splice(index, 1) } } }]) // The accordion directive simply sets up the directive controller. This command simply sets up the controller for this directive and adds a css type. // and adds an accordion CSS class to itself element. // Corresponding instruction template .directive('uibAccordion', function() { return { controller: 'UibAccordionController', controllerAs: 'accordion', // controller alias Transclude: true, / / ​​can be replaced templateUrl: function(element, attrs) 'template/accordion/accordion.html' } }) // The accordion-group directive indicates a block of html that will expand and collapse in an This command indicates that an html is expanded or collapsed in an accordion effect. // This content is straight in the template. .directive('uibAccordionGroup', function() { return { Require: '^uibAccordion', // We need this directive to be inside an accordion //We need this command to put an accordion effect in it Transclude: true, // It transcludes the contents of the directive into the template to allow the contents of this directive to be replaced in the template replace: true, // The element containing the directive will be replaced with the template templateUrl: function(element, attrs) 'template/accordion/accordion-group.html' , scope: { Heading: '@', // Interpolate the heading attribute onto this scope //pass the string isOpen: '=?', //= two-way binding isDisabled: '=?' //? Tells the instruction not to throw an exception if it does not find a dependent instruction. }, Controller: function() { //Set the heading of the current object this.setHeading = function(element) { this.heading = element } }, link: function(scope, element, attrs, accordionCtrl) { accordionCtrl.addGroup(scope)//Add the current scope to the accordion array scope.openClass = attrs.openClass || 'panel-open'//Expanded styles scope.panelClass = attrs.panelClass//Get the specific defined class name Scope.$watch('isOpen', function(value) {//Listen isOpen to determine if a change has occurred element.toggleClass(scope.openClass, !!value)//If isOpen is true, add the expanded style if (value) { accordionCtrl.closeOthers(scope) } }) scope.toggleOpen = function($event) { // Accordion Switch Function If (!scope.isDisabled) { //execute when scope.isDisabled is false if (!$event || $event.which === 32) { scope.isOpen = !scope.isOpen } } } } } }) // Use accordion-heading below an accordion-group to provide a heading containing HTML //Use the accordion group below the accordion title to provide a content tag // corresponding command I can have markup, too!.directive('uibAccordionHeading', function() { return { transclude: true, // Grab the contents to be used as the heading template: '', // In effect remove this element! replace: true, require: '^uibAccordionGroup', link: function(scope, element, attrs, accordionGroupCtrl, transclude) { // Pass the heading to the accordion-group controller // so that it can be transcluded into the right place in the template // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] accordionGroupCtrl.setHeading(transclude(scope, angular.noop)) } } }) // Use in the accordion-group template to indicate where you want the heading to be transcluded // You must provide the property on the accordion-group controller that will hold the transcluded element // {{heading}} .directive('uibAccordionTransclude', function() { return { require: ['?^uibAccordionGroup', '?^accordionGroup'], link: function(scope, element, attrs, controller) { controller = controller[0] ? controller[0] : controller[1] // Delete after we remove deprecation scope.$watch(function() { return controller[attrs.uibAccordionTransclude] }, function(heading) {//{{heading}} if (heading) { element.find('span').html('') element.find('span').append(heading) } }) } } }) /* Deprecated accordion below //Deprecated accordion dead code angular.module('ui.bootstrap.accordion') .value('$accordionSuppressWarning', false) // .controller('AccordionController', ['$scope', '$attrs', '$controller', '$log', '$accordionSuppressWarning', function($scope, $attrs, $controller, $log, $accordionSuppressWarning) { if (!$accordionSuppressWarning) { $log.warn('AccordionController is now deprecated. Use UibAccordionController instead.') } Angular.extend(this, $controller('UibAccordionController', { //extend the current controller $scope: $scope, $attrs: $attrs })) / / Load the controller and pass in a scope, the same as AngularJS at runtime }]) .directive('accordion', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { restrict: 'EA', controller: 'AccordionController', controllerAs: 'accordion', //controller alias transclude: true, replace: false, templateUrl: function(element, attrs) Return attrs.templateUrl , link: function() { if (!$accordionSuppressWarning) { $log.warn('accordion is now deprecated. Use uib-accordion instead.') } } } }]) .directive('accordionGroup', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { require: '^accordion', // We need this directive to be inside an accordion restrict: 'EA', transclude: true, // It transcludes the contents of the directive into the template replace: true, // The element containing the directive will be replaced with the template templateUrl: function(element, attrs) return attrs.templateUrl , scope: { Heading: '@', //pass the string value // Interpolate the heading attribute onto this scope isOpen: '=?', //= two-way binding isDisabled: '=?' //=Two-way binding }, Controller: function() { // Make the heading of this directive equal to the passed argument this.setHeading = function(element) { this.heading = element } }, link: function(scope, element, attrs, accordionCtrl) { if (!$accordionSuppressWarning) { $log.warn('accordion-group is now deprecated. Use uib-accordion-group instead.') } accordionCtrl.addGroup(scope) scope.openClass = attrs.openClass || 'panel-open' scope.panelClass = attrs.panelClass scope.$watch('isOpen', function(value) { element.toggleClass(scope.openClass, !!value) if (value) { accordionCtrl.closeOthers(scope) } }) scope.toggleOpen = function($event) { if (!scope.isDisabled) { if (!$event || $event.which === 32) { scope.isOpen = !scope.isOpen } } } } } }]) .directive('accordionHeading', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { restrict: 'EA', transclude: true, // Grab the contents to be used as the heading template: '', // In effect remove this element! replace: true, require: '^accordionGroup', link: function(scope, element, attr, accordionGroupCtrl, transclude) { if (!$accordionSuppressWarning) { $log.warn('accordion-heading is now deprecated. Use uib-accordion-heading instead.') } // Pass the heading to the accordion-group controller // so that it can be transcluded into the right place in the template // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] accordionGroupCtrl.setHeading(transclude(scope, angular.noop)) } } }]) .directive('accordionTransclude', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { return { require: '^accordionGroup', link: function(scope, element, attr, controller) { if (!$accordionSuppressWarning) { $log.warn('accordion-transclude is now deprecated. Use uib-accordion-transclude instead.') } scope.$watch(function() { return controller[attr.accordionTransclude] }, function(heading) { if (heading) { element.find('span').html('') element.find('span').append(heading) } }) } } }]) */ / / Specific content display module, no independent scope, use the same scope with the parent angular.module('ui.bootstrap.collapse', []) // directive .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { Var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null//Determine if there is a $animateCss service injection, if any, get this service return { Link: function(scope, element, attrs) {//link function / / Expand the function function expand() { element.removeClass('collapse') .addClass('collapsing') .attr('aria-expanded', true)//aria-expanded indicates the expanded state. The default is undefined, indicating that the current expanded state is unknown. Here is the expansion .attr('aria-hidden', false)//This means to close If ($animateCss) {//If there is a $animateCss service, it is used to add animation $animateCss(element, { addClass: 'in', //in style is block easing: 'ease', to: { height: element[0].scrollHeight + 'px' } }).start().finally(expandDone) } else {//If not, use the animation module that comes with ng $animate.addClass(element, 'in', { to: { height: element[0].scrollHeight + 'px' } }).then(expandDone)//return a promise object, use then to handle successful callbacks } } / / After the callback Function expandDone() {//The specific success callback function is used to manipulate the class element.removeClass('collapsing') .addClass('collapse')//display:none .css({height: 'auto'}) } / / contraction function function collapse() { If (!element.hasClass('collapse') && !element.hasClass('in')) {//if element does not have a collapse style, and there is no in style return collapseDone() } element // IMPORTANT: The height must be set before adding 'collapsing' class. // Otherwise, the browser attempts to animate from height 0 (in // collapsing class) to the given height here. .css({height: element[0].scrollHeight + 'px'}) //Set the height // initially all panel collapse have the collapse class, this removal // prevents the animation from jumping to collapsed state .removeClass('collapse') //Remove collapse, which is to remove display:none .addClass('collapsing') //Add collapsing, height 0 .attr('aria-expanded', false) .attr('aria-hidden', true) if ($animateCss) { $animateCss(element, { removeClass: 'in', to: {height: '0'} }).start().finally(collapseDone) } else { $animate.removeClass(element, 'in', {//Set animation, height is 0 to: {height: '0'} }).then(collapseDone) } } / / contraction callback function function collapseDone() { Element.css({height: '0'}) // Required so that collapse works when animation is disabled element.removeClass('collapsing')//collapsing sets the height to 0 .addClass('collapse')//display:none } / / Listen to attrs.uibCollapse is the value of !isOpen to determine the expansion and closure scope.$watch(attrs.uibCollapse, function(shouldCollapse) { if (shouldCollapse) { collapse() } else { expand() } }) } } }]) /* Deprecated collapse below dead code angular.module('ui.bootstrap.collapse') .value('$collapseSuppressWarning', false) .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) { var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null return { link: function(scope, element, attrs) { if (!$collapseSuppressWarning) { $log.warn('collapse is now deprecated. Use uib-collapse instead.') } function expand() { element.removeClass('collapse') .addClass('collapsing') .attr('aria-expanded', true) .attr('aria-hidden', false) if ($animateCss) { $animateCss(element, { easing: 'ease', to: { height: element[0].scrollHeight + 'px' } }).start().done(expandDone) } else { $animate.animate(element, {}, { height: element[0].scrollHeight + 'px' }).then(expandDone) } } function expandDone() { element.removeClass('collapsing') .addClass('collapse in') .css({height: 'auto'}) } function collapse() { if (!element.hasClass('collapse') && !element.hasClass('in')) { return collapseDone() } element // IMPORTANT: The height must be set before adding 'collapsing' class. // Otherwise, the browser attempts to animate from height 0 (in // collapsing class) to the given height here. .css({height: element[0].scrollHeight + 'px'}) // initially all panel collapse have the collapse class, this removal // prevents the animation from jumping to collapsed state .removeClass('collapse in') .addClass('collapsing') .attr('aria-expanded', false) .attr('aria-hidden', true) if ($animateCss) { $animateCss(element, { to: {height: '0'} }).start().done(collapseDone) } else { $animate.animate(element, {}, { height: '0' }).then(collapseDone) } } function collapseDone() { element.css({height: '0'}) // Required so that collapse works when animation is disabled element.removeClass('collapsing') .addClass('collapse') } scope.$watch(attrs.collapse, function(shouldCollapse) { if (shouldCollapse) { collapse() } else { expand() } }) } } }]) */ angular.module('template/accordion/accordion-group.html', []).run(['$templateCache', function($templateCache) { $templateCache.put('template/accordion/accordion-group.html', ' ' + ' ' + '

' + ' {{heading}} ' + '

' + ' ' + ' ' + ' ' + ' ' + ' ' + '') }]) angular.module('template/accordion/accordion.html', []).run(['$templateCache', function($templateCache) { $templateCache.put('template/accordion/accordion.html', ' ') }])

{{heading}}

Toggle last panel Enable / Disable first panel

Open only one at a time This content is straight in the template. {{group.content}}

The body of the uib-accordion group grows to fit the contents



Add Item {{item}} Hello

Please, to delete your account, click the button below

Delete I can have markup, too!This is just some content to illustrate fancy headings.
angular.module('ui.bootstrap.demo').controller('AccordionDemoCtrl', function ($scope) { $scope.oneAtATime = true $scope.groups = [ { title: 'Dynamic Group Header - 1', content: 'Dynamic Group Body - 1' }, { title: 'Dynamic Group Header - 2', content: 'Dynamic Group Body - 2' } ] $scope.items = ['Item 1', 'Item 2', 'Item 3'] $scope.addItem = function() { var newItemNo = $scope.items.length + 1 $scope.items.push('Item ' + newItemNo) } $scope.status = { isFirstOpen: true, isFirstDisabled: false } })

最初の部分はAngularプラグインのソースコードであり、その後に公式Webサイトの例が続きます( https://angular-ui.github.io/bootstrap/ )。
プラグインの最も中心的な部分は、異なる命令間の通信を実装することです。プラグインを実装する方法は、UibAccordionControllerとuibCollapseの2つのスコープのスコープの下で通信を実装することです。 。通信の方法は、スコープの継承を通じて親子命令を使用することです。 3つのパラメーターheading isOpen isDisabledを継承(双方向バインディング)します。次に、別の手順でisOpenを監視することにより、さまざまな効果を得ることができます。