Angular watch on parent directive's controller with require - javascript

I have a parent and child directive with a controller each, both of which are required in the child directive. I want to watch for changes to a property on the parent directive controller within the link function of the child directive. However, the watch function is fired on initialisation but not subsequently when the property is changed by a button in the parent directive's scope, or by the link function of the parent directive.
Please could somebody explain why this is and how I should resolve it?
Parent directive
myApp.directive('parentDirective', function ($timeout) {
return {
restrict: 'E',
scope: true,
controllerAs: 'parentCtrl',
controller: function () {
var vm = this;
vm.someProperty = true;
vm.toggle = function () {
vm.someProperty = !vm.someProperty;
}
},
link: function (scope, element, attrs, controller) {
$timeout(function () {
controller.toggle();
}, 1000);
}
} });
Child directive
myApp.directive('childDirective', function () {
return {
restrict: 'E',
scope: true,
require: ['childDirective', '^parentDirective'],
controllerAs: 'childCtrl',
controller: function () {
var vm = this;
vm.someProperty = '';
},
link: function (scope, element, attrs, controllers) {
var controller = controllers[0];
var parentController = controllers[1];
scope.$watch('parentController.someProperty', function () {
controller.someProperty = parentController.someProperty
? 'Hello world!' : 'Goodbye cruel world';
});
}
}
});
View
<parent-directive>
<button ng-click="parentCtrl.toggle()">Toggle message</button>
<child-directive>
<p>{{childCtrl.someProperty}}</p>
</child-directive>
</parent-directive>
Fiddle.

On the scope you're watching, the parent controller is 'parentCtrl' instead of 'parentController', so you're not actually watching the property you want to be. The following should work:
scope.$watch('parentCtrl.someProperty', function () {
controller.someProperty = parentController.someProperty
? 'Hello world!' : 'Goodbye cruel world';
});

Related

pass parameter to angular directive on click

I am trying to get parameter on click using directive.I want to get child data in the click event for checking has child or not.
.....
html
div ng-app="treeApp">
<ul>
<treeparent></treeparent>
</ul>
js
(function () {
var treeApp = angular.module('treeApp', []);
treeApp.directive('treeparent', function () {
return {
restrict: 'E',
template: "<button addchild child='m'>ajith</button><div id='new'></div>"
}
});
treeApp.directive('addchild', function ($compile) {
return {
scope: {
'child':'='
},
link: function (scope, element, attrs) {
debugger;
element.bind("click", function (scope,attrs) {
debugger;
//here i want to get hild ie 'm'
angular.element(document.getElementById('new')).append("<div><button button class='btn btn-default'>new one</button></div>");
});
}
}
});
})();
plz help me
So, i think scope.child is undefined becouse it is overlaps in declaring event.
You can define variable before event binding
link: function (scope, element, attrs) {
var child = scope.child;
element.bind("click", function (scope,attrs) {
// here you can use child
console.log('child', child);
});
}
or declare different argument names
link: function ($scope, $element, attrs) {
element.bind("click", function (scope,attrs) {
// here you can use $scope.child
console.log('$scope.child', $scope.child);
});
}
Is a callback has scope and attrs argument? May be it has only one $event argument?
link: function (scope, element, attrs) {
element.bind("click", function ($event) {
// here you can use child
console.log('child', scope.child);
});
}
Example for call method from directive in parent scope
parent template
<test-dir data-method="myFunc"></test-dir>
<button ng-click="myFunc('world')">click me</button>
or
<button test-dir data-method="myFunc" ng-click="myFunc('world')">click me</button>
directive
.directive('testDir', function() {
return {
scope: {
method: '=',
},
controller : function($scope) {
$scope.method = function(text) {
alert('hello '+text);
};
},
};
})

How to access a require'd controller from another controller?

