Accessing controller function in Directive scope - angularjs

I am trying to access the function defined in the controller scope from directive nested in another directive. I am using the "&" and passing the function name as the attribute to the directive, however I am still not able to reach the function in the controller scope.
Could some one correct where I am going wrong? I had spent few hours trying to find that I have to use the "&" for reaching controller scope but stuck here after whatever I did.
JSFiddle here - http://jsfiddle.net/fwR9Q/12/
Code:
<div ng-controller="PlayerCtrl">
<div class="col-md-4 col-sm-6 col-xs-12" ng-repeat="video in videos" >
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('PlayerCtrl',
function PlayerCtrl($scope,$log, trailers)
{
$scope.videos = trailers;
$scope.playVideo = function(videoId, modeNum){
alert("video Id = " + videoId + "; mode Num = " + modeNum);
return false;
};
}
);
myApp.directive('tfVideo', function() {
return{
restrict: 'AE',
scope: {
src: '#',
handleClick: '&',
width: '#width',
height: '#height'
},
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
};
});
myApp.factory('trailers', function(){
var trailerVideos = [
{
src:"6kw1UVovByw",
width:"324",
height:"300"
},
{
src:"uWgDVz4NEO4",
width:"324",
height:"300"
}
];
return trailerVideos;
});
</script>
Thank you

1) On your directive declaration:
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
Since attributes use the snake-cased form.
Instead of: handleClick=
You want the snake cased: handle-click=
2) In your template:
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
Instead of: {videoId: {{src}}, modeId: modeNum{{$parent.$index}} }
You want: {videoId: src, modeNum: $parent.$index }
Since you need the "modeNum" parameter name to match between the template and the directive and you want to map directly to the variables, not expressions.
Updated fiddle

Related

How can I use isolated scope with a component and a directive?

