controllerAs in directive, functions in controller can't see some variables - angularjs

I have bellow definition of directive/controller. If you look, there's an onClick function defined. When function is being called, it can see this variable, with ftConditionButton bound to it as described by directive. The thing is, onClick doesn't see conditionButtonController which is against my understanding of JavaScript. Can someone explain to me what I am missing? Right now it looks to me like a new "Class" was created and was given all the methods of original controller.
angular
.module('app')
.directive('ftConditionButton', ftConditionButton);
function ftConditionButton() {
var directive = {
restrict: 'A',
scope: {},
require: ['ftConditionButton'],
templateUrl: 'conditionButton.html',
controller: ConditionButtonController,
controllerAs: 'conditionButtonController',
bindToController: {
ftConditionButton: '&'
}
};
return directive;
}
function ConditionButtonController() {
var conditionButtonController = this;
conditionButtonController.onClick = onClick;
////////////////
function onClick() {
this.ftConditionButton; // this works
conditionButtonController; // conditionButtonController is undefined
}
}

Not sure what you're doing wrong. Seems okay to me.
Few things I can recommend:
1) Make sure you're using angularjs-1.4.
2) Always wrap everything into local function, such that you don't expose anything globally.
(function() {
angular.module('experiment', [])
.controller('MyController', function($scope){
$scope.test = function() {
alert("Test!");
};
});
angular
.module('experiment')
.directive('ftConditionButton', ftConditionButton);
function ftConditionButton() {
var directive = {
template: '<button ng-click="conditionButtonController.onClick()">Hello </button>',
restrict: 'A',
scope: {},
bindToController: {
ftConditionButton: '&'
},
controller: ConditionButtonController,
controllerAs: 'conditionButtonController',
};
return directive;
}
function ConditionButtonController($scope) {
var conditionButtonController = this;
conditionButtonController.onClick = onClick;
function onClick() {
conditionButtonController.ftConditionButton();
}
}
})();
and view:
<div ng-app="experiment">
<div ng-controller="MyController">
<div ft-condition-button="test()" />
</div>
</div>
PS, use JSFiddle next time, to demonstrate your problem.

Related

Cant use directive controller values in directive template

