Passing a controller to a directive in angularJS. Gives undefined error - javascript

I am working to add modals to my directives using ui-bootstrap and did so fine on the previous directive. I don't believe I am doing anything differently in this one but I get the ReferenceError: milestoneController is not defined when I run the edit() function from within the directive.
milestone.html (this is the template HTML for the directive below):
<div ng-controller = "milestoneController"></div>
milestone directive:
angular.module('ireg').directive('milestone', function (milestoneFactory,$modal) {
return {
restrict:'E',
scope: {
objectid:'#objectid'
},
templateUrl: '/ireg/components/milestone/milestone.html',
link: function ($scope, element, attrs) {
$scope.edit = function(data) {
milestoneController.editMilestoneDialog(data);
};
}
}
});
angular.module('ireg').controller('milestoneController', function ($scope, $modal){
$scope.editMilestonesDialog = function (objectid) {
//fun
}
});
EDIT: I allso felt I should mention that the milestone directive is repeated in a ng-repeat loop. Thanks!

ok you're going to want to use a transcluded scope in your directive to pass a controller function to the directive. Your directive now becomes:
angular.module('ireg').directive('milestone', function (milestoneFactory,$modal) {
return {
restrict:'E',
scope: {
objectid:'#objectid',
editMilestoneDialog:'&'
},
templateUrl: '/ireg/components/milestone/milestone.html',
link: function ($scope, element, attrs) {
$scope.edit = function(data) {
$scope.editMilestoneDialog(data);
};
}
}
and your markup becomes:
<milestone edit-milestone-dialog="editMilestoneDialog"></milestone>

Related

Pass controller variable value to directive in AngularJS

I'm working on a fiddle that I found from this answer on SO
I've modified the fiddle for my situation in the following way:
angular.module('sampleApp', [])
.controller('myCtrl', function($scope) {
$scope.func = function() {
$scope.name = "Test Name";
}
})
.directive("myDirective", function($compile) {
return {
template: "<div>{{name}}</div>",
scope: {
name: '='
},
link: function(scope, element, attrs) {
alert(scope.name);
}
}
});
Basically, I am trying to pass a variable value from a scope function to the directive - but the alert message shows up an undefined value.
Any idea as to what is going wrong here? How can I pass the value stored within $scope.name within the $scope.func function and pass it to the directive?
The updated fiddle can be found here.
The problem is that you are defining name inside a function in your controller and that function is never called. Change to this.
angular.module('sampleApp', [])
.controller('myCtrl', function($scope) {
$scope.name = "Test Name";
})
.directive("myDirective", function($compile) {
return {
template: "<div>{{name}}</div>",
scope: {
name: '='
},
link: function(scope, element, attrs) {
alert(scope.name);
}
}
});
<div ng-app="sampleApp" ng-controller="myCtrl">
<div my-directive name="name">
</div>
</div>
The alert function is executing before the data is set by the controller.
To see value changes, add a controller and use $onChanges Life-Cycle Hook:
app.directive("myDirective", function() {
return {
template: "<div>{{name}}</div>",
scope: {
̶n̶a̶m̶e̶:̶ ̶'̶=̶'̶
name: '<'
},
controller: function() {
this.onChanges = function(changesObj) {
if (changesObj.name) {
alert(changesObj.name.currentValue);
};
};
}
}
});
Also note that one-time < binding is used instead of two-way = binding.

Angular directive and controller 2 way binding

Error: Expression 'undefined' used with directive is non-assignable
I'm getting the above error when trying to change a controller scope variable from my directive. Directive:
<mydir selUser="selectedUser"></mydir>
My controller:
angular.module('myApp')
.controller('MainCtrl', function ($scope, $http) {
$scope.selectedUser = {};
$scope.$watch('selectedUser', function() {
console.log('user changed');
});
});
And finally my directive:
angular.module('myApp')
.directive('mydir', function() {
return {
restrict: 'E',
templateUrl: 'app/mydir/mydir.html',
scope: {
selUser: '='
},
link: function(scope) {
scope.changeUser = function(user) {
console.log('user: ' + JSON.stringify(user));
scope.selUser = user;
};
}
};
});
As you can see, I am trying to change the selected user in the parent (controller) scope on the click of a button. But then the click happens, the error is thrown.
I have seen several combinations of how to define the scope in the directive and on the html declaring the directive but none of these seemed to work. Is there something else I'm missing?

angular directive load after got variables

I have an angular directive that loads data from service,
BUT
it loads the data with a variable he go from a controller that it was loaded as well from a service.
code:
directive:
app.directive("posts", ['Posts', function(Posts) {
return {
restrict: 'E',
template: '' +
'<div ng-repeat="post in posts"></div>',
scope: {
showLoading: '&',
hideLoading: '&',
spot: '#'
},
controller: ['$scope', function ($scope) {
}],
link: function(scope, element, attrs) {
$scope.load = function () {
Posts.loadPostsBySpot(scope.spot)
};
}
};
}]);
Controller
app.controller('spotPageController', ['$scope', 'Spots', function ($scope, $Spots) {
doit = function () {
Spots.getSpot($)
.success(function (data) {
$scope.spotId = data.data;
console.log($scope.spot);
}).error(function (data) {
console.log('error');
});
};
}]);
html inside
<posts spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
but when the directive is loaded the "spot" is not yet set,
so how do I make the directive load only after the spot is set.
Use ng-if.
<posts ng-if="spotId" spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
This element will be rendered only after the spotId is initialized. Therefore, your directive will not be called before that.
If you want to encapsulate this behavior in directive, you should watch for changes of the scopeId. See the fiddle.

How to pass a collection to a directive in angular.js?

Please forgive my lack of understanding.
I pass the name of a collection to my directive:
<ul tag-it tag-src="preview_data.preview.extract.keywords"><li>Tag 1</li><li>Tag 2</li></ul>
The directive is defined:
app.directive('tagIt', function (){
return {
restrict: 'A',
link: function(scope,elem, attr) {
elem.tagit();
console.log(attr.tagSrc); //the name of my collection, but how do I access it?
}
}
});
How do I access my collection from the directive and make sure my directive is called when the collection is populated? Here is how preview_data.preview.extract.keywords gets populated.
app.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
console.log('config');
$routeProvider.when("/", {
templateUrl: "/templates/addItem.html",
controller: "AddItemController",
resolve: {
loadData: addItemCtrl.loadData
}
});
});
var addItemCtrl=app.controller("AddItemController", function ($scope, $route, $sce, Preview) {
var title = decodeURIComponent($route.current.params.title);
var ua = decodeURIComponent($route.current.params.ua);
var uri = decodeURIComponent($route.current.params.uri);
$scope.preview_data = {
uri: uri,
title: title,
ua: ua
}
//pass parameters to web preview API
Preview.get(uri, ua, title).then(function (data) {
$scope.preview_data.preview = data;
if (data.embed.html) {
$scope.preview_data.preview.embed.html = $sce.trustAsHtml(data.embed.html);
}
}, function (data) {
alert('Error: no data returned')
});
});
You need to set the variable in the directive scope and set the template to iterate between the tags:
template : '<li data-ng-repeat="tag in tagSrc">{{tag.name}}</li>',
scope : {
tagSrc : '='
},
And will became this:
app.directive('tagIt', function (){
return {
restrict: 'A',
template : '<li data-ng-repeat="tag in tagSrc">{{tag.name}}</li>',
scope : {
tagSrc : '='
},
link: function(scope,elem, attr) {
console.log(attr.tagSrc);
}
}
});
the '=' attribute will tells to angular to use a tw way binding with the array passed in the directive declaration in the HTML.
Here is a plunker with a working example.
And here is a good arcticle explaning the directive's attributes and life cycle.
I hope it helps.
[EDIT]
If you want just iterate the array, without creating some different behavior in the list items, you can just simply use the ng-repeat directive:
<ul>
<li data-ng-repeat="tag in tags">{{tag.name}}</li>
<ul>