The goal here is to let MainCtrl know when there is an error(s) found in the directive. The error must be displayed here:
<div ng-if="errors[0]">Error 1: {{errors[0]}}</div>
How can I get isolated scope with a directive inside a component? The following application works if you uncomment the 2 lines mentioned below. As it is, I get error:
Multiple Directive Resource Contention
I can read the causes. I need to know how to fix this while still allowing the directive to have isolated scope. I may have 3-4 of these directives on a page and each one needs it's own unique of errors that is also available to the parent.
(working case example on codepen)
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.errors = [false, false];
$scope.text = "bobby";
});
app.directive('testDirective', function(){
return {
restrict: 'A',
scope: {
errors: '=',
text: '#'
},
link: function($scope, $element, $attr, ngModel) {
console.log('link fired');
console.log('errors: ', $scope.errors);
console.log('scope.text', $scope.text);
$attr.$observe('text', function (val) {
if($scope.text === 'bobby'){
$scope.errors[0] = true;
}else{
$scope.errors[0] = false;
}
});
},
template: '<p>text: {{ text }} </p>'
+ '<p>errors: {{errors}}</p>'
+ '<p><input type="text" ng-model="errors" /></p>'
};
});
app.component('panel', {
bindings: {
},
template: [
'<div>',
'</div>'
].join(''),
controller: function() {
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js"></script>
<section ng-app="app" ng-controller="MainCtrl">
<h3>Parent Scope</h3>
<p>errors: {{errors}}</p>
<input type="text" ng-model="text"></div>
<div ng-if="errors[0]">Error 1: {{errors[0]}}</div>
<div ng-if="errors[1]">Error 2: {{errors[1]}}</div>
<!-- UNCOMMENT THE FOLLOWING 2 LINES AND THIS APP WILL WORK
<h3>Directive by itself</h3>
<div test-directive text="{{text}}" errors="errors"><div>
-->
<h3>Directive in component</h3>
<panel test-directive text="{{text}}" errors="errors"></panel>
</section>
After researching, I noticed Angular only returns bool from $validators (as opposed to object). At this point I decided my approach was wrong. I decided to create a unique $valiators for each unique error message. Then use ng-message for the output.
In order to work with multiple components on the same page, I also have to check the ngModel.$error as part of validation. This blog covers the basic approach.

AngulaJs ng-click in expression html [duplicate]

This question already has answers here:
How to make ng-bind-html compile angularjs code
(8 answers)
Closed 3 years ago.
I am displaying HTML in a AngularJS expression.
In My HTML
<div ng-bind-html="myText"></div>
And In My Controller
$scope.myText = $sce.trustAsHtml("<div class='my-style' ng-click='onClick(10)'>click me</div>");
$scope.onClick = function(value)
{
console.log("onClick: value:"+value);
}
But I am not able to get the click event
UPDATE
In one of the question I see it has be described about how to add HTML tags in the text or how to compile HTML. But my question is related to how to get ng-click event from the complied HTML. I don't see any of the answers providing this solution.
Sorry for again writing the code :
First give a unique id to your div :
<div id="test"></div>
Then Change that code to below one:
var myText = "<div class='my-style' ng-click='onClick(10)'>click me</div>";
var element = angular.element(document.querySelector('#test'));
var generated = element.html(myText);
$compile(generated.contents())($scope);
This will manage your code to work. cheers!
Use directive to implement this functionality. Check demo here http://plnkr.co/edit/tTgVCMt7JqvaQOrjhLLL?p=preview
JS:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.testClick = function(value) {
alert("onClick: value:"+value);
};
});
app.directive("clickDynamic", function(){
return {
template:"<div class='my-style' ng-click='testClick(10)'>click me</div>"
};
});
HTML:
<body ng-controller="MainCtrl">
<div click-dynamic></div>
</body>
I have append the html using id or class
var add = angular.element( document.querySelector( '#updatechoose' ) );
add.append('<div class="form-group" ng-repeat="updatedata in getdata">'+
'<div>'+
'<input type="file" id="imgupload" ng-model="attachment" imgepath="{{updatedata.imagepath}}" imgname="{{updatedata.imagename}}" name="file[]" style="padding-left: 15px;" class="imgupload col-md-4 cropit-image-input" />'+
'<i class="glyphicon glyphicon-trash" **ng-click="remove()"**></i>'+
'</div>'+
'</div>');
One solution is to use plain javascript and get the scope. This way you can call your inside methods.
<div ng-bind-html="myText"></div>
$scope.myText = $sce.trustAsHtml( '<div class="button" onClick="var scope = angular.element(this).scope();scope.testClick(10);">click me</div>' );
Please note that I'm using onClick instead of ng-click, then I get the scope, this way I can call the inner methods.
It's a dirty solution, but maybe it can help you.
I've added a snippet, which shows an implementation via a directive and the $compile service.
angular.module('TestApp', [])
.directive('compileDir', function($compile) {
return {
restrict: 'E',
scope: {
dynTpl: '='
},
template: 'Num clicked: {{numClicked}}',
link: function(scope, elem) {
scope.numClicked = 0;
scope.click = function() {
scope.numClicked++;
};
var tpl = $compile(scope.dynTpl)(scope);
elem.append(tpl);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.12/angular.min.js"></script>
<div ng-app="TestApp">
<compile-dir dyn-tpl="'<button ng-click=\'click()\'>Click</button>'"></compile-dir>
</div>

Using a function created in parent scope and also local scope varibles in an AngularJS directive with a template

So I am trying to create a directive which will replace an element with HTML which includes an ng-click which calls a function created in $scope.
An example I made can be found at http://jsfiddle.net/adinas/wbfu9ox3/5/
My HTML is
<div ng-app="myapp" ng-controller="mycontroller">
{{greeting}}
<!--This works-->
<div xyz></div>
<!--This works-->
<div ng-click="dosomething('Click me 2')">Click me 2</div>
<!--The click added in the directive here does NOT work-->
<div abc mytext="Click me 3"></div>
My JS is
var myapp = angular.module('myapp',[]);
myapp.controller('mycontroller', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
$scope.dosomething = function (what) {
alert(what);
};
}]);
//Works
myapp.directive('xyz', function () {
return {
template: '<div ng-click="dosomething(\'Click me 1\')">Click me 1</div>'
};
});
//Does not work
myapp.directive('abc', function () {
return {
template: '<div ng-click="dosomething(\''+attr.mytext+'\')">Click me 3</div>'
};
});
I made 3 elements. The first two show that A. without the directive the click work. B. a directive without using the 'dosomething' function also works.
The 3rd element which tries to pass a parameter to the 'abc' directive and also call the function fails.
How can I also pass a parameter and also use the function? Thanks.
Well, if you look in the console, you will see an error that shows why this is not working:
Error: attr is not defined
You're trying to access an attr variable that doesn't exist.
The way to include attribute values in your directive template is to include them in the directive's scope:
scope: {
mytext: '#'
}
This means that there will now be a mytext variable in the scope of the directive.
This creates a problem, however, since the use of the scope: {} option creates an isolate scope for the directive, and this means that the enclosing controller's scope is not part of the directive's scope.
If feasible, the clean approach here is to also pass in the dosomething function as a parameter of the directive:
<div abc mytext="Click me 3" action='dosomething'></div>
return {
template: '<div ng-click="action(mytext)">Click me 3</div>',
scope: {
mytext: '#',
action: '='
}
};
http://jsfiddle.net/jx1hgjnr/1/
But if that's not what you want to do and you really want to access dosomething from the parent scope, one thing you can do is use $parent.dosomething:
return {
template: '<div ng-click="$parent.dosomething(mytext)">Click me 3</div>',
scope: {
mytext: '#'
}
};
http://jsfiddle.net/c815sqpn/1/
Such directive should have isolated scope, so it can be used everywhere.
That is simple directive that introduce two parameters: func and param.
app.directive('pit', function () {
return {
scope : {
func: '=',
param: '='
},
template: '<button ng-click="func(param)">Click me 3</button>'
};
});
And this is how u use it in html:
<div pit func="test1" param="test2"></div>
My plunker:
http://plnkr.co/edit/YiEWchRPwX6W7bohV3Zo?p=preview

AngularJS isolate scope: & vs =

I'm trying to learn AngularJS, but there's one thing I can't wrap my head around.
What is the benefit of using "&" over "="? All the tutorials, demonstrations and documentation I've found tell me that & evaluates the expression in the parent scope. However, when I first tried doing callbacks to my controller from my directive, I just used a = binding to a function on the parent scope, and it worked fine.
For example, with a function on the controller's scope foo that takes a parameter bar, I could do a directive declaration like
scope: { callback: '=' },
template: '<div ng-click="callback(value)"></div>'
and include the directive like
<my-directive callback="foo"></my-directive>
To achieve the same with &, it seems like I have to do
scope: { callback: '&' },
template: '<div ng-click="callback({bar:value})"></div>'
and
<my-directive callback="foo(bar)"></my-directive>
From this, I don't really see the advantage. Have I misunderstood &?
EDIT: I suppose a valid addition to my question is: Is it a bad idea to bind to parent scope functions using = instead of &?
The difference it seems is that with the & binding, the user of the directive fixes what function is called on a parent scope and which parameters are used, while an = binding simply means passing a reference function reference which the directive can call with whatever arguments.
= isn't meant for that though, it exists mainly for synchronizing properties between nested scopes, while & is meant give the directive a way to interact with the 'outside' world without having knowledge about the outside world.
example of both
<div ng-app="app" ng-controller="ParentCtrl as parentCtrl">
<bind-dir func-is="parentCtrl.func" func-and="parentCtrl.func(arg)"></bind-dir>
</div>
var app = angular.module('app', []);
app.controller('ParentCtrl', function () {
this.func = function (arg) {
alert(arg);
}
})
app.directive('bindDir', function () {
return {
scope: {
funcIs: '=',
funcAnd: '&'
},
template: '<button ng-click="funcIs(\'=\')">=</button><button ng-click="funcAnd({arg:\'&\'})">&</button>'
}
});
Official docs.
# or #attr - bind a local scope property to the value of DOM attribute. The result is always a string since DOM attributes are strings.
= or =attr - set up bi-directional binding between a local scope property and the parent scope property of name defined via the value of the attr attribute.
& or &attr - provides a way to execute an expression in the context of the parent scope.
Further reading: Understanding-Scopes.
'=' gives two way data binding. This means that if you alter the value of the expression within the directive it also alters in the controller. This is called polluting the controller.
'&' is nicer and more modular. You can pass in the function and it will be executed in the scope of the controller but you are unable to change the function in the controller scope.
Please see http://jsfiddle.net/b6ww0rx8/ and it will be a bit clearer.
<div ng-app="myApp">
<div ng-controller="MyController">
<div my-directive
callback1="aFunction"
callback2="anotherFunction()">
</div>
<button ng-click="test()">click me</button>
</div>
</div>
angular.module('myApp', [])
.controller('MyController', function($scope) {
$scope.aFunction = function() {
console.log('abc');
};
$scope.anotherFunction = function() {
console.log('def');
};
$scope.test = function () {
console.log($scope.aFunction);
console.log($scope.anotherFunction);
};
console.log($scope.aFunction);
console.log($scope.anotherFunction);
})
.directive('myDirective', function(){
return {
scope: {
callback1: '=',
callback2: '&'
},
link: function (scope, element, attrs) {
scope.callback1 = 123;
scope.callback1 = 456;
}
}
});
Is the capability to execute an expression against the parent's context a benefit for you? The first example below executes myLocalModel as a function, unlike the '=', you get the result already.
template: "{{ myLocalModel() }}"
begin added update 01
example, you might have 2 attributes with expression, and you only want to execute either one depending on a condition. that could save execution time. is it beneficial for you?
end update 01
the '&'
, Execute expression against a parent's context.
http://ngtutorial.com/learn/directive.html#/exec-expr
angular.module("myApp", []).directive("myCustom", function(){
return {
restrict: 'EA',
scope: {
myLocalModel: '&theElementsAttrName',
},
// note that myLocalModel is a function
template: "{{ myLocalModel() }}"
};
});
........
<body ng-app="myApp">
<div ng-init="ParentModel='the parents value';
ParentNum1=100;
ParentNum2=200"></div>
<div ng-controller="CreateChildScopeController">
my-custom 1) <my-custom the-elements-attr-name="ParentModel + ' ---> adding more'"></my-custom><br/>
my-custom 2) <my-custom the-elements-attr-name="ParentNum1 + 12"></my-custom><br/>
</div>
my-custom 3) <my-custom the-elements-attr-name="ParentNum2 + 12"></my-custom><br/>
</body>
.... output
my-custom 1) the parents value ---> adding more
my-custom 2) 112
my-custom 3) 212
the '='
,Sync with existing model.
http://ngtutorial.com/learn/directive.html#/sync-existing
angular.module("myApp", []).directive("myCustom", function(){
return {
restrict: 'EA',
scope: {
myLocalModel: '=theElementsAttrName',
},
template: "{{ myLocalModel }}"
};
});
.....
<body ng-app="myApp">
<div ng-init="ParentModel='the parents value';
ParentNum1=100;
ParentNum2=200"></div>
<div ng-controller="CreateChildScopeController">
my-custom 1) <my-custom the-elements-attr-name="ParentModel"></my-custom><br/>
my-custom 2) <my-custom the-elements-attr-name="ParentNum1"></my-custom><br/>
</div>
my-custom 3) <my-custom the-elements-attr-name="ParentNum2"></my-custom><br/>
</body>
..... output
my-custom 1) the parents value
my-custom 2) 100
my-custom 3) 200