Im having a hard time accessing the attributes passed in to my directive from the template of that directive. I want to be able to access 'companyId' from album.tmpl.html but no matter what i try i can't get it. The strangest part is i can see it has made its way in to the controller, but somehow it's not getting from the controller to the template. I know the template is correctly calling the controller as it can succesfully print out the value of 'testVar' which is initialised inside the controller. Any advice would be appreciated.
directive + directive controller
(function () {
'use strict';
angular.module('erCommon')
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective($log) {
function albumLink(scope, element, attrs, AlbumController) {
//watch vars in here
}
return {
restrict: 'E',
scope: {
companyId: '=companyId'
},
bindToController: true,
templateUrl: 'components/temp/album.tmpl.html',
controller: 'AlbumController',
controllerAs: 'albumCtrl',
link: albumLink
};
}
})();
template ( album.tmpl.html
<div ng-controller="AlbumController as albumCtrl">
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
usage
<er-album company-id="2"></er-album>
output
test var: test var initiated
companyId:
You need to remove ng-controller from your template:
<div>
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
To achieve the result you wanted i had to modify the structure of your code slightly. Hope this helps you to understand the issue. Look for materials about isolated scopes which Angular uses with directives.
HTML:
<div ng-app="erCommon" ng-controller="AlbumController as albumCtrl">
<er-album company-id="2" test = "albumCtrl.testVar"></er-album>
</div>
Controller:
angular.module('erCommon', [])
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective() {
return {
restrict: 'E',
scope: {
test: '=test',
companyId: '#companyId'
},
template: '<div> testVar: {{test}}<BR> companyId:{{companyId}}<BR> </div>', // it will work fine with templateUrl as well, just didn't want to cr8 another file...
link: function(scope, element, attrs){
//do whatever else you might need;
}
};
}

Why is callback function into directive controller not being recognized?

I'm trying to get a directive to work with its own controller:
http://jsfiddle.net/edwardtanguay/xfbgjun5/14/
However, when I click the button:
var template = '<button ng-click="vm.addItem()">add item</button>'+
'<ul><li ng-repeat="item in vm.items">{{item}}</li></ul>';
It tells me that:
Error: vm.add is not a function
even though I define controllerAs and bindToController:
return {
restrict: 'A',
scope: {
datasource: '=',
add: '&'
},
controller: controller,
controllerAs: 'vm',
bindToController: true,
template: template
};
and pass in a scope method from the main controller:
<div item-menu datasource="customers" add="addCustomer()"></div>
Why does it not recognize add as a function on vm?
ADDENDUM: The controller looks like this:
.controller('mainController', function ($scope) {
$scope.customers = ['First','Second','Third'];
$scope.score = 0;
$scope.addCustomer = function() {
$scope.score++;
}
})
You can find the answer to your question in this blog post
If you use Angular 1.3 you need to add to the directive the following line of code:
bindToController: true,
Check the blog post and the code snippets.
When you bind to attributes, you have to bind it to $scope, not the controller.
var controller = function($scope) {
var vm = this;
function init() {
vm.items = angular.copy($scope.datasource);
}
init();
vm.addItem = function() {
$scope.add();
vm.items.push('new one');
}
}
http://jsfiddle.net/xfbgjun5/15/

Calling module controller function from child directive

My first try at angularjs.
I'm trying to create a directive that will, on click, call a function that is declared in the model's controller function.
Here is my simplified code: http://jsfiddle.net/vnj62xn8/
JS:
angular.module('tasks', [])
.controller('taskCtrl', function($scope){
$scope.complete = function(){
console.log('parent function');
}
})
.directive('taskList', function(){
return {
restrict: 'E',
scope: {
complete: '&'
},
template: '<button ng-click="complete()">Complete Task</button>',
controller: function($scope){
/*$scope.complete = function(){
console.log('child function');
}*/
}
}
});
HTML:
<div ng-app="tasks">
<task-list></task-list>
</div>
I've looked at other Q/As on SO and found that this seems to be the most generalized solution, although it will never call the complete() function found in the taskCtrl controller. It will, however, call the complete() function from the directive's controller, if uncommented. What am I missing?
You need to write the html as following:
<div ng-controller="taskCtrl">
<task-list complete="complete()"></task-list>
</div>
Here is the link to updated fiddle http://jsfiddle.net/paila/vnj62xn8/4/
Simply change your code to :
.directive('taskList', function(){
return {
restrict: 'E',
scope: {
complete: '&'
},
template: '<button ng-click="complete()">Complete Task</button>',
controller: 'taskCtrl' // Here you are referencing the taskCtrl
}
});
You just need to reference your existing controller inside the controller assignment

Show the content of a Directive when the user clicks a button - almost working

In my html page I have a button and a directive snippet like so:
<button ng-click="showProfile();"></button>
<profile ng-if="isProfile==true"></profile>
In my controller I have initialized the $scope.isProfile variable = false and have the function called by the button:
$scope.showProfile = function(contact) {
$scope.contact = contact; // this object needs to get passed to the controller that the directive initiates, but how??
$scope.isProfile = true;
};
In my app I have a directive defined as such...
app.directive('profile', function () {
return {
templateUrl: '/contacts/profile',
restrict: 'ECMA',
controller: contactsProfileController,
link:function(scope, element, attrs) {
console.log('k');
}
};
});
Everything is working but I can't figure out how to pass the $scope.contact object to the controller that the directive references.
I've tried adding scope:scope to the return {} of the directive but with no luck. Do I need to do something in the link function? I've spent the entire day reading about directives and am exhausted so any tips would be greatly appreciated!!!
Thanks in advance for any help!
Here's what the controller that's being called from the directive looks like as well:
var contactsProfileController = function($scope,contact) {
$scope.init = function() {
console.log($scope.contact); //this should output the contact value from the showProfile function.
};
....
}
try this on your directive.
<profile ng-if="isProfile==true" contact="contact"></profile>
and add this to the scope
app.directive('profile', function () {
return {
templateUrl: '/contacts/profile',
restrict: 'ECMA',
scope: {
contact: '=contact'
}
controller: contactsProfileController,
link:function(scope, element, attrs) {
console.log('k');
}
};
});
But I see a couple of issues from your code:
- your showProfile function is expecting a "contact" argument that is not being passed from the button directive, so it will be undefined.
- you are injecting a "contact" dependency on your contactsProfileController controller. Do you have a service / factory declared with that name?
Instead of contact: '#contact', do contact: '=contact'
Since your custom directive is a "component" of sorts, it is a good idea to use an isolate scope and pass the necessary data (i.e. contact) via attributes.
E.g.:
<button ng-click="showProfile(...)"></button>
<profile contact="contact" ng-if="isProfile"></profile>
$scope.showProfile = function (contact) {
$scope.contact = contact;
$scope.isProfile = true;
};
.directive('profile', function () {
return {
restrict: 'ECMA',
scope: {contact: '='}
templateUrl: '/contacts/profile',
controller: contactsProfileController
};
});
Then, the property will be available on the scope (e.g. contactsProfileController's $scope):
var contactsProfileController = function ($scope) {
$scope.$watch('contact', function (newValue) {
// The `contact` has changed, do something...
console.log($scope.contact);
});
...
};
Both of your responses were incredibly helpful and I was able to get things working in a matter of minutes after reading your posts. Thank you so much!!!
Adding contact="contact" into the directive placeholder was key as was adding the scope object to the actual directive code.
So I ended up with:
<profile ng-if="isProfile===true" contact="contact"></profile>
and
.directive('profile', function () {
return {
templateUrl: '/contacts/profile',
restrict: 'ECMA',
controller: contactsProfileController,
scope: {contact: '='},
link:function(scope, element, attrs) {
}
};
});

Angular ng-click not executing function inside a custom directive

I have created a custom directive and added a controller to it and a function on the state hamburgerClick.Here is my code:
directivesModule.directive('headerDir', [function () {
var headerDir = {
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
};
headerDir.controller = ['$state', function ($state) {
$state.hamburgerClick = function() {
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
};
}];
return headerDir;
}]);
<div>
<span class="menu-overlay"></span>
<section class="menu-bar">
<article>
<div class="menu-button" ng-click="hamburgerClick()">
<span class="hamburger-icon"></span>
</div>
<h1 class="logo"></h1>
</article>
</section>
My problem is that for some reason the function does not get executed when I am trying to click on it.ANyone know what I am doing wrong?
Try this!
directivesModule.directive('headerDir', [function () {
return{
restrict: 'E',
templateUrl: 'App/scripts/main/directives/header/HeaderDir.html',
replace: true
controller: function($scope){
$scope.hamburgerClick = function() {
var app = $('.application-wrap');
$('.application-wrap').toggleClass('menu-opened');
};
}
}
}]);
There are several things doubtful in your code
1) You should replace $state with $scope
2) You do not use your directive inside your HTML code. Instead, you refer to a directive named 'article'
3) You use replace:true, which replaces the original content of the directive. Unless you planned on defining your $('.menu-button') as header-dir directive, the call to hamburgerClick will be removed.
Furthermore, you could replace
var app = $('.application-wrap');
if (app.hasClass('menu-opened')) {
app.removeClass('menu-opened');
}
else {
app.addClass('menu-opened');
}
with
$('.application-wrap').toggleClass('menu-opened');

Resources