I have an Angular 1.3 module that looks something like this (directive that requires the presence of a parent directive, using controllerAs):
angular.module('fooModule', [])
.controller('FooController', function ($scope) {
this.doSomething = function () {
// Accessing parentDirectiveCtrl via $scope
$scope.parentDirectiveCtrl();
};
})
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
};
});
Here I'm just using $scope to pass through parentDirectiveCtrl, which seems a little clunky.
Is there another way to access the require-ed controller from the directive's controller without the linking function?
You must use the link function to acquire the require-ed controllers, but you don't need to use the scope to pass the reference of the controller to your own. Instead, pass it directly to your own controller:
.directive('fooDirective', function () {
return {
require: ["fooDirective", "^parentDirective"],
link: function link(scope, element, attrs, ctrls) {
var me = ctrls[0],
parent = ctrls[1];
me.parent = parent;
},
controller: function(){...},
};
});
Be careful, though, since the controller runs prior to link, so within the controller this.parent is undefined, until after the link function runs. If you need to know exactly when that happens, you can always use a controller function to pass the parentDirective controller to:
link: function link(scope, element, attrs, ctrls) {
//...
me.registerParent(parent);
},
controller: function(){
this.registerParent = function(parent){
//...
}
}
There is a way to avoid using $scope to access parent controller, but you have to use link function.
Angular's documentation says:
Require
Require another directive and inject its controller as the fourth
argument to the linking function...
Option 1
Since controllerAs creates namespace in scope of your controller, you can access this namespace inside your link function and put required controller directly on controller of childDirective instead of using $scope. Then the code will look like this.
angular.module('app', []).
controller('parentController', function() {
this.doSomething = function() {
alert('parent');
};
}).
controller('childController', function() {
this.click = function() {
this.parentDirectiveCtrl.doSomething();
}
}).
directive('parentDirective', function() {
return {
controller: 'parentController'
}
}).
directive('childDirective', function() {
return {
template: '<button ng-click="controller.click()">Click me</button>',
link: function link(scope, element, attrs, parentDirectiveCtrl) {
scope.controller.parentDirectiveCtrl = parentDirectiveCtrl;
},
controller: 'childController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'
}
});
Plunker:
http://plnkr.co/edit/YwakJATaeuvUV2RBDTGr?p=preview
Option 2
I usually don't use controllers in my directives at all and share functionality via services. If you don't need to mess with isolated scopes of parent and child directives, simply inject the same service to both of them and put all functionality to service.
angular.module('app', []).
service('srv', function() {
this.value = '';
this.doSomething = function(source) {
this.value = source;
}
}).
directive('parentDirective', ['srv', function(srv) {
return {
template: '<div>' +
'<span ng-click="srv.doSomething(\'parent\')">Parent {{srv.value}}</span>' +
'<span ng-transclude></span>' +
'</div>',
transclude: true,
link: function(scope) { scope.srv = srv; }
};
}]).
directive('childDirective', ['srv', function(srv) {
return {
template: '<button ng-click="srv.doSomething(\'child\')">Click me</button>',
link: function link(scope) { scope.srv = srv; }
}
}]);
Plunker
http://plnkr.co/edit/R4zrXz2DBzyOuhugRU5U?p=preview
Good question! Angular lets you pass "parent" controller. You already have it as a parameter on your link function. It is the fourth parameter. I named it ctrl for simplicity. You do not need the scope.parentDirectiveCtrl=parentDirectiveCtrl line that you have.
.directive('fooDirective', function () {
return {
// Passing in parentDirectiveCtrl into $scope here
link: function link(scope, element, attrs, ctrl) {
// What you had here is not required.
},
controller: 'FooController',
controllerAs: 'controller',
bindToController: true,
require: '^parentDirective'};});
Now on your parent controller you have
this.doSomething=function().
You can access this doSomething as
ctrl.doSomething().

Accessing directive function from controller using $scope.$parent.$$childHead.functionName in AngularJS

I have created a directive.
angular.module('app')
.directive('navtree', function (service) {
return {
restrict: 'A',
scope: {},
link: function (scope, el) {
scope.loadNavtree = function(){
service.data()
.then(function (data) {
///Do something
});
}
scope.loadNavtree();
}
};
});
from my controller I can access the method using
$scope.$parent.$$childHead.loadNavtree();
Though this is working, I feel that this is not the right approach. I want to understand what are the disadvantages of accessing function defined in directive from your controller like this.
I looked this link but I was not able to follow
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
/// How to call takeTablet() available in directive from here?
});
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
scope.takeTablet = function() {
alert('from directive');//
}
}
};
});
this is not the correct approach because angular do not recommend to use its private variable to access to directive function so you need to get a good approach to do that here is an example to access the directive function from controller.
If you want to use isolated scopes you can pass a control object using bi-directional binding ('=') of a variable from the controller scope. In this way you can control also several instances of the same directive on a page.
plunkr
Controller/Directive:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.focusinControl = {
};
});
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
scope.internalControl = scope.control || {};
scope.internalControl.takenTablets = 0;
scope.internalControl.takeTablet = function() {
scope.internalControl.takenTablets += 1;
}
}
};
});
HTML:
<button ng-click="focusinControl.takeTablet()">Call directive function</button>
<h4>In controller scope:</h4>
{{focusinControl}}
<h4>In directive scope:</h4>
<focusin control="focusinControl"></focusin>
<h4>Without control object:</h4>
<focusin></focusin>

