Angular js directive issue - angularjs

Below is my code
I created a simple page and include a directive in it. And on ng-click in directive i want to call parent scope's method.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Directive Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"> </script>
</head>
<body>
<div ng-app="myapp" ng-controller="myController">
<ul-dir on-item-click="itemClick(obj)"></ul-dir>
</div>
</body>
</html>
<script>
var myapp = angular.module("myapp", []);
myapp.directive('ulDir', function() {
return {
restrict: 'E',
replace: 'true',
template: '<div id="container"><ul><li ng-repeat="content in contents">{{content.name}}</li></ul></div>',
controller: function ($scope) {
$scope.contents = [{'name':'Nishu', 'age':'20'},{'name':'Nidhi', 'age':'21'},{'name':'Kirti', 'age':'24'}];
},
scope: {
onItemClick: '&' // Pass a reference to the method
}
}
});
myapp.controller('myController', function($scope) {
$scope.itemClick = function(content){
console.log("Success : "+content);
};
});
</script>
So my console log print as "success : undefined"
So content object not passing from directive scope to parentscope.
Please help me.

I believe your call inside template should either be:
<a href="#" ng-click="onItemClick({'content':content})">
or
<a href="#" ng-click="onItemClick({'obj':content})">
Can you try. For more details see this SO post Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?

Related

AngularJS Directive Template is not display

I start learning AngularJS today with directive ... I can't get directive work. Please help
<html ng-app="ngBasic">
<head>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script>
var app = angular.module('ngBasic', []);
app.controller('ngBasicCtrl', function($scope) {
$scope.age = 20;
});
app.directive('myDirective', function($scope) {
return {
template: '<h1>{{age}}</h1>'
}
});
</script>
</head>
<body ng-controller="ngBasicCtrl">
<h1>{{age}}</h1>
<my-directive></my-directive>
<div my-directive></div>
<div class='my-directive'></div>
</body>
</html>
https://jsfiddle.net/bigzidane/t8Lv3mpg/2/
Currently, the page onlys show 20. Expecting tag but not displaying yet.
Use the scope option to create an isolated scope the template can refer to :
app.directive('myDirective', function() {
return {
scope: {
age: '='
},
template: '<h1>{{age}}</h1>'
}
});
Now you can pass the controller $scope's age to the directive scope, for example this way :
<my-directive age="age+1"></my-directive>
Will render out 21. Updated fiddle -> https://jsfiddle.net/t8Lv3mpg/3/
You have to remove the $scope from the function returning your directive as it does not take a $scope parameter.
app.directive('myDirective', function() {
return {
template: '<h1>{{age}}</h1>'
}
});

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

How to call a controller function, inside a directive link function using '&' with a parameter?

