Directive does not have access to controller function (AngularJS) - angularjs

When I declare a directive to use that accesses a controller function, it cannot find the controller.
Here is my directive declared in app.js:
app.directive("delete", function() {
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('click', function(e) {
alertCtrl.alert();
});
}
}
});
Here is my controller:
app.controller('AlertController', function() {
this.alert = function() {
alert('Ahah!');
}
});
And here is my HTML:
<div ng-controller="AlertController as alertCtrl">
<div ng-repeat="i in [1,2,3]">
<img src='image.png' delete />
</div>
</div>
If I click on the image, I get no alert and the console says that alertCtrl is not defined. How come alertCtrl is not defined when you click on the image with the delete directive on it?
If I change the controller to have $scope.alert = function()... it works fine. But I do not want this.
Also, is this the proper way to handle such a situation? If not, what is the best practice?

You need to call, it on the scope. scope has the property alertCtrl which is your controller instance. Your controller alias (alertCtrl) is available when you bind it on the html since scope is implicit there. but when you do it in the javascript i.e in the directive, or another controller you would need to get the controller instance (defined as alias) from the scope as a property.
scope.alertCtrl.alert();
Plnkr

Related

Calling controller method from directive in template

I have a directive that is sitting in a template that is then included on a page.
If I place my directive directly onto my page, then on a button click I can call a method within my controller.
However, when I place the directive within a template, and then the template on the page, I can no longer call a method in my controller from the directive.
I've tried a number of things with the posted code below my latest attempt. However, this code produces the error
asking for new/isolated scope on:
So HTML first;
This is on my HTML page.
<session-list trackid='san'></session-list>
This is the template HTML;
<div class="container col-sm-12 col-xs-12">
<div>Session list template for {{trackid}}</div>
<session-calendar callback-fn="ctrlFn()"></session-calendar>
</div>
My primary controller looks like this with the "eventClick" method I want to call.
angular.module('GAP.viewsessions', ['ngRoute'])
.controller('viewsessionsCtrl', ['$scope', function($scope){
$scope.eventClick = function(eventData){
console.log(eventData);
}
}]);
Then the "SessionList" directive;
angular.module("GAP.sessionList", [])
.directive("sessionList", function(){
return {
restrict: 'E',
link: function(scope, element, attributes){
},
scope: {
trackid: '#'
},
templateUrl: '/templates/sessionlist.html', // or use a path to a html file like 'path_to/template.html'
replace: true,
};
})
The other directive is a FullCalendar and in the click event of the event I have this;
eventClick: function(calEvent, jsEvent, view) {
scope.someCtrlFn();
if (scope.eventClick){
scope.eventClick(calEvent.data);
}
},
And If I include this;
scope: { someCtrlFn: '&callbackFn' },
I get the previously quoted error. If I leave it out, then the page renders but the "eventClick" method is never run in my controller.
One possible workaround is use an angular event
Inject $rootScope in directive then something like:
$rootScope.$broadcast('cal-event-clicked', eventData)
In controller
$scope.$on('cal-event-clicked', function(evt, data){
$scope.eventClick(data)
})

How to get modified values from controller to custom directive?

