Let's imagine, we have one child directive <child/>, that takes ng-model and ng-change and does some actions with them. And we have two kind of wrappers <w1/> and <w2/>, that contain <child/>.
W1 should pass ng-model through directly to <child/>
W2 should pass some inner object as model to <child/>
in first case i would use require: '^ngModel'
in second : require: 'ngModel' but i need them to work simultaneously
so model is simple object that can be passed easily through.
<wrapper ng-model="foo"></wrapper>
Wrapper:
module
.directive('wrapper', function () {
return {
restrict: 'E',
template: "<child ng-model='ngModel'></child>",
scope: {
ngModel:'='
},
link: function ($scope) {
}
};
});
Child:
module
.directive('child', function () {
return {
restrict: 'E',
require: 'ngModel',
template: "<div>some wierd stuff</div>",
scope: {
},
link: function ($scope, iElem, iAttr, ngModel) {
var a = ngModel.$viewValue;
}
};
});
Related
i made this directive that i call in my html like
<my-datepicker />
what i want to do now, is to expend it, so that i can do something like this
<my-datepicker >
<my-day>{{date}}</my-day>
<my-month>{{moth}}</my-month>
<my-year>{{year}}</my-year>
</my-datepicker>
resulting in my-datepicker directive can get the child elements.
how can you do this? do you need to make the childs like standanlone directive, an how can you get the value from them in the parent(my-datepicker) directive
You can access parent directive controller like this:
app.directive('myDay', function () {
return {
restrict: 'E',
require: '^myDatepicker',
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.doSomething();
}
};
});
In parent:
app.directive('myDatepicker', function () {
return {
restrict: 'E',
controller: function () {
this.doSomething = function () {
alert('Do something in parent');
};
}
};
});
I think the best way to extend your directive is by passing parameters (date month ec... ) as attributes.
Your html should be somethig like this
<my-datepicker day="day" month="month" year="year"/>
Inside your directive you should be able to retrieve those variables by declaring them in the scope.
function myDatepicker() {
var directive = {
templateUrl: 'yourtemplate.html',
restrict: 'E',
link: link,
scope: {
date: '=',
month: '=',
year: '='
}
}
return directive;
function link(scope, element, attrs) {
// use here scope.date scope.month and scope.year
}
};
This code is just a test that I'm putting together so I can understand the logic.
Basically what I want to happen is to get a value passed into the directive. Based on that value, I want to add several attributes to the scope.
This first test is just to update the attribute that was already passed in.
Directive:
angular.module('bd.common')
.directive('bdPopoverLegend', function () {
return {
restrict: 'AE',
replace: true,
scope: {
legendInfo: '=info'
},
templateUrl: '/bd/common/ctl/bdPopoverLegend.html',
link: function (scope, element, attrs) {
scope.legendInfo.test = 'hello world 4';
}
};
});
Template:
<div>{{legendInfo.test}}</div>
Calling the Directive:
<div bd-popover-legend="" info="{test: 'hello world 3'}">
The value never changes, it stays as 'hello world 3' and not 'hello world 4'
Try this:
.directive('bdPopoverLegend', function () {
return {
restrict: 'AE',
replace: true,
scope: {
legendInfo: '=info'
},
templateUrl: '/bd/common/ctl/bdPopoverLegend.html',
link: function (scope, element, attrs) {
scope.$watch('legendInfo', function (watchInfo) {
watchInfo.test = 'hello world 4';
});
}
};
});
i have this html
<div ng-app="myApp">
<parent>
<child></child>
</parent>
</div>
and the following Angular code
var myApp = angular.module('myApp',[]);
myApp.directive('parent', function() {
return {
transclude: true,
link: function(scope, element, attrs) {
scope.getContent = function () {
console.log('called getContent');
}
},
restrict: 'E',
template: '<span>parent <span ng-transclude></span></span>'
}
});
myApp.directive('child', function() {
return {
restrict: 'E',
scope:true,
template: '<div>child</div>',
link: function(scope, element, attrs) {
scope.getContent();
}
}
});
JSfiddle of the above is here
my problem in this example is i can see the inheritance working and transclusion working in isolation but when i try to combine the two, the child directive has no knowledge of the parent scope and hence the function getContent. So no console.log and before that the child directive errors that scope.getContent is undefined.
I realise that this might be that the child directive is no longer a child having been transcluded so i was thinking i need to start playing with post and prelink functions in the compile method maybe? or do i need to define a custom transclude function in the parent?
Any pointers for the code or further reading appreciated - i have read and similar questions on here about this kind of thing but am finding it difficult to follow and hoping some one can solve my hello world to kickstart my understanding
thanks in advance
You should read that article: http://www.undefinednull.com/2014/07/07/practical-guide-to-prelink-postlink-and-controller-methods-of-angular-directives/
Basically, your child link is called before the parent one. What you want to do is use your parent pre-link function so scope.getContent is defined beforte the child is linked.
link: {
pre: function (scope, element, attrs) {
scope.getContent = function () {
console.log('called');
}
}
}
JSfiddle: http://jsfiddle.net/floribon/gpwasrkz/3/
Have a look at "Creating Directives that Communicate" in the docs. Maybe this is a solution for what you are trying o achieve. You can require the parent controller and call it's functions:
var myApp = angular.module('myApp', []);
myApp.directive('parent', function () {
return {
transclude: true,
controller: function ($scope) {
this.getContent = function () {
console.log('called')
}
},
link: function (scope, element, attrs) {},
restrict: 'E',
template: '<span>parent <span ng-transclude></span></span>'
}
});
myApp.directive('child', function () {
return {
restrict: 'E',
scope: true,
require: '^parent',
template: '<div>child</div>',
link: function (scope, element, attrs, ctrl) {
ctrl.getContent();
}
}
});
See this fiddle.
This is just a general question about Angular directives. Why does angular choose to have a function that returns an object and not just list the object directly as the second parameter?
in other words, why does it look like this:
app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});
and not this:
app.directive('helloWorld',{
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
});
so you can inject dependencies
for example:
app.directive('helloWorld', function($rootScope) {
return {
restrict: 'AE',
replace: 'true',
template: '<h3>Hello World!!</h3>'
};
});
angular will inject the $rootScope into your directive.
Because when you instantiate a directive you want a new instance of it. The function will instantiate a new object for you as opposed to returning the same object.
Additionally the function can also be used for Dependency Injection.
I believe this is to allow for dependency injection ex.
.directive('someDirective', ['$filter', function ($filter) {
'use strict';
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
});
}
};
}]);
I'm not sure this is the way to do this, but my goal is the following:
I have a parent directive
Inside the parent directive's block, I have a child directive that will get some input from the user
The child directive will set a value in the parent directive's scope
I can take it from there
Of course the problem is that the parent and child directives are siblings. So I don't know how to do this. Note - I do not want to set data in the
Fiddle: http://jsfiddle.net/rrosen326/CZWS4/
html:
<div ng-controller="parentController">
<parent-dir dir-data="display this data">
<child-dir></child-dir>
</parent-dir>
</div>
Javascript
var testapp = angular.module('testapp', []);
testapp.controller('parentController', ['$scope', '$window', function ($scope, $window) {
console.log('parentController scope id = ', $scope.$id);
$scope.ctrl_data = "irrelevant ctrl data";
}]);
testapp.directive('parentDir', function factory() {
return {
restrict: 'ECA',
scope: {
ctrl_data: '#'
},
template: '<div><b>parentDir scope.dirData:</b> {{dirData}} <div class="offset1" ng-transclude></div> </div>',
replace: false,
transclude: true,
link: function (scope, element, attrs) {
scope.dirData = attrs.dirData;
console.log("parent_dir scope: ", scope.$id);
}
};
});
testapp.directive('childDir', function factory() {
return {
restrict: 'ECA',
template: '<h4>Begin child directive</h4><input type="text" ng-model="dirData" /></br><div><b>childDir scope.dirData:</b> {{dirData}}</div>',
replace: false,
transclude: false,
link: function (scope, element, attrs) {
console.log("child_dir scope: ", scope.$id);
scope.dirData = "No, THIS data!"; // default text
}
};
});
If you want that kind of communication, you need to use require in the child directive. That will require the parent controller so you need a controller there with the functionality you want the children directives to use.
For example:
app.directive('parent', function() {
return {
restrict: 'E',
transclude: true,
template: '<div>{{message}}<span ng-transclude></span></div>',
controller: function($scope) {
$scope.message = "Original parent message"
this.setMessage = function(message) {
$scope.message = message;
}
}
}
});
The controller has a message in the $scope and you have a method to change it.
Why one in $scope and one using this? You can't access the $scope in the child directive, so you need to use this in the function so your child directive will be able to call it.
app.directive('child', function($timeout) {
return {
restrict: 'E',
require: '^parent',
link: function(scope, elem, attrs, parentCtrl) {
$timeout(function() {
parentCtrl.setMessage('I am the child!')
}, 3000)
}
}
})
As you see, the link receives a fourth param with the parentCtrl (or if there is more than one, an array). Here we just wait 3 seconds until we call that method we defined in the parent controller to change its message.
See it live here: http://plnkr.co/edit/72PjQSOlckGyUQnH7zOA?p=preview
First, watch this video. It explains it all.
Basically, you need to require: '^parentDir' and then it will get passed into your link function:
link: function (scope, element, attrs, ParentCtrl) {
ParentCtrl.$scope.something = '';
}