I'm trying to pass a parameter into a function, inside of a directive link function, similar to this question:
Angular: calling controller function inside a directive link function using &
However, while I've seen a few examples, like this one: Passing Parameters from a Directive to a function and I've seen a parameter value get set inside the link inside a directive. Yet, I've not seen any where you are passing a primitive, like a number in as a parameter to the directive which passes that into the controller function.
I've tried multiple things, but haven't figured out the syntax.
HTML CODE:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl">
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>
SCRIPT.JS
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function() {
$scope.count = 0;
console.log('In mainCtrl ctrlFn!');
$scope.count += count;
// console.log("count is: " + JSON.stringify($scope.count));
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '&',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({count: 10})'>Click Here</button></div>",
link: function(scope, element, attributes) {
var count = null;
scope.ctrlFn = scope.ctrlFn({count: count});
//scope.text = scope.fn({ count: 0 });
}
};
});
My plunker is here: http://plnkr.co/edit/6uDntNeqe0g343PmeCED?p=preview
Can a primitive get passed in as a parameter in this use case? If so, what am I missing here?
Aftermath:
In case someone is looking for this syntax: ctrlFn({count: 10}) in the angularjs docs, it's mentioned here under custom directives:
Often it's desirable to pass data from the isolate scope via an
expression to the parent scope, this can be done by passing a map of
local variable names and values into the expression wrapper function.
For example, the hideDialog function takes a message to display when
the dialog is hidden. This is specified in the directive by calling
close({message: 'closing for now'}). Then the local variable message
will be available within the on-close expression.
You've made two mistakes:
scope.ctrlFn = scope.ctrlFn({count: count}); -
this line overrides passed reference to a function and sets it to be value returned by this function (undefined in this case)
to pass count value to your direcive you should use = instead of &
Below is code of simplified example.
script.js:
var app = angular.module('app', []);
app.controller("mainCtrl", function($scope) {
$scope.count = 0;
$scope.ctrlFn = function(count) {
console.log(count)
console.log('In mainCtrl ctrlFn!', count);
$scope.count += count;
//Call service here
};
})
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
'count' : '=',
'ctrlFn' : '&'
},
template: "<div><button ng-click='ctrlFn({ count: 100 })'>Click Here</button></div>"
};
})
index.html:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.4.2" data-semver="1.4.2" src="https://code.angularjs.org/1.4.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id="app" ng-app="app">
<div ng-controller="mainCtrl"> {{count}}
<my-directive ctrl-fn="ctrlFn(count)"></my-directive>
</div>
</div>
</body>
</html>

AngularJS - how to pass object (created on the fly) with a interpolated data to a custom directive

I would like to create a custom directive to which I can pass an object (created on the fly) with interpolated values.
Example:
Newly created array as a parameter:
{userName: vm.data.name}
vm.data.name should be replaced with controller data.
Here is my code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom directive</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<script>
(function(angular) {
'use strict';
angular.module('testDirectiveApp', [])
.controller('testController', ['$scope', function($scope) {
var data = {
name: 'ABC'
}
var vm = this;
vm.data = data;
}])
.directive('customDirective', ['$interpolate', function($interpolate) {
return {
scope: {
customParam: '='
},
link: function(scope, element, attributes){
scope.$watch('customParam', function() {
console.log(attributes.customParam, $interpolate(attributes.customParam)(scope), scope.$eval(attributes.customParam));
});
}
};
}]);
})(window.angular);
</script>
</head>
<body ng-app="testDirectiveApp">
<div ng-controller="testController as vm">
Name: <input ng-model="vm.data.name"> <hr/>
<div custom-directive custom-param="{userName: vm.data.name}"></div>
<p>{{vm.data.name}}</p>
</div>
</body>
</html>
Is this doable? What I'm doing wrong?
Best Regards,
The problem is that you are trying to access a scope variable with the attributes object, and not the scope object.
In your directive, change attributes.customParam to scope.customParam
http://plnkr.co/edit/1o4746GamtIcwHekoKyl?p=info

How to bind to controller vars from sibling directives

