web-dev-qa-db-ja.com

再利用可能なコンポーネントであるディレクティブからパブリックAPIを公開する方法は?

再利用可能なコンポーネントであるangularにディレクティブがある場合、コントローラからアクセスできるパブリック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にアクセスするにはどうすればよいですか?

それは意味がありますか?

35
Safajirafa

これはうまくいきますか?

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();
22
Andrej Kaurin

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')

6
Vall3y

私のAngularアプリケーションで dygraph チャートをインスタンス化するディレクティブを記述するときにこの問題に直面しました。ほとんどの作業はデータバインディングで実行できますが、 APIはdygraphオブジェクト自体へのアクセスを必要とします。イベントを$emit() ingすることで解決しました:

'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を使用できます。

5
Max

私の意見では、親は子スコープにアクセスすべきではありません。どれを使用し、どれを使用しないかをどのようにして知ることができますか。コントローラは、自分のスコープまたは親スコープのみにアクセスする必要があります。それ以外の場合は、カプセル化が解除されます。

ラベルを変更したい場合、必要なのは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
            });
        }
    }  
});
2
Benoit Tremblay

前後に移動する要素でこれらのディレクティブを使用します。

<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()]);
            });
        }
    };
})