I'm trying to create a component that contains multiple child components. I'm creating a collection of directives that other developers on my team can use to create custom search forms. For example, they would create markup like:
<search-panel name="myCustomSearch">
<search-field name="dates" type="dateRange"></search-field>
<search-field name="categories" type="categorySelector" data="{{categories}}"></search-field>
</search-panel>
I have a search panel directive and several search field directives. The search panel acts as a container and the search fields each provides a specific type of field (date range, type ahead, category selector, etc). Each search field has a value property in it's scope and I am trying to figure out a way to have the parent search panel directive have a property in it's scope that contains a key, value collection of all of the child search fields.
I've got both directives rendering correctly, but I'm not sure how to either make the search-panel aware of/have access to all the child components.
You can require in search-field
directive a search-panel
controller using require:'^searchPanel'
Then in link function you'll have a link to that controller, so those directives could add themselves into some array (I assuemm each search field has its isolated scope):
link: function(scope, elem, attrs, spCtrl) {
spCtrl.fields.push({name: attrs.name, scope: scope});
}
(ofcourse, you could add not whole scope, but some object, then watch for changes and update value field of that object.
The basic idea here is to create a common controller between the two and link them up in the directive linking.
When you create a directive you can pass the controllers into the fourth parameter as such:
app.directive('myDirective', function(){
return {
scope: true,
link: function postLink( scope, element, attrs, ctrls ){
//check ctrls for common link
}
}
}
An excellent example created in the angularui/bootstrap project is the Tabs Directive which has a common controller linking them up as an example on where to get started.
Hope this helps.
There is a good stackoverflow answer here by Mark Rajcok:
AngularJS directive controllers requiring parent directive controllers?
with a link to this very clear jsFiddle: http://jsfiddle.net/mrajcok/StXFK/
<div ng-controller="MyCtrl">
<div screen>
<div component>
<div widget>
<button ng-click="widgetIt()">Woo Hoo</button>
</div>
</div>
</div>
</div>
JavaScript is in the jsFiddle.
My implementation of composite component (multiple transcluding directives) is based on the following idea:
- capture child component $transclude with one directive
- output this child component with another directive
Live demo http://nickholub.github.io/angular-composite-component/#/
Demo source code https://github.com/nickholub/angular-composite-component
Directive source code https://github.com/nickholub/angular-composite-component/blob/master/app/directive/angular-composite-component.js
<div cs-composite>
<div cs-section="header">
Composite Component Header
</div>
<div cs-section="footer">
Composite Component Footer
<div>Random Value: {{randomValue}}</div>
<div>Percentage: {{percentage}}%</div>
</div>
</div>
Directive that captures content
.directive('csSection', function () {
return {
transclude: 'element',
priority: 100,
require: '^csComposite',
link: function (scope, element, attrs, ctrl, $transclude) {
var directiveTransclude = {
id: attrs.csSection,
transclude: $transclude,
element: element
};
ctrl.registerTransclude(directiveTransclude);
}
};
})
Directive that outputs content
.directive('csTransclude', function () {
return {
transclude: true,
link: function (scope, element, attrs) {
var id = attrs.csTransclude;
var directiveTransclude = scope.transcludes[id];
if (directiveTransclude) {
var selectedScope = scope.$new();
directiveTransclude.transclude(selectedScope, function (clone) {
element.append(clone);
});
}
}
};
})