I am trying to figure out how AngularJS works (or should work). I've got in mind a very simple app with a header, main content and footer. Main content has a mainCtrl that holds most data (no service at this stage). I built header and footer as directives, outside the main content div. Is it possible (or recommended) to access and alter (bind) the mainCtrl variables from my directives? I read that controller and controllerAs, create a copy of the controller, while require might be what I want. But I can't get it to work. Maybe some info and pointing to the right direction could help.
PS Is it much better to have each controller get data using a service/factory? If yes, wouldn't it be efficient to store some data in the controller instead of calling the service all the time?
Please let me know if you need any clarification.
*EDIT: Demo here: plunker Demo
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My AngularJS App</title>
<meta name="description" content="">
<link rel="stylesheet" href="bower_components/angular-material/angular-material.css">
<link rel="stylesheet" href="app.css">
</head>
<body ng-app="menuApp">
<div header-directive></div>
<hr/>
<div style="min-height: calc(100vh - 400px)" ng-controller="MainController as main">
<p>Direct access to (mainCtrl) SCOPE user: {{ user }}</p>
<p>Direct access to mainCtrl user: {{ main.user }}</p>
<ul>
<li ng-repeat="item in main.menuItems"><a ng-href="#/{{ item.toLowerCase() }}">{{ item }}</a></li>
</ul>
<!--<img src="http://ost2.gr/files/gimgs/1_random2.png">-->
<ng-view></ng-view>
</div>
<div footer-directive></div>
Below footer: {{ main.user }}
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-animate/angular-animate.js"></script>
<script src="bower_components/angular-material/angular-material.js"></script>
<script src="bower_components/angular-aria/angular-aria.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="app.js"></script>
<script src="menuApp.config.js"></script>
<script src="mainController.js"></script>
<script src="ProductsController.js"></script>
<script src="ChartsController.js"></script>
<script src="AboutController.js"></script>
<script src="footerDirective.js"></script>
<script src="headerDirective.js"></script>
</body>
</html>
Main Controller:
(function () {
'use strict';
angular
.module('menuApp')
.controller('MainController', MainController);
MainController.$inject = ['$scope'];
/* #ngInject */
function MainController($scope) {
/* jshint validthis: true */
var vm = this;
vm.activate = activate;
vm.title = 'main';
activate();
////////////////
function activate() {
vm.menuItems = ['Main', 'Products', 'Charts', 'About'];
$scope.user = 'SCOPE USER';
vm.user = 'user from MainController';
}
}
})();
header Directive:
(function () {
'use strict';
angular
.module('menuApp')
.directive('headerDirective', headerDirective);
headerDirective.$inject = ['$window'];
/* #ngInject */
function headerDirective($window) {
// Usage:
//
// Creates:
//
var directive = {
//link: link,
restrict: 'EA', //This means that it will be used as an attribute and NOT as an element. I don't like creating custom HTML elements
replace: true,
templateUrl: "header.html"
//require: '^MainController'
//scope: { user: '=' }
,controller: 'MainController'
,controllerAs: 'main'
};
return directive;
//function link(scope, element, attrs) {
//}
}
})();
Footer Directive:
(function () {
'use strict';
angular
.module('menuApp')
.directive('footerDirective', footerDirective);
footerDirective.$inject = ['$window'];
/* #ngInject */
function footerDirective($window) {
// Usage:
//
// Creates:
//
var directive = {
//link: link,
restrict: 'EA',
replace: true,
templateUrl: 'footer.html',
require: "^headerDirective"
//scope: {'main.user': '='}, // This is one of the cool things :). Will be explained in post.
//,controller: 'MainController'
//,controllerAs: 'main'
};
return directive;
//function link(scope, element, attrs) {
//}
}
})();
Header template:
<div>
<p>Scope user: {{ user }} </p>
<p>Main user: {{ main.user }}</p>
<hr/>
<p>
This part of the header is always here
</p>
<p ng-if="user">
User is logged in :D
</p>
<p ng-if="!user">
Hey buddy, log in! Be cool
</p>
<hr/>
<p>Scope user: <input ng-model="user"> {{ user }}</p>
<p>Main user: <input ng-model="main.user"> {{ main.user }}</p>
</div>
Footer template:
<md-toolbar class="md-medium-tall">
<div layout="row" layout-align="center center" flex>
<span>FOOTER</span>
</div>
$SCOPE USER: {{ user }}
<hr/>
MAIN USER: {{ main.user }}
</md-toolbar>
If your directives sitting outside of the controller div, you will not be able to access the scope of your controller no matter how you will configure the directives scope.
You can use a service and inject the service to the the MainCtrl and to the directives or a good practices for your use case is to put a Parent controller that will be the global scope of your app. Then you configure your directives scope to inherit from the Parent controller
for example
<div ng-controller="ParentCtrl">
<div header-directive></div>
<div ng-controller="ChildCtrl">
</div>
<div footer-directive></div>
</div>

Resources