AngularJS; Send a function from the parent into directive scope - javascript

I'm creating a reusable component/widget as a directive using a template and isolated scope. I'd like to be able to also send a callback into the directive and call it in the widget. Is this possible?
Something like...
mainView template:
<my-widget callback="someFunction"></my-widget>
directive:
return {
restrict: 'E',
scope: {
callback: '='
},
templateUrl: '/partials/widget.html',
}
And the template:
<input type="text" ng-change="callback()" />
So when the widget value is changed, it triggers the callback function that was passed in the main view

What you're looking for is &. Quoting the old angular docs: "& or &attr - provides a way to execute an expression in the context of the parent scope".
Try this as your directive code:
return {
restrict: 'E',
scope: {
callback: '&'
},
templateUrl: '/partials/widget.html',
}

You can also $watch for it in the link or controller.
.directive('myDirective', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) { {
scope.$watch(attrs.myDirective, function(value){ ... });
}
...

Turns out I needed to do as suggested and use an & rather than an =, but also this still wouldn't work until I added ng-model as well to my input:
<input type="text" ng-model="myValue" ng-change="callback()" />

Related

Pass directive attribute into linked controller?

I have a directive i'm using to do the same search filtering across multiple pages. So the directive will be using a service and get pretty hefty with code. Because of that I want to link to a controller instead of have the controller inside the directive like this:
.directive('searchDirective', function($rootScope) {
return {
restrict: 'E',
templateUrl:'searchtemplate.html',
controller: 'searchCtrl',
controllerAs: 'search'
};
});
I also want access to parent scope data inside the template, so I don't want to use a isolated scope.
Anyway here's what i'm not sure how to do. My directive looks like this:
<search-directive filter="foo"/>
How do I pass in the value in the filter attribute so that I can access it in my controller using $scope.filter or this.filter?
If I were using an isolated scope it'd be simple. If i had the controller in the same page I could use $attrs. But since i'm using a controller from another spot and don't want an isolated scope i'm not sure how to get the attrs values into the controller.
Any suggestions?
What about using the link function and passing the value to the scope?
return {
restrict: 'E',
templateUrl:'searchtemplate.html',
controller: 'searchCtrl',
controllerAs: 'search',
link: function (scope, element, attr) {
scope.filter = attr.filter;
}
};
searchDirective.js
angular
.module('searchDirective', []).controller('SearchCtrl', SearchCtrl)
.directive('SearchDirective', directive);
function directive () {
var directive = {
templateUrl:'searchtemplate.html',
restrict: "E",
replace: true,
bindToController: true,
controller: 'searchCtrl as search',
link: link,
scope: { filter:'=' } // <-- like so here
};
return directive;
function link(scope, element, attrs) {}
}
SearchCtrl.$inject = [
'$scope',
'$filter'];
function SearchCtrl(
$scope,
$filter) {
/** Init SearchCtrl scope */
/** ----------------------------------------------------------------- */
var vs = $scope;
// ....
Also I highly recommend checking out this AngularJS style guide, how you are writing your directive above is how I use to do it too. John Papa shows some way better ways: https://github.com/johnpapa/angular-styleguide
Directives:
https://github.com/johnpapa/angular-styleguide#directives
Controllers:
https://github.com/johnpapa/angular-styleguide#controllers
Flip the values of bindToController and scope around.
{
....
scope: true,
bindToController: { filter:'=' }
...
}
I have just hit the same issue over the weekend, and made a simple complete example here: bindToController Not Working? Here’s the right way to use it! (Angular 1.4+)

Pass ng-model as argument in directive angualjrs

I am writing a directive in AngularJs and I want to pass ng-model as an argument.
<div class="col-md-7"><time-picker></time-picker></div>
The directive is:
app.directive('timePicker', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="text" class="form-control time-picker" ng-model="emp.signin">',
link: function ($scope, element, form) {
$(element).timepicker({'timeFormat': 'H:i:s'});
}
}
})
It is working fine, and here the ng-model is emp.signin. I want to be able to pass this ng-model dynamically as argument
How is this possible?
You can use
<div class="col-md-7"><time-picker model-value="emp.signin"></time-picker></div>
Angular
app.directive('timePicker', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="text" class="form-control time-picker"ng-model="modelValue ">',
scope: {
modelValue : '=',
}
link: function ($scope, element, form) {
$(element).timepicker({'timeFormat': 'H:i:s'});
}
}
})
Explaination
The “=” prefix will create a two-way binding between the parent and
directive scope and it’ll always expect the attribute value to be the
model name which means you cannot provide an expression as the value
of attribute mapped to “=” prefix.
For reference: "http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/"