How to require a controller in an angularjs directive

Can anyone tell me how to include a controller from one directive in another angularJS directive.
for example I have the following code
var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: '/js/partials/home.html'
})
.when('/products', {
controller: 'ProductsController',
templateUrl: '/js/partials/products.html'
})
.when('/products/:productId', {
controller: 'ProductController',
templateUrl: '/js/partials/product.html'
});
}]);
app.directive('mainCtrl', function () {
return {
controller: function ($scope) {}
};
});
app.directive('addProduct', function () {
return {
restrict: 'C',
require: '^mainCtrl',
link: function (scope, lElement, attrs, mainCtrl) {
//console.log(cartController);
}
};
});
By all account I should be able to access the controller in the addProduct directive but I am not. Is there a better way of doing this?
I got lucky and answered this in a comment to the question, but I'm posting a full answer for the sake of completeness and so we can mark this question as "Answered".
It depends on what you want to accomplish by sharing a controller; you can either share the same controller (though have different instances), or you can share the same controller instance.
Share a Controller
Two directives can use the same controller by passing the same method to two directives, like so:
app.controller( 'MyCtrl', function ( $scope ) {
// do stuff...
});
app.directive( 'directiveOne', function () {
return {
controller: 'MyCtrl'
};
});
app.directive( 'directiveTwo', function () {
return {
controller: 'MyCtrl'
};
});
Each directive will get its own instance of the controller, but this allows you to share the logic between as many components as you want.
Require a Controller
If you want to share the same instance of a controller, then you use require.
require ensures the presence of another directive and then includes its controller as a parameter to the link function. So if you have two directives on one element, your directive can require the presence of the other directive and gain access to its controller methods. A common use case for this is to require ngModel.
^require, with the addition of the caret, checks elements above directive in addition to the current element to try to find the other directive. This allows you to create complex components where "sub-components" can communicate with the parent component through its controller to great effect. Examples could include tabs, where each pane can communicate with the overall tabs to handle switching; an accordion set could ensure only one is open at a time; etc.
In either event, you have to use the two directives together for this to work. require is a way of communicating between components.
Check out the Guide page of directives for more info: http://docs.angularjs.org/guide/directive
There is a good stackoverflow answer here by Mark Rajcok:
AngularJS directive controllers requiring parent directive controllers?
with a link to this very clear jsFiddle: http://jsfiddle.net/mrajcok/StXFK/
<div ng-controller="MyCtrl">
<div screen>
<div component>
<div widget>
<button ng-click="widgetIt()">Woo Hoo</button>
</div>
</div>
</div>
</div>
JavaScript
var myApp = angular.module('myApp',[])
.directive('screen', function() {
return {
scope: true,
controller: function() {
this.doSomethingScreeny = function() {
alert("screeny!");
}
}
}
})
.directive('component', function() {
return {
scope: true,
require: '^screen',
controller: function($scope) {
this.componentFunction = function() {
$scope.screenCtrl.doSomethingScreeny();
}
},
link: function(scope, element, attrs, screenCtrl) {
scope.screenCtrl = screenCtrl
}
}
})
.directive('widget', function() {
return {
scope: true,
require: "^component",
link: function(scope, element, attrs, componentCtrl) {
scope.widgetIt = function() {
componentCtrl.componentFunction();
};
}
}
})
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
}

Categories