AngularJS - why is the ng-click handler not being fired? - angularjs

Here's the HTML:
<tr data-ng-repeat="a in assignments" data-ng-click="vm.handleAssignmentClicked()">
...
</tr>
Here are the associated directive and controller:
angular
.module('app.assignments')
.directive('stfrAssignments', assignmentsDirective)
.controller('AssignmentsController', AssignmentsController);
function assignmentsDirective() {
var directive = {
restrict: 'E',
templateUrl: 'components/assignments/assignments.html',
scope: {
assignments: '='
},
controller: 'AssignmentsController',
controllerAs: 'vm'
};
return directive;
}
function AssignmentsController() {
var vm = this;
function handleAssignmentClicked() {
console.log('handleAssignmentClicked');
}
}
When I click an assignment (a row in the table), the handleAssignmentClicked() function is not being called. Where should I look for the problem?

Update: As pointed out by a comment, the parent directive (which we supposed is being used) is using the controllerAs syntax, which is addressed in the second point [2]. I leave the first one for information.
[1] The this keyword in your controller is not the same as its $scope, unless you use the contoller as controllerName syntax.
You can inject $scope in your controller:
function AssignmentsController($scope) {
$scope.handleAssignmentClicked() {
console.log('handleAssignmentClicked');
}
}
[2] Or alternatively, which is your case:
<div ng-controller="AssignmentsController as assignCtrl">
...
<tr data-ng-repeat="a in assignCtrl.assignments" data-ng-click="assignCtrl.handleAssignmentClicked()">
And in your controller you would use this to make the function accessible:
this.handleAssignmentClicked = function() { ... }

Related

how to call twice nested method in angular 1.x?

My HTML looks like this:
Controller HTML:
<div ng-controller="Ctrl">
<first-directive></first-directive>
</div>
first-directive HTML
<li>
<second-directive></second-directive>
</li>
Controller JS:
app.controller('Ctrl', (#scope) => {
$scope.foo = function() {
console.log('do smthn');
}
});
first directive:
app.directive('first-directive', function(){
return {
restrict: 'E',
templateUrl: '/partials/first-directive.html',
replace: true,
scope: {
// some data
}
controllerAs: function(){}
}
}
second directive:
app.directive('second-directive', function(){
return {
restrict: 'E',
templateUrl: '/partials/second-directive.html',
controllerAs: function(){}
}
}
So i have controller with nested directive and there is another nested directive. When i'm trying to call $parent.foo() from first-directive it works. When i'm trying to call $parent.$parent.foo() from second-directive it doesn't work. I tried also use ng-controller="Ctrl as ctrl" and ctrl.foo() from second-directive syntax, but it doesn't work either. Why?
have you added second directive inside
<div ng-controller="Ctrl">
are you using in ng-if before second directive? because ng-if also creates its own scope
You can use $rootScope.$broadcastin nested directive controller and then on parent controller use $rootScope.$on.
In second directive
app.directive('second-directive', function(){
return {
restrict: 'E',
templateUrl: '/partials/second-directive.html',
controller: function($rootScope){
$rootScope.$broadcast('parentCtrl',{});
}
}
}
In parent controller
app.controller('Ctrl', ($scope,$rootScope) => {
$scope.foo = function() {
console.log('do smthn');
}
$rootScope.$on('parentCtrl', function (event, data){
//do the parent task here
}
});
You can also use service function to communicate between the child controller and parent controller.
As second nested directive doesn't have isolated scope, try $parent.foo()

How to pass object to directive through attribute in AngularJS?

I want to pass object (reference - two way binding) through ATTRIBUTE not by isolated scope. How can I do this? Because code bellow passing string instead of object:
HTML
<tr ng-form="rowForm" myDirective="{{row.data}}">
Directive
angular.module("app").directive("myDirective", function () {
return {
require: ["^form"],
restrict: "A",
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
return attrs.myDirective;
}, function (newValue, oldValue) {
// .....
Directives can do two-way data binding without parsing or compile anything manually, sorry for not delivering the plunker but it's rebelius and won't save for me
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.myObj = {name: 'Tibro', age: 255}
})
.directive('myDirective', function(){
return {
scope: {
'myAttribute': '='
},
template: '{{myAttribute}}',
link: function(scope){
scope.myAttribute.age= 31
}
}
})
HTML
<body ng-controller="MainCtrl">
controller: {{myObj}} <br/>
directive: <my-directive my-attribute="myObj"></my-directive>
</body>
OUTPUT
controller: {"name":"Tibro","age":31}
directive: {"name":"Tibro","age":31}
you can see from the output that passed object has been binded two-way and change made in directive is reflected on controller level
The result of {{ }} interpolation is a string. An object can't be passed like that.
Bindings are idiomatic here and thus preferable. The whole thing becomes messy when the directive is forced to use parent scope. However, it can be done by parsing scope properties manually with $parse:
$scope.$watch(function () {
var myDirectiveGetter = $parse($attrs.myDirective);
return myDirectiveGetter($scope);
}, ...);
This is a job for binding (< or =, depending on the case). If isolated scope isn't desirable, this can be done with inherited scope and bindToController:
scope: true,
bindToController: {
myDirective: '<'
},
controllerAs: `vm`,
controller: function ($scope) {
$scope.$watch('vm.myDirective', ...);
}
Notice that directive attribute is my-directive, not myDirective.
Couple of things:
myDirective in the tr should be my-directive, as per Angular's
conventions.
{{row.data}} prints the variable, you need to pass it without the
{{}} for it to go through as an object.

inherit Angular parent directive attribute with isolate scope

I'm trying to inherit an attribute from a parent custom directive with isolate scope. In the example below, I want to be able to access the api attribute on myParent from the myChild controller or link function. My end goal is to inject an instance of the api that can be accessed by the children and from the view controller.
<my-parent api="parentInstance1">
<my-child ng-repeat="field in ::data"
ng-attr-src="{{field.src||undefined}}"
</my-child>
</my-parent>
<my-parent api="parentInstance2">
<my-child ng-repeat="field in ::data"
ng-attr-src="{{field.src||undefined}}"
</my-child>
</my-parent>
A simplified version of both directives looks like this
app.directive('myParent', function () {
return {
transclude: true,
restrict: "E",
scope: {
api: '=?'
},
template: '...',
controller: function ($scope, $attrs ) {
// foo is injected from a factory instance
function foo ( ) {
}
$scope.api = {
foo: foo
}
},
link: function ($scope, $element, $attr) {
}
}
});
app.directive('myChild', function () {
return {
require: "^myParent",
restrict: "E",
scope: {
api: '=?'
},
template: "...",
controller: function ( $scope ) {
// I want to access $scope.api in link or controller
},
link: function ($scope, $element, $attr) {
// I want to access $scope.api in link or controller
}
}
});
I can't access $scope.api from the child directive but $scope.parentInstance1 and $scope.parentInstance2 are visible. I realise I can just explicitly declare but I'd rather understand how to do it correctly.
I dont know why you are referencing parentInstance1 and parentInstance2 on my-parent but the attributes on my-child are in myParent's $scope so you can reference the actual $scope.api object that is on myParent's $scope in the attributes of the my-child directive tag and then reference the name of the attribute in the isolate scope definition of the myChild directive.
<my-child inner-api="api"></my-child>
.. and then in the child directive...
app.directive('myChild', function () {
...
scope: {
innerApi: '=?'
}
...
controller: function($scope) {
$scope.innerApi // <- accessible in the controller
}
Heres a simplified fiddle...

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

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.

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/

Resources