35

有一个可重用组件的角度指令,公开可以从控制器访问的公共 API 的最佳实践是什么?因此,当组件有多个实例时,您可以从控制器访问

angular.directive('extLabel', function {
    return {
        scope: {
            name: '@',
            configObj: '='
        },
        link: function(scope, iElement, iAttrs) {
            // this could be and exposed method
            scope.changeLabel = function(newLabel) {
                scope.configObj.label = newLabel;
            }
        }
    }
});

然后当有:

<ext-label name="extlabel1" config-obj="label1"></ext-label>
<ext-label name="extlabel2" config-obj="label2"></ext-label>
<ext-label name="extlabel3" config-obj="label3"></ext-label>

如何在控制器中访问 extLabel2 的 scope.changeLabel?

是否有意义?

4

5 回答 5

23

这对你有用吗?

angular.directive('extLabel', function() {
    return {
        restrict: 'E',
        scope: {
            api: '='
        },
        link: function(scope, iElement, iAttrs) {
            scope.api = {
                    doSomething: function() { },
                    doMore: function() { }
                };
        }
    };
});

从包含父母

<ext:label api="myCoolApi"></ext:label>

在控制器中

$scope.myCoolApi.doSomething();
$scope.myCoolApi.doMore();
于 2014-06-11T10:58:12.203 回答
6

我喜欢 Andrej 的并经常使用这种模式,但我想建议对其进行一些更改

angular.directive('extLabel', function {
    return {
        scope: {
            api: '=?',
            configObj: '='
        },
        // A controller, and not a link function. From my understanding, 
        // try to use the link function for things that require post link actions 
        // (for example DOM manipulation on the directive)
        controller: ['$scope', function($scope) {

          // Assign the api just once
          $scope.api = {
            changeLabel: changeLabel
          };

          function changeLabel = function(newLabel) {
            $scope.configObj.label = newLabel;
          }
        }]
    }
});



<ext-label name="extlabel1" config-obj="label1"></ext-label>
<ext-label api="label2api" name="extlabel2" config-obj="label2"></ext-label>
<ext-label name="extlabel3" config-obj="label3"></ext-label>

当然在控制器中label2api.changeLabel('label')

于 2015-06-04T09:50:27.467 回答
5

在我的 Angular 应用程序中编写指令以实例化dygraph图表时,我遇到了这个问题。虽然大部分工作可以通过数据绑定来完成,但 API 的某些部分需要访问 dygraph 对象本身。我$emit()通过一个事件解决了它:

'use strict';
angular.module('dygraphs', []);

angular.module('dygraphs').directive('mrhDygraph', function ($parse, $q) {
    return {
        restrict: 'A',
        replace: true,
        scope: {data: '=', initialOptions: '@', options: '='},
        link: function (scope, element, attrs) {
            var dataArrived = $q.defer();
            dataArrived.promise.then(function (graphData) {
                scope.graph = new Dygraph(element[0], graphData, $parse(scope.initialOptions)(scope.$parent));
                return graphData.length - 1;
            }).then(function(lastPoint) {
                scope.graph.setSelection(lastPoint);
                scope.$emit('dygraphCreated', element[0].id, scope.graph);
            });
            var removeInitialDataWatch = scope.$watch('data', function (newValue, oldValue, scope) {
                if ((newValue !== oldValue) && (newValue.length > 0)) {
                    dataArrived.resolve(newValue);
                    removeInitialDataWatch();
                    scope.$watch('data', function (newValue, oldValue, scope) {
                        if ((newValue !== oldValue) && (newValue.length > 0)) {
                            var selection = scope.graph.getSelection();
                            if (selection > 0) {
                                scope.graph.clearSelection(selection);
                            }
                            scope.graph.updateOptions({'file': newValue});
                            if ((selection >= 0) && (selection < newValue.length)) {
                                scope.graph.setSelection(selection);
                            }
                        }
                    }, true);
                    scope.$watch('options', function (newValue, oldValue, scope) {
                        if (newValue !== undefined) {
                            scope.graph.updateOptions(newValue);
                        }
                    }, true);
                }
            }, true);
        }
    };
});

事件的参数dygraphCreated包括元素 id 以及 dygraph 对象,允许在同一范围内使用多个 dygraph。

于 2013-08-30T13:47:55.203 回答
2

在我看来,父母不应该访问儿童范围。你怎么知道哪一个使用,哪一个不使用。控制器应该只访问他自己的范围或他的父范围。否则它会破坏封装。

如果要更改标签,您真正需要做的就是更改 label1/label2/label3 变量值。启用数据绑定后,它应该可以工作。在您的指令中,$watch如果每次更改时都需要一些逻辑,您可以使用它。

angular.directive('extLabel', function {
    return {
        scope: {
            name: '@',
            configObj: '='
        },
        link: function(scope, iElement, iAttrs) {
            scope.$watch("configObj", function() {
                // Do whatever you need to do when it changes
            });
        }
    }  
});
于 2013-08-30T13:05:43.237 回答
1

对您想要上一个和下一个的元素使用这些指令:

<carousel>
 <slide>
   <button class="action" carousel-next> Next </button>
   <button class="action" carousel-prev> Back </button>
 </slide>
</carousel>

.directive('carouselNext', function () {
       return {
        restrict: 'A',
        scope: {},
        require: ['^carousel'],
        link: function (scope, element, attrs, controllers) {
            var carousel = controllers[0];
            function howIsNext() {
                if ((carousel.indexOfSlide(carousel.currentSlide) + 1) === carousel.slides.length) {
                    return 0;
                } else {
                    return carousel.indexOfSlide(carousel.currentSlide) + 1;
                }
            }
            element.bind('click', function () {
                carousel.select(carousel.slides[howIsNext()]);
            });
        }
    };
})

.directive('carouselPrev', function () {
    return {
        restrict: 'A',
        scope: {},
        require: ['^carousel'],
        link: function (scope, element, attrs, controllers) {
            var carousel = controllers[0];
            function howIsPrev() {
                if (carousel.indexOfSlide(carousel.currentSlide) === 0) {
                    return carousel.slides.length;
                } else {
                    return carousel.indexOfSlide(carousel.currentSlide) - 1;
                }
            }
            element.bind('click', function () {
                carousel.select(carousel.slides[howIsPrev()]);
            });
        }
    };
})
于 2015-07-21T18:23:39.350 回答