Как открыть публичный 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>
Как я могу получить доступ к области scope.changeLabel extLabel2 в контроллере?
Имеет ли смысл?
Ответы
Ответ 1
Это работает для вас?
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();
Ответ 2
Я столкнулся с этой проблемой при написании директивы о создании образца dygraph в моих приложениях Angular. Хотя большую часть работы можно выполнить с привязкой данных, некоторые части 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
включают идентификатор элемента, а также объект dygraph, позволяя использовать несколько dygraphs в пределах одного и того же объема.
Ответ 3
Мне нравится 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')
Ответ 4
По моему мнению, родитель не должен иметь доступ к детской области. Как вы узнаете, какой из них использовать и какой из них не использовать. Контроллер должен получить доступ к своей области или только к родительским областям. В противном случае это нарушает инкапсуляцию.
Если вы хотите изменить свой ярлык, все, что вам действительно нужно сделать, это изменить значение переменной 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
});
}
}
});
Ответ 5
Используйте эти директивы для элемента, который вы хотите перейти в предыдущий и следующий:
<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()]);
});
}
};
})