I have defined one variable in controller and i have assigned this value to one attribute of custom directive. So on the basis of this value i am showing the modal box template. Now if i click on the cancel button from modal box template then it calls one function from controller which is modifying the variable value to false but it is not hiding the popup box. Please help me to fix it.
(function () {
'use strict';
angular.module('module1').directive('myDirective', function () {
function linkFunction(scope, elem, attrs) {
//scope.openvalue = attrs.openvalue;
scope.closevalue = false;
scope.close = function () {
console.log("Inside Close");
scope.openvalue = false;
scope.closevalue = false;
};
};
return {
templateUrl: 'confirmTemplate.html',
restrict: 'E',
link: linkFunction,
scope: {
confirmtext: '#',
openvalue: '=',
closeconfirm: '&',
submitconfirm: '&'
},
controller: ['$scope', function ($scope) {
$scope.$watch('openvalue', function () {
console.log("OpenValue : " + $scope.openvalue);
});
}]
};
});
})();
Following is the html for opening this modal.
<div class="col-xs-12 options" ng-click="cntrl.flag1 = true">
<div class="row">
<myDirective openvalue="cntrl.flag1" confirmtext="This is the text from directive"
closeconfirm="cntrl.closeconfirm()" submitconfirm="cntrl.submitconfirm()"></myDirective>
<div class="col-xs-9 no-left-right-padding">My text</div>
</div>
</div>
And i want the updated value of openvalue inside html template but it is not working.
It would be more clear to have you codes here, but I think the problem is when you call the function from controller, it doesn't actually modify the variable of controller scope but modal's scope.
In AngularJS scope, any change of inherited variable in child scope will create a local version.
Based on your words, when you open a modal window it will create a new child scope and when you call the function from controller to modify that scope variable, you actually modifying that child scope variable not controller's.
You can simply add console.log($scope.$id); in controller and the function then you should be able to see the scope id is different.
This Fiddle will give you the idea, press Esc key to close the modal window. However, as I said it would be better to have your code to address exact issue.
Based on your code, a quick fix is assign the cntrl object into directive which will make sure your directive refer to the same object.
Change your modal to
<myDirective cntrl="cntrl" confirmtext="This is the text from directive"></myDirective>
in your directive
scope: {
confirmtext : '#',
cntrl : '='
},
in your linkFunction
function linkFunction(scope, elem, attrs){
scope.close = function(){
scope.cntrl.flag1 = false;
}
you still can access closeconfirm and submitconfirm by $scope.cntrl.closeconfirm and $scope.cntrl.submitconfirm respectively.

pass data from controller to directive's link?

In my controller :
myApp.controller('homeCtrl', function($scope, $rootScope, $state, 'red';
$rootScope.$on('new_story', function(event, data) {
$scope.cardObj = {key:'value'};
});
});
In my HTML :
<div clickmeee ></div>
<div id="feedContainer" card='{{cardObj}}'> </div>
In my directive :
myApp.directive('clickmeee', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
card: '#'
},
link: function(scope, element, attrs) {
element.bind('click', function() {   
scope.$watch('card', function(newVal, oldVal) {
alert(scope.card);
});       
});
}
};
});
How do I pass data from controller to this directive. I compile some html and prepend it to the div. All of that is sorted out but I need some data from object I am trying to pass.
Any help??
There are several problems in your code:
you define a scope attribute named 'card', but you use cardObj instead
you use a watch that is completely unnecessary. And worse: you create a new watch every time the element is clicked
you don't define any card attribute on your clickmeee element. Instead, you're placing it on another element, on which the directive is not applied
you're passing the attribute with '#'. That works, but the directive will receive a string, containing the JSONified object, rather than the object itself
you're not showming us where you emit an event that will initialize cardObj in the controller scope
Here is a plunkr showing a working version of your code.
Also, note that using bind('click') is a bad idea. You'd better have a template in your directive and use ng-click in the template, or simply not use a directive at all and just use ng-click directly on the div element.
Bad news. You are doing it wrong all the ways.
Firstly
card='{{cardObj}}' >
this one should be put in the
<div clickmeee ></div>
So you can take it as binded scope variable in your directive registration
Secondly
If you managed to use '#' syntax
card: '#'
it will turn your input to string, not a binded scope. Use '=' instead.
In the end
You dont need to use watch here:
scope.$watch('card', function(newVal, oldVal) {
alert(newVal);
});
since scope.card is binded via '=' connector. Just simple use alert(scope.card). (Need to warn you that alert an object is not a good idea)
I have tried your code here: plunker. Changed a litte bit by using cardObj as string for easier presentation. Does it match your work?
You should watch the card object:
myApp.directive('clickmeee', function() {
return {
restrict: 'A',
scope: {
card: '#'
},
link: function(scope, element, attrs) {
scope.$watch('card', function(value) {
console.log(value);
});
}
};
});
And:
<div clickmeee id="feedContainer" card='{{cardObj}}'> </div>
Whenever the controller changes the cardObj, the directive's watch on card is triggered:
$scope.$apply(function() {
$scope.cardObj = "test";
}

Passing controller scope from directive to partial

I am new to AngularJs. I have a route configured to a controller and a template. In the template I am calling a custom directive. The custom directive loads a partial file in which I need to fetch the scope which is set by the controller. How can I pass the scope from the directive to the partial so that the partial file can consume the scope data.
Kindly let me know how to get this implemented in AngularJs
Code snippet inside the link function of the directive:
myApp.directive('basicSummary', function() {
return {
restrict: 'E',
replace:'true',
templateUrl: "partials/basicSummary.html",
link: function(scope, elem, attrs) {
console.log(scope.testURL);
}
}
});
Output on the console is : undefined
Update: I found the root cause of why the variable was not getting initialized. The issue, is that the variable is being fetched by making an ajax call in the controller and by the time the ajax call is completed and the variable is put inside the scope inside the controller, the partial file is already loaded and hence I am getting the value of the variable inside the directive as undefined.
How can I make sure that only on success of the http call, I load the partial and the directive?
Adding the jsfiddle link: http://jsfiddle.net/prashdeep/VKkGz/
You could add a new variable to your scope in the definition of your directive to create a two-way binding, that you could safely watch for changes (for use in Javascript once the variable has been populated via ajax), and in your template use ng-show to show/hide based on whether or not the variable is defined. See this JSFiddle for an example of how that would work: http://jsfiddle.net/HB7LU/3588/
Default Template
<div ng-controller="MyCtrl">
<my-test my-test-url="myAjaxProperty"></my-test>
</div>
App Definition
var myApp = angular.module('myApp',[]);
myApp.directive('myTest', function(){
return {
restrict: 'E',
repalce: 'true',
template: '<div ng-show="myTestUrl">{{myTestUrl}}</div>',
scope: { myTestUrl: '=' },
link: function(scope, elem, attrs){
scope.$watch('myTestUrl', function(newVal, oldVal){
if(newVal){
console.log("Watched value is defined as: "+scope.myTestUrl);
}
})
}
}
});
function MyCtrl($scope, $timeout) {
$timeout(function(){
$scope.myAjaxProperty = "My Test Url";
console.log("Ajax returned");
}, 3000, true)
console.log("Default Controller Initialized");
}
as long as you don't isolate your scope with,
scope: {}
in your directive, your scope has access to its parent controller's scope directly.

scope change in a directive not a apply in view

I have a directive in a template.html, included by a ng-include, in this directive I change the scope , but it is not change in my view
Here is my html
<div ng-controller="myCtrl">
<div id="modal">
<div ng-show="showDIv">Somthing to controll</div>
</div>
<div ng-include src="template.html">
</div>
Here is my template
<a ng-support></a>
And here is my directive
app.directive('ngSupport', function(){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('click', function(e) {
$("#modal").dialog({height:518,width:900,modal:true });
scope.showDiv = true;
scope.$apply();
});
}
};
});
When i change the scope in the directive it is not apply in the view, anyone could help please ?
ng-include creates a new scope so scope.showDiv only affects the local scope.
Depending on how you want to structure your application, you could try accessing scope.$parent.showDiv instead, but it is not really future proof as it will depend on the HTML nesting.
A better solution would be to have the showDiv property stored inside an object in the parent scope. For example scope.ui = {}, this way, when you set scope.ui.showDiv = true in your directive, it will look up the parent scope automatically (using prototype inheritance), instead of adding the property to the local scope.
Finally, another solution would be to refactor your code to make it less complex: I think using a ng-include just for adding one element is an overkill, you could put directly <a ng-support></a> inside your html, which would avoid the problem you have with an intermediary scope being generated.
Another option is to broadcast an event, and watch for it in the controller. Something like this.
app.directive('ngSupport', function($rootScope){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('click', function(e) {
$("#modal").dialog({height:518,width:900,modal:true });
$rootScope.$broadcast('event:modal-clicked');
});
}
};
});
With this in your controller.
$scope.$on('event:modal-clicked', function() {
$scope.showDiv = true;
});

Resources