How to pass evaluated values to a custom element directive?

I have a custom element directive with the following template:
<div>
<input value="{{dataFromRootScope}}" />
</div>
And definition:
dirModule.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
}
);
I would like to use the directive as shown below:
<my-directive my-value="{{dataFromScope}}"></my-directive>
i.e. I want to use the evaluated dataFromScope value inside my custom directive as dataFromRootScope. How can I reach this?
You can use isolated scope two-way binding:
dirModule.directive('myDirective', function() {
return {
scope: {
model: '=myValue'
},
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
});
Where directive template is
<div>
<input ng-model="model" />
</div>
and usage is
<my-directive my-value="dataFromScope"></my-directive>
Demo: http://plnkr.co/edit/Npiq2hCO4tQHmakG4IAe?p=preview
I want to use the evaluated dataFromScope value inside my custom
directive as dataFromRootScope. How can I reach this?
Well you have two options to achieve this.
Option-1: Create an isolated scope for your directive
This way, you would need to assign value of dataFromRootScope from myValue. The = operator ensures two-way binding.
app.directive('myDirective', function() {
return {
restrict: 'E',
scope:{
dataFromRootScope: '=myValue'
},
templateUrl: 'myDirective.html'
};
}
);
'dataFromScope' will not be available in myDirective because it has isolated scope. You can access it via dataFromRootScope(see how its getting its value from myValue)
<div>
<input value="{{dataFromRootScope}}" />
</div>
Demo-1
Option-2: Enjoy shared scope.
In this case, you dont need to create an isolated scope. You can simply use dataFromScope in your directive template OR, if you really want to access it as dataFromRootScope in your template, simply assign it in your link function.
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'myDirective.html',
link:function(scope,ele,attr){
scope.dataFromRootScope = scope.dataFromScope
}
};
}
);
<div>
<input value="{{dataFromRootScope}}" />
</div>
Demo-2
You can use the '#' sign :
dirModule.directive('myDirective', function() {
return {
scope: { myValue: '#' },
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
});
The '#' sign binds the evaluated value of the DOM attribute to the directive.
You can then use the directive as you asked :
<my-directive my-value="{{dataFromScope}}"></my-directive>

My directive is not firing

I want to recreate nsClick behavior with my directive ( changing priority).
So this is my code:
angular.module('MyApp').directive('nsClickHack', function () {
return {
restrict: 'E',
priority: 100,
replace: true,
scope: {
key: '=',
value: '=',
accept: "&"
},
link: function ($scope, $element, $attrs, $location) {
$scope.method();
}
}
});
and the line I'm trying to bind to:
<li ng-repeat="item in items" ns-click-hack="toggle(); item.action()">
toggle and item.action are from other directives.
Can you point me where I was making mistake?
If you are trying to re-create ng-click, then it's probably better to look at the source of the ngClick directive.
For example, it does not create an isolate scope since only one isolate scope can be created on an element and it tries to be accommodating towards other directives. The alternative is to $parse the attribute value, which is what the built-in implementation is doing.
If you are just creating a "poor's man" version of ngClick, then, sure, you could use a callback function "&" defined on the scope, and invoke it when the element is clicked:
.directive("nsClickHack", function(){
return {
restrict: "A",
scope: {
clickCb: "&nsClickHack"
},
link: function(scope, element){
element.on("click", function(e){
scope.clickCb({$event: e}); // ngClick also passes the $event var
});
}
}
});
The usage is as you seem to want it:
<li ng-repeat="item in items" ns-click-hack="toggle(); item.action()">
plunker

Pass additional parameter to template using AngularJS

I have the following directive:
app.directive("actTemplate", function() {
return {
restrict: 'A',
templateUrl: "/views/myTemplate.html"
};
});
how can i pass additional parameter to myTemplate so:
<div>
{{aditionalParam}}
...
</div>
takes the value?
Define
app.directive("actTemplate", function() {
return {
restrict: 'A',
templateUrl: "/views/myTemplate.html"
scope: {
foo: '=boo'
}
};
});
Template
<div>
{{foo}}
</div>
Use
<actTemplate boo="lalala" />
You have to specify that your directive should create it's inner scope. Scope variable could then be shared in single or double binding way (see directive scope binding doc)
app.directive("actTemplate", function() {
return {
restrict: 'A',
scope: {
additionalParam: '='
},
template: "<div>{{additionalParam}}</div>"
};
});
Then "call" your directive with this dashed syntaxe:
<div act-template additional-param="foobar">
You can have one-way binding (controller -> directive) like in this jsfiddle example.
Or two way data binding (controller <-> directive) , like in this one.

Categories

Resources