Angular.js - binding a directive to a variable in the controller

I want to bind a directive to a variable within a controller, but cannot work out how to do it from the Angular.js docs (nor searching the web, looking at the egghead videos).
I have the following html:
<body ng-app="MyApp">
<div ng-controller="triCtrl">
<div jqslider pleaseBindTo="firstValue"></div>
<br>
<br>
<br>
<br>
<div jqslider pleaseBindTo="secondValue"></div>
<p>{{firstValue.v}}</p>
<p>{{secondValue.v}}</p>
</div>
</body>
And the following JS:
function triCtrl($scope) {
$scope.firstValue = {"min":0, "max":100, "v":50};
$scope.secondValue = {"min":0, "max":1000, "v":450};
}
var myAppModule = angular.module('MyApp', []);
myAppModule.directive('jqslider', function() {
return {
link:function(scope, element, attrs) {
element.slider({
range: false,
min: scope.min,
max: scope.max,
value: scope.v,
slide: function( event, ui ) {
scope.v = ui.value;
scope.$apply();
}
});
}
};
});
I have tried several ways using scope:{ } with &, #, = etc, but I can't get it to work. Any ideas? I understand the pleaseBindTo attribute has to be captured somewhere, but I don't know where or how.
Some Observations:
1. Your directive is an attribute
2. You are passing values to attributes themselves.
Maybe you should change your directive to an element instead. Rewrite the code as follows:
<jqslider pleaseBindTo="firstValue"></jqslider>
Remove the div's and use the directive directly as an element. Next, in the definition of your directive, write the following:
myAppModule.directive('jqslider', function() {
return {
scope: {
pleaseBindTo: "=pleaseBindTo"
}
link:function(scope, element, attrs) {
element.slider({
range: false,
min: scope.pleaseBindTo.min,
max: scope.pleaseBindTo.max,
value: scope.pleaseBindTo.v,
slide: function( event, ui ) {
scope.pleaseBindTo.v = ui.value;
scope.$apply();
}
});
}
};
});
If you wish to still keep the directive as an attribute, you could try to access the value of pleaseBindTo inside the link function with the statement attrs.pleaseBindTo - I am not sure of this, need to check it out.

Resources