I have nested directives.
I send data from the first one the the second.
The problem is that I lose the the binding to the main scope.
This is my code:
Plunker
(clicking the button changes the value in the main scope but not in the directive)
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.change = function(){
var id = Math.floor((Math.random() * 4) + 0);
var val = Math.floor((Math.random() * 100) + 1);
$scope.data.items[id].id = val;
}
$scope.data = {
items: [{
id: 1,
name: "first"
}, {
id: 2,
name: "second"
}, {
id: 3,
name: "third"
}, {
id: 4,
name: "forth"
}]
}
})
.directive('firstDirective', function($compile) {
return {
replace: true,
restrict: 'A',
scope: {
data: '='
},
link: function(scope, element, attrs) {
var template = '';
angular.forEach(scope.data, function(item, key) {
var sss = JSON.stringify(item).replace(/"/g, "'");
var tmp = '<div>' +
'<div second-directive data="' + sss + '"></div>' +
'</div>';
template = template + tmp;
});
element.html(template);
$compile(element.contents())(scope);
}
}
})
.directive('secondDirective', function() {
var comp = function(element, attrs){
var data = JSON.parse(attrs.data.replace(/'/g, "\""));
var template = '<div class="second-directive">' +
'<h4>Directive 2</h4>' +
'ID :' + data.id + '<br />' +
'Name : ' + data.name +
'</div>';
element.replaceWith(template);
}
return {
replace: true,
restrict: 'A',
compile: comp
};
});
/* Put your css in here */
.second-directive{
border:1px solid green;
padding:4px;
text-align:center;
width:100px;
height:auto;
overflow:hidden;
float:left;
margin:2px;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<h2>MainCtrl</h2>
{{data}}
<BR />
<button ng-click="change()">change value</button>
<div first-directive data="data.items">
</div>
</body>
</html>
Thanks a lot
Avi
Not sure why you need nested directives. Seems to overcomplicate things. Why not just pass the data object to one directive and any changes you make in the parent controller will update in the directive as well.
http://plnkr.co/edit/gR3qBRmDotiUesS6DuyN?p=preview
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<h2>MainCtrl</h2>
{{data}}
<BR />
<button ng-click="change()">change value</button>
<div first-directive data="data.items">
</div>
</body>
</html>
template1.html
<div>
<div class="second-directive" ng-repeat="item in data">
<h4>Directive</h4>
ID :{{ item.id }} <br />
Name : {{item.name }}
</div>
</div>
app.js
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.change = function(){
var id = Math.floor((Math.random() * 4) + 0);
var val = Math.floor((Math.random() * 100) + 1);
$scope.data.items[id].id = val;
}
$scope.data = {
items: [{
id: 1,
name: "first"
}, {
id: 2,
name: "second"
}, {
id: 3,
name: "third"
}, {
id: 4,
name: "forth"
}]
};
})
.directive('firstDirective', function() {
return {
replace: true,
templateUrl: 'template1.html',
restrict: 'A',
scope: {
data: '='
},
link: function(scope, element, attrs) {
}
}
});
If you really need nested directives then you will need to look into the require option on the directive definition object where you can specify a parent directive controller which will be injected in the link function of the child directive. You can then access any properties on the parent directive scope in the child directive.
See: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object
Hope that helps.
Related
Quite straightforward, but I am fairly new to angular, and I think I have bitwise-sick, so I am kinda stuck :
// Checkboxes control existence of value in an array
var app = angular.module('myApp', []);
app.controller('MainController', function($scope) {
$scope.bitMaskFromDB = 5;
$scope.fruits = [{value:1, label: 'apple'}, {value:2, label:'orange'}, {value:4, label:'pear'}, {value:8, label:'naartjie'}];
});
app.directive('bitMask', function() {
return {
scope: {
bitMask: '=',
value: '#'
},
link: function(scope, elem, attrs ) {
var handler = function(setup) {
if (setup){
var checked = scope.bitMask&scope.value;
elem.prop('checked', checked);
}else{
var checked = elem.prop('checked');
if (!scope.bitMask&scope.value)
scope.bitMask |= scope.value
// elem.prop('checked', !checked);
scope.bitMask ^= scope.value
}
console.log ('bit = '+ scope.bitMask)
// console.log ('value = '+ scope.value)
// console.log ('checked ='+ checked)
};
var setupHandler = handler.bind(null, true);
var changeHandler = handler.bind(null, false);
elem.on('change', function() {
scope.$apply(changeHandler);
});
scope.$watch('bitMask', setupHandler, true);
}
};
});
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
</head>
<body ng-app="myApp" ng-controller='MainController'>
Bitmask checkboxes <br>
<span ng-repeat="fruit in fruits">
<input type='checkbox' value="{{fruit.value}}" bit-mask='bitMaskFromDB' > {{fruit.label}}<br />
</span>
<div>Current value of bitmask: {{bitMaskFromDB }}</div>
</body>
</html>
I failed to
1/ change the bit mask value correctly to checkbox values and
2/ show/bind value back to controller
http://plnkr.co/edit/3M8KNyOxMBgPwj9jf01q
You are very close my friend.
Here is the fix for you. Well, I don't really remember what they said about that. But there are weird behavior binding primitive to scope
updated your plkr
// Checkboxes control existence of value in an array
var app = angular.module('myApp', []);
app.controller('MainController', function($scope) {
$scope.IforgetHowTheyExplainThis = {bitMaskFromDB:5};
$scope.fruits = [{value:1, label: 'apple'}, {value:2, label:'orange'}, {value:4, label:'pear'}, {value:8, label:'naartjie'}];
});
app.directive('bitMask', function() {
return {
scope: {
bitMask: '=',
value: '#'
},
link: function(scope, elem, attrs ) {
var handler = function(setup) {
if (setup){
console.log ('value1 = '+ scope.value)
var checked = scope.bitMask&scope.value;
elem.prop('checked', checked);
}else{
console.log ('value2 = '+ scope.value)
var checked = elem.prop('checked');
if (!scope.bitMask&scope.value)
scope.bitMask |= scope.value
else
scope.bitMask ^= scope.value
}
console.log ('bit = '+ scope.bitMask)
// console.log ('value = '+ scope.value)
// console.log ('checked ='+ checked)
};
var setupHandler = handler.bind(null, true);
var changeHandler = handler.bind(null, false);
elem.on('change', function() {
scope.$apply(changeHandler);
});
scope.$watch('bitMask', setupHandler, true);
}
};
});
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
</head>
<body ng-app="myApp" ng-controller='MainController'>
Bitmask checkboxes <br>
<span ng-repeat="fruit in fruits">
<input type='checkbox' value="{{fruit.value}}" bit-mask='IforgetHowTheyExplainThis.bitMaskFromDB' > {{fruit.label}}<br />
</span>
<div>Current value of bitmask: {{IforgetHowTheyExplainThis.bitMaskFromDB }}</div>
</body>
</html>
I'm trying to create a directive inside another directive while the first directive gets an array and for each item in the array call the other directive.
i'm having trouble with passing the data correctly.
Plunker
Here is my code:
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.data = {
items: [{
id: 1,
name: "first"
}, {
id: 2,
name: "second"
}, {
id: 3,
name: "third"
}]
}
})
.directive('firstDirective', function() {
return {
replace: true,
restrict: 'A',
compile: function(element, attrs) {
var template = '';
angular.forEach(attrs.data, function(item, key) {
var tmp = '<div>' +
// '<h4>First Directive</h4>' +
'{{dataFirst}}' +
'<div second-directive data="' + item + '"></div>' +
'</div>';
element.append(tmp);
});
}
}
})
.directive('secondDirective', function() {
return {
replace: true,
restrict: 'A',
compile: function(element, attrs) {
var template = '<div class="second-directive">' +
'<h4>Directive 2</h4>' +
'ID :' + attrs.data + '<br />' +
'Name : ' + attrs.data +
'</div>';
element.replaceWith(template);
}
};
});
.second-directive{
border:1px solid green;
padding:4px;
text-align:center;
width:100px;
height:auto;
overflow:hidden;
float:left;
margin:2px;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<h2>MainCtrl</h2>
{{data}}
<div first-directive data="data.items">
</div>
</body>
</html>
Thanks a lot
Avi
As you mentioned you want to call other directive for each item in the array.
you can do it in this way .
HTML :
<body ng-controller="MainCtrl">
<div first-directive>
<div ng-repeat="oneEle in data.items">
<div second-directive dirval="oneEle">
</div>
</div>
</div>
</body>
directive('secondDirective', function() {
var tmp = '<div>' +
'<div class="second-directive" >'+
'<h4>Directive 2</h4>' +
'ID : {{dirval.id}} <br />' +
'Name : {{dirval.name}}</div>' +
'</div>';
return {
replace: true,
restrict: 'A',
scope: {
dirval: '='
},
template : tmp
}
})
if you want load dynamic template for different object, you can pass in $scope.data object.
Here is the Plunker
Thank's everyone.
This is the solution i cam up with.
angular.module('app', [])
.controller('MainCtrl', function($scope) {
$scope.data = {
items: [{
id: 1,
name: "first"
}, {
id: 2,
name: "second"
}, {
id: 3,
name: "third"
}]
}
})
.directive('firstDirective', function($compile) {
return {
replace: true,
restrict: 'A',
scope: {
data: '='
},
link: function(scope, element, attrs) {
var template = '';
angular.forEach(scope.data, function(item, key) {
var sss = JSON.stringify(item).replace(/"/g, "'");
var tmp = '<div>' +
'<div second-directive data="' + sss + '"></div>' +
'</div>';
template = template + tmp;
});
element.html(template);
$compile(element.contents())(scope);
}
}
})
.directive('secondDirective', function() {
var comp = function(element, attrs){
var data = JSON.parse(attrs.data.replace(/'/g, "\""));
var template = '<div class="second-directive">' +
'<h4>Directive 2</h4>' +
'ID :' + data.id + '<br />' +
'Name : ' + data.name +
'</div>';
element.replaceWith(template);
}
return {
replace: true,
restrict: 'A',
compile: comp
};
});
.second-directive{
border:1px solid green;
padding:4px;
text-align:center;
width:100px;
height:auto;
overflow:hidden;
float:left;
margin:2px;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<h2>MainCtrl</h2>
{{data}}
<div first-directive data="data.items">
</div>
</body>
</html>
Here, I am trying to replace the table row through directive and compile function. Somehow, the scope is not linking to newly added template.
Here is the really simple code.
Created a plnkr here: http://plnkr.co/edit/toAxkoLkWSIxYJU06iuW?p=preview
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<!-- <script src="app.js"></script>-->
</head>
<body>
<h1> stand alone </h1>
<!-- <row></row>
-->
<h1>in table</h1>
<table>
<tr><td> Row 1</td></tr>
<tr sda-data-embed='true' row><td>my row goes here</td></tr>
<tr><td> Row 4</td></tr>
</table>
<script>
var app = angular.module('plunker', []);
app.directive('row', function($compile) {
var template = '<tr><td>Row 2: This is good</td></tr><tr><td>Row 3: {{name.firstName}}</td></tr>';
return {
restrict: 'EA',
replace: true,
scope:{},
/*template: function(element, attrs) {
var templ = template;
if (!attrs.sdaDataEmbed) {
templ = '<table>' + template + '</table>';
}
return templ;
},*/
compile: function(element, attrs) {
//$scope.firstName = 'Y';
var templ = template;
if (!attrs.sdaDataEmbed) {
templ = '<table>' + template + '</table>';
}
element[0].outerHTML = templ;
},
controller: ['$scope', DataController]
}
function DataController($scope) {
$scope.name = {};
$scope.name.firstName = 'Gosh';
}
});
</script>
</body>
</html>
UPDATE : Later I found how to replace the table row, not sure if it is recommended but it works!
I replaced, compiled the html element from link function.
here is the code.
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<!-- <script src="app.js"></script>-->
</head>
<body>
<h1> stand alone </h1>
<div>
<my-row></my-row>
</div>
<h1>in table</h1>
<table>
<tr>
<td>Row 1</td>
</tr>
<tr sda-data-embed='true' my-row>
<td>my row goes here</td>
</tr>
<tr>
<td>Row 4</td>
</tr>
</table>
<script>
var app = angular.module('plunker', []);
app.directive('myRow', function($compile) {
var template = '<tr><td>Row 2: This is good</td></tr><tr><td>Row 3: {{name.firstName}}</td></tr>';
return {
restrict: 'EA',
replace: true,
scope: {},
link: function(scope, element, attrs) {
var templ = template;
if (!attrs.sdaDataEmbed) {
templ = '<table>' + template + '</table>';
}
var e = $compile(templ)(scope);
element.replaceWith(e);
},
controller: ['$scope', DataController]
}
function DataController($scope) {
$scope.name = {};
$scope.name.firstName = 'Gosh';
}
});
</script>
</body>
</html>
in your compile function instead of doing the HTML manipulation return an object with the pre and post properties
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
// or
// return function postLink( ... ) { ... }
}
then perform the html manipulation on the pre: function this will allow you to manipulate the dom pre-link
https://docs.angularjs.org/api/ng/service/$compile
I try to create general directive for filtering array in angular.
<body ng-controller="MainCtrl">
controller: <input type="text" ng-model="query.name" /> - work<br>
directive: <span filter by="name"></span> - not work
<ul>
<li ng-repeat="item in list | filter:query">{{item.name}}</li>
</ul>
</body>
controller and directive are:
app.controller('MainCtrl', function($scope) {
$scope.list = [
{id: 1, name:'aaa'},
{id: 2, name:'bbb'},
{id: 3, name:'ccc'}
];
});
app.directive('filter', function () {
return {
scope: {
by: '#'
},
link: function postLink(scope, element, attrs) {
element.append(
'<input type="text" ng-model="query.' + attrs.by + '">');
}
}
});
Filter in controller works but filter in directive doesn't. I don't know how to fix it.
Solution is fixed in plunker: http://plnkr.co/edit/WLGd6RPQEwMFRBvWslpt?p=preview
Since you have isolated the scopes you have to use eiter $parent or you have to set up two way binding using '=' i have update your example with two way binding
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
controller: <input type="text" ng-model="query.name" /> - work<br>
directive: <span filter by="query"></span> - not work
<ul>
<li ng-repeat="item in list | filter:query">{{item.name}}</li>
</ul>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function ($scope) {
$scope.list = [
{ id: 1, name: 'aaa' },
{ id: 2, name: 'bbb' },
{ id: 3, name: 'ccc' }
];
alert("123");
$scope.query = { name: "" }
});
app.directive('filter', function () {
return {
scope: {
by: '='
},
replace: true,
template:'<input ng-model="by.name"></input>'
}
});
</script>
</body>
</html>
Following my previous question, I'm now trying to call a method on the parent controller from my directive. I get an undefined parameter. Here's what I do:
<body ng-app="myApp" ng-controller="MainCtrl">
<span>{{mandat.rum}}</span>
<span>{{mandat.surname}}</span>
<input type="text" ng-model="mandat.person.firstname" />
<my-directive mandate-person="mandat.person" updateparent="updatePerson()" >
</my-directive>
</body>
And the script:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function ($scope) {
$scope.mandat = { name: "John", surname: "Doe", person: { id: 1408, firstname: "sam" } };
$scope.updatePerson = function(person) {
alert(person.firstname);
$scope.mandat.person = person;
}
});
app.directive('myDirective', function () {
return {
restrict: 'E',
template: "<div><span>{{mandatePerson.id}}<span><input type='text' ng-model='mandatePerson.firstname' /><button ng-click='updateparent({person: mandatePerson})'>click</button></div>",
replace: true,
scope: { mandatePerson: '=', updateparent: '&' }
}
}
)
when the updatePerson method gets called, person is undefined.
jsfiddle here : http://jsfiddle.net/graphicsxp/Z5MBf/7/
Just simple change your html as below
<my-directive mandate-person="mandat.person" updateparent="updatePerson(person)" >
</my-directive>
you are not passing "person" with updatePerson thats why it is not working
Accessing controller method means accessing a method on parent scope from directive controller/link/scope.
If the directive is sharing/inheriting the parent scope then it is quite straight forward to just invoke a parent scope method.
Little more work is required when you want to access parent scope method from Isolated directive scope.
There are few options (may be more than listed below) to invoke a parent scope method from isolated directives scope or watch parent scope variables (option#6 specially).
Note that I used link function in these examples but you can use a directive controller as well based on requirement.
Option#1. Through Object literal and from directive html template
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p> Directive Content</p>
<sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>
</body>
</html>
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChanged({selectedItems:selectedItems})" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
<option>--</option>
</select>
app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
items: '=',
selectedItems: '=',
selectedItemsChanged: '&'
},
templateUrl: "itemfilterTemplate.html"
}
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'TARS';
$scope.selectedItems = ["allItems"];
$scope.selectedItemsChanged = function(selectedItems1) {
$scope.selectedItemsReturnedFromDirective = selectedItems1;
}
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
working plnkr: http://plnkr.co/edit/rgKUsYGDo9O3tewL6xgr?p=preview
Option#2. Through Object literal and from directive link/scope
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p> Directive Content</p>
<sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged(selectedItems)" items="items"> </sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>
</body>
</html>
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;"
ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
<option>--</option>
</select>
app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
items: '=',
selectedItems: '=',
selectedItemsChanged: '&'
},
templateUrl: "itemfilterTemplate.html",
link: function (scope, element, attrs){
scope.selectedItemsChangedDir = function(){
scope.selectedItemsChanged({selectedItems:scope.selectedItems});
}
}
}
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'TARS';
$scope.selectedItems = ["allItems"];
$scope.selectedItemsChanged = function(selectedItems1) {
$scope.selectedItemsReturnedFromDirective = selectedItems1;
}
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
working plnkr: http://plnkr.co/edit/BRvYm2SpSpBK9uxNIcTa?p=preview
Option#3. Through Function reference and from directive html template
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p> Directive Content</p>
<sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnFromDirective}} </p>
</body>
</html>
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;"
ng-change="selectedItemsChanged()(selectedItems)" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
<option>--</option>
</select>
app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
items: '=',
selectedItems:'=',
selectedItemsChanged: '&'
},
templateUrl: "itemfilterTemplate.html"
}
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'TARS';
$scope.selectedItems = ["allItems"];
$scope.selectedItemsChanged = function(selectedItems1) {
$scope.selectedItemsReturnFromDirective = selectedItems1;
}
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
working plnkr: http://plnkr.co/edit/Jo6FcYfVXCCg3vH42BIz?p=preview
Option#4. Through Function reference and from directive link/scope
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p> Directive Content</p>
<sd-items-filter selected-items="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItemsReturnedFromDirective}} </p>
</body>
</html>
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" ng-change="selectedItemsChangedDir()" ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
<option>--</option>
</select>
app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
items: '=',
selectedItems: '=',
selectedItemsChanged: '&'
},
templateUrl: "itemfilterTemplate.html",
link: function (scope, element, attrs){
scope.selectedItemsChangedDir = function(){
scope.selectedItemsChanged()(scope.selectedItems);
}
}
}
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'TARS';
$scope.selectedItems = ["allItems"];
$scope.selectedItemsChanged = function(selectedItems1) {
$scope.selectedItemsReturnedFromDirective = selectedItems1;
}
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
working plnkr: http://plnkr.co/edit/BSqx2J1yCY86IJwAnQF1?p=preview
Option#5: Through ng-model and two way binding, you can update parent scope variables.. So, you may not require to invoke parent scope functions in some cases.
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p> Directive Content</p>
<sd-items-filter ng-model="selectedItems" selected-items-changed="selectedItemsChanged" items="items"> </sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}} </p>
</body>
</html>
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;"
ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
<option>--</option>
</select>
app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
items: '=',
selectedItems: '=ngModel'
},
templateUrl: "itemfilterTemplate.html"
}
})
app.controller('MainCtrl', function($scope) {
$scope.name = 'TARS';
$scope.selectedItems = ["allItems"];
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
working plnkr: http://plnkr.co/edit/hNui3xgzdTnfcdzljihY?p=preview
Option#6: Through $watch and $watchCollection
It is two way binding for items in all above examples, if items are modified in parent scope, items in directive would also reflect the changes.
If you want to watch other attributes or objects from parent scope, you can do that using $watch and $watchCollection as given below
html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{user}}!</p>
<p>directive is watching name and current item</p>
<table>
<tr>
<td>Id:</td>
<td>
<input type="text" ng-model="id" />
</td>
</tr>
<tr>
<td>Name:</td>
<td>
<input type="text" ng-model="name" />
</td>
</tr>
<tr>
<td>Model:</td>
<td>
<input type="text" ng-model="model" />
</td>
</tr>
</table>
<button style="margin-left:50px" type="buttun" ng-click="addItem()">Add Item</button>
<p>Directive Contents</p>
<sd-items-filter ng-model="selectedItems" current-item="currentItem" name="{{name}}" selected-items-changed="selectedItemsChanged" items="items"></sd-items-filter>
<P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}}</p>
</body>
</html>
script app.js
var app = angular.module('plunker', []);
app.directive('sdItemsFilter', function() {
return {
restrict: 'E',
scope: {
name: '#',
currentItem: '=',
items: '=',
selectedItems: '=ngModel'
},
template: '<select ng-model="selectedItems" multiple="multiple" style="height: 140px; width: 250px;"' +
'ng-options="item.id as item.name group by item.model for item in items | orderBy:\'name\'">' +
'<option>--</option> </select>',
link: function(scope, element, attrs) {
scope.$watchCollection('currentItem', function() {
console.log(JSON.stringify(scope.currentItem));
});
scope.$watch('name', function() {
console.log(JSON.stringify(scope.name));
});
}
}
})
app.controller('MainCtrl', function($scope) {
$scope.user = 'World';
$scope.addItem = function() {
$scope.items.push({
id: $scope.id,
name: $scope.name,
model: $scope.model
});
$scope.currentItem = {};
$scope.currentItem.id = $scope.id;
$scope.currentItem.name = $scope.name;
$scope.currentItem.model = $scope.model;
}
$scope.selectedItems = ["allItems"];
$scope.items = [{
"id": "allItems",
"name": "All Items",
"order": 0
}, {
"id": "CaseItem",
"name": "Case Item",
"model": "PredefinedModel"
}, {
"id": "Application",
"name": "Application",
"model": "Bank"
}]
});
You can always refer AngularJs documentation for detailed explanations about directives.
There are two ways, with which we can call using & and =.
If I am using = for a scope attribute, then
ng-click='updateparent({person: mandatePerson})'
will be changed to
ng-click='updateparent(mandatePerson)'
And in the directive,
updateparent="updatePerson()"
will change to
updateparent="updatePerson"
No need to mention arguments here, they will be passed to controller's function definition as a reference.
Using & is explained in other answers.
Here's another pattern (works in Angular 1.5).
angular.module('module', [])
.controller('MyController', function() {
var self = this;
self.msg = 0;
// implement directive event listener interface
this.onEvent = function(arg) {
self.msg++;
};
})
.directive('myDirective', function() {
return {
scope: {
data: '=',
handler: '='
},
template: '<button ng-click="handler.onEvent(data)">Emit event</button>'
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="module" ng-controller="MyController as ctrl">
<my-directive handler="ctrl" data="'...received'"></my-directive>
{{ctrl.msg}}
</div>