How to get isolated scope of a directive with ngrepeat and two way bind?

How we can get particular isolated scope of the directive while calling link function from controller(parent)?
I am having a directive and repeating it using ng-repeat. Whenever a button in the directive template is clicked it will call a function- Stop() in directive controller which in-turn calls function test() in parent controller, inside test() it will call a method dirSample () in directive's link function.
When I print the scope inside dirSample(), it prints the scope of the last created directive not the one which called it.
How can I get the scope of the directive which called it?
Find the pluker here
.directive('stopwatch', function() {
return {
restrict: 'AE',
scope: {
meri : '&',
control: '='
},
templateUrl: 'text.html',
link: function(scope, element, attrs, ctrl) {
scope.internalControl = scope.control || {};
scope.internalControl.dirSample = function(){
console.log(scope)
console.log(element)
console.log(attrs)
console.log(ctrl)
}
},
controllerAs: 'swctrl',
controller: function($scope, $interval)
{
var self = this;
self.stop = function()
{
console.log($scope)
$scope.meri(1)
};
}
}});
full code in plunker
I've changed the binding of your function from & to = since you need to pass a parameter. This means some syntax changes are in order, and also you need to pass the scope along the chain if you want to have it all the way at the end:
HTML:
<div stopwatch control="dashControl" meri="test"></div>
Controller:
$scope.test = function(scope)
{
console.log(scope);
$scope.dashControl.dirSample(scope);
}
Directive:
.directive('stopwatch', function() {
return {
restrict: 'AE',
scope: {
meri : '=',
control: '='
},
templateUrl: 'text.html',
link: function(scope, element, attrs, ctrl) {
scope.internalControl = scope.control || {};
scope.internalControl.dirSample = function(_scope){
console.log(_scope);
}
},
controllerAs: 'swctrl',
controller: function($scope, $interval)
{
var self = this;
self.stop = function()
{
console.log($scope);
$scope.meri($scope);
};
}
}});
Plunker

AngularJS: Watch a property of the parent scope

I'm quite new to AngularJS and I'm trying to understand a few things.
First of all, I have my controller of which I will place a snippet here:
var OfficeUIRibbon = angular.module('OfficeUIRibbon');
// Defines the OfficeUIRibbon controller for the application.
OfficeUIRibbon.controller('OfficeUIRibbon', ['$scope', '$http', function($scope, $http) {
var ribbon = this;
ribbon.setApplicationMenuAsActive = function() {
ribbon.applicationMenuActive = true;
}
}
Then I have a directive:
var OfficeUIRibbon = angular.module('OfficeUIRibbon');
OfficeUIRibbon.directive('ribbonApplicationMenu', function() {
return {
restrict: 'E',
replace: false,
scope: {
data: '#'
},
templateUrl: function(element, attributes) {
return attributes.templateurl;
}
}
});
The directive is called like this:
<ribbon-application-menu templateUrl="/OfficeUI.Beta/Resources/Templates/ApplicationMenu.html" data="/OfficeUI.Beta/Resources/JSon/Ribbon/ribbon.json"></ribbon-application-menu>
This does all work and in my template for the directive, the following is placed:
<div id="application" id="applicationMenuHolder" ng-controller="OfficeUIRibbon as OfficeUIRibbon" ng-show="OfficeUIRibbon.applicationMenuActive"...
From inside another element, when I execute a click a function on my controller is executed:
ng-click="OfficeUIRibbon.setApplicationMenuAsActive()"
Here's the directive from the other element:
OfficeUIRibbon.directive('ribbon', function() {
return {
restrict: 'E',
replace: false,
scope: {
data: '#'
},
templateUrl: function(element, attributes) {
return attributes.templateurl;
}
}
});
This function does change the property "applicationMenuActive" on the ribbon itself, which should make the item in the directive template show up.
However, this is not working. I'm guessing I need to watch this property so the view get's updated accordingly.
Anyone has an idea on how this could be done?

Categories

Resources