Directive inside directive and ngModel - angularjs

I'm trying to create a directive that uses another directive.
The main directive, slipt a string to edit each item separately.
The problem is that the main directive doesn't receibe the ng-model changes from the inner directive.
Use the example below:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap-tpls.min.js"></script>
</head>
<body ng-app="app" ng-controller="Ctrl">
<input type="text" ng-model="values">
<editors model="values">
<script>
var app = angular.module('app', ['ui.bootstrap']);
app.controller('Ctrl', ['$scope', function($scope) {
$scope.values = '1 2';
}]);
app.directive('editors', function() {
return {
restrict: 'E',
template: '<accordion><accordion-group heading="Editor 1 - {{field1}}"><editor model="field1"></accordion-group><accordion-group heading="Editor 2 - {{field2}}"><editor model="field2"></accordion-group>',
scope: {
model: '=model'
},
controller: ['$scope', function($scope) {
$scope.$watch('model', function() {
var values = $scope.model.split(' ');
$scope.field1 = values[0];
$scope.field2 = values[1];
$('body').append($scope.field1 + ' - ');
$('body').append($scope.field2+ '<br>');
});
}]
};
});
app.directive('editor', function() {
return {
restrict: 'E',
template: '<input type="text" ng-model="model"> {{model}}',
scope: {
model: '=model'
}
};
});
</script>
</body>
</html>
Image 1 - Changing the value in the field1 (Editor 1) doesn't affect the accordion title.
Iamge 2 - Changing the root value (input outside accordion) updates the fields (field1 and field2) and accordion heading.
How can I get it working, when I change the Editor 1 value to update accordion heading ?

NEVER EVER EVER EVER EVER EVER use the prefix "ng" in your own custom directives!!!!
ng-model is a directive developped and maintained by angular. It will create its own scope.
It is supposed to receive a string and nothing else.
Just bind it that way :
scope: {
model: '=model'
},

Related

Passing scope variable to directive's controller

My directive has separate controller in js file, which has a scope variable parameterDatabase which need to be populated from calling page. I am unable to find the way to pass value to it.
<body ng-app="testAPP" ng-controller="ctl">
Directive Here
<my-cust parameterDATABASE="dt"></my-cust>
<script >
APP = angular.module("testAPP",['schedule']);
APP.controller("ctl",function($scope)
{
$scope.dt = {date:"02-03-2017",sDay:"Thu",sTime:"01:00"};
}) // end of controller
APP.directive("myCust",function()
{
return{
scope:{
parameterDATABASE:'='
},
controller:"scheduleCtrl",
templateUrl:"templateForDirective.html"
}
})
</script>
The scheduleCtrl has a variable parameterDATABASE too.
part of Directive's contrller
var APP = angular.module('schedule',[]);
APP.controller('scheduleCtrl',function($scope,$filter)
{ $scope.parameterDATABASE=[]; // This is the variable I want to populate
..............
1) According to some angular naming conventions, the attribute name of a directive should be converted into camelCase.
So, parameterDATABASE in the html Directive should be parameter-database
So, inside the directive, you should use that as,
scope: {
parameterDatabase: '='
}
So, parameterDatabase maps to ==> parameter-database
2) you can also use, parameterdatabase directly in both places without capitalizing.
Eg: parameter-database="dt" in html directive
scope: {
parameterdatabase: '='
}
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="myApp" ng-controller="ctl">
<isolate-scope-with-controller parameter-database="dt" add="addCustomer()"></isolate-scope-with-controller>
<script>
var app = angular.module("myApp", []);
app.controller("ctl",function($scope)
{
$scope.dt = {date:"02-03-2017",sDay:"Thu",sTime:"01:00"};
}) // end of
app.directive('isolateScopeWithController', function () {
var controller = ['$scope', function ($scope) {
console.log($scope.parameterDatabase)
}],
template = '<h1>I am from directive controller</h1><br><h2>{{parameterDatabase}}</h2>';
return {
restrict: 'EA', //Default in 1.3+
scope: {
parameterDatabase: '='
},
controller: controller,
template: template
};
});
</script>
</body>
</html>
PLEASE RUN THE ABOVE SNIPPET
Here is a working DEMO

Custom angular directive : how to watch for scope changes

I am writing a custom directive with a single field in its scope. This field is a dictionary with arrays in values.
I want the directive to react to any change made on this field : new entry, additional value in list, etc...
I'm just trying to figure out why :
my directive does not react when I change values in the dictionary.
directive is not even initialized with the initial dictionary.
Here is a simplified version of my script, where I only perform some logging in the sub-directive.Nothing happens when the button is clicked on the dictionary is modified :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
angular.module("myApp", [])
.controller("myCtrl", function($scope) {
$scope.dico = {};
$scope.dico["Paris"] = [];
$scope.dico["Paris"].push("Tour Eiffel");
$scope.dico["Paris"].push("Champs Elysees");
$scope.dico["London"] = [];
$scope.dico["London"].push("British Museum");
$scope.addPOI = function() {
$scope.dico["Wellington"] = [];
$scope.dico["Wellington"].push("Botanic Garden");
console.log($scope.dico);
};
})
.directive('subdirective', function() {
return {
restrict: 'E',
template: '<div><span ng-repeat="key in myDico">{{key}}</span></div>',
link: function(scope, element, iAttrs) {
console.log("test");
scope.$watch("myDico", function(newVal, oldVal) {
console.log("watch!");
console.log(newVal);
//update values...
}, true);
},
scope: {
myDico: '='
}
};
});
</script>
</head>
<body ng-app="myApp">
<div ng-controller="myCtrl">
<button ng-click="addPOI()">
Add POI
</button>
<div>
<subdirective myDico="dico"></subdirective>
</div>
</div>
</body>
</html>
I have tried to use $watch, $watchCollection, deep watch, but it does not seem to do the job.
You are missing scope binding definition in your Directive Definition Object.
scope: {
myDico: '='
}

best way in remove directive by unique id in angularjs

i have simple directive that create a text, after click to add buttom.
and after click to each directive remove and destroy directive properly.
but i need delete all directive after selected.
for example i clicked to add button for 5 step and result same bellow
Directive content
Directive content
Directive content
Directive content
Directive content
i need click to item 2 then remove and destroy scope of item 3,4,5
another question is , can i delete directive by spsephic id ?
<body ng-app="app">
<div ng-controller="MainController">
<button ng-click="Stage()">{{stage}}</button>
<div class="my-directive-placeholder"></div>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('MainController', function($scope, $compile, $element){
$scope.stage = 'Add';
var childScope;
$scope.Stage = function(){
childScope = $scope.$new();
var el = $compile( "<b my-directive></b>" )(childScope);
$('.my-directive-placeholder').append(el);
}
})
app.directive('myDirective', function($interval){
return {
template: 'Directive content<br>',
link: function(scope, element, attrs){
element.on('click', function () {
scope.$destroy();
element.remove();
});
scope.$on('$destroy', function(){
console.log('destroid');
});
}
}
});
</script>
https://jsfiddle.net/0vucwqrc/
Instead creating html on compile time, may be its better to have dedicated directive for this like, See if this fits your requirment
angular.module('MyApp', [])
angular.module('MyApp')
.controller("DirectivePageController",
function() {
var self = this;
self.fields = [{
Name: 'Directive1'
}];
self.newField = function() {
self.fields.push({
Name: ('Directive' + (self.fields.length + 1))
});
};
self.removeField = function(field) {
var index = self.fields.indexOf(field);
if (index >= 0) {
self.fields.splice(index, 1);
}
};
})
.controller("appDirectiveController", ['$scope', '$attrs',
function($scope, $attrs) {
var self = this;
var directiveScope = $scope.$parent;
self.options = directiveScope.$eval($attrs.model);
self.onOk = function() {
alert(JSON.stringify(self.options) + ' button clicked');
}
}
])
.directive('appDirective', function($compile) {
return {
transclude: true,
template: '<div ng-click="dirCtrl.onOk()" type="">{{type|uppercase}}</div>',
scope: {
index: '#',
type: '#'
},
restrict: 'E',
replace: true,
controller: 'appDirectiveController',
controllerAs: 'dirCtrl',
}
})
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<body>
<div ng-app="MyApp">
<div ng-controller="DirectivePageController as pageCtrl">
<span>Add Button</span>
<div ng-repeat="field in pageCtrl.fields track by $index">
<app-directive type="{{field.Name}}" model="field">
</app-directive>
Remove
</div>
</div>
</div>
</body>
</html>

AngularJS : Data binding in directive template doesn't work with ng-repeat

I created this simple plunker to demonstrate the problem:
http://plnkr.co/edit/xzgzsAy9eJCAJR7oWm74?p=preview
var app = angular.module('app',[]);
app.controller('ctrl',function($scope){
$scope.items = {};
})
app.directive('myDirective',function(){
return {
restrict: 'E',
scope: {
item: "=item"
},
template: "<h2>ng-repeat: </h2>" +
"<button ng-repeat='i in [1,2,3]' ng-click='item = true'>Set to true</button>" +
"<h2>no ng-repeat: </h2>" +
"<button ng-click='item = false'>Set to false</button>"
}
})
<body ng-controller='ctrl'>
<h1>Item: {{items.someItem}}</h1>
<my-directive item='items.someItem'></my-directive>
</body>
Two way data binding works when I pass a model to the directive, unless it is accessed from inside of ng-repeat.
Why is this happening and how to solve this problem?
You find answer here. Briefly, ng-repeat create a new scope, a primitive data type (boolean, integer, ...) copied by value in the new scope. But objects ({}, []) copied by pointer (not value) and it be same in the new scope and parents scope.
Edited:
I solved your case plnkr
Html:
<!DOCTYPE html>
<html ng-app='app'>
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller='ctrl'>
<h1>Item: {{items.someItem}}</h1>
<my-directive item='items.someItem'></my-directive>
</body>
</html>
JavaScript:
var app = angular.module('app', []);
app.controller('ctrl', function($scope) {
$scope.items = {};
})
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
extItem: "=item"
},
template: "<h2>ng-repeat: </h2>" +
"<button ng-repeat='i in [1,2,3]' ng-click='intItem.val = true'>Set to true</button>" +
"<h2>no ng-repeat: </h2>" +
"<button ng-click='intItem.val = false'>Set to false</button>",
link: function(scope, element, attrs) {
scope.intItem = {
val: scope.extItem
};
scope.$watch('intItem.val', function(){scope.extItem = scope.intItem.val})
}
}
})
In this solution I'm create internal object intItem with Boolean property val, which passed into ng-repeat and added $watch for intItem.val.

angularjs pass newly created array in attribute to directive

I've created this fiddle to show my issue...
http://jsfiddle.net/dQDtw/
I'm passing a newly created array to a directive, and everything is working out just fine. However, I'm getting an error in the console window indicating:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Any thoughts on what I need to massage to clean this up? I'd like to be able to reuse the directive without the need to update the controller.
Here is the html
<body ng-app="myApp">
<test-dir fam-people='[1,4,6]'> </test-dir>
<test-dir fam-people='[2,1,0]'> </test-dir>
</body>
Here is the JS.
var myApp = angular.module('myApp', []);
myApp.directive('testDir', function() {
return { restrict: 'E'
, scope: { famPeople: '=famPeople' }
, template: "<ol> <li ng-repeat='p in famPeople'> {{p}}"
};
});
That error is because your directive is not able to interpret the array as an array, Try this:
<body ng-app="myApp" ng-controller="ctrl1">
<test-dir fam-people='people'> </test-dir>
</body>
var myApp = angular.module('myApp', []);
myApp.directive('testDir', function() {
return { restrict: 'E'
, scope: { famPeople: '=' }
, template: "<ol> <li ng-repeat='p in famPeople'> {{p}}"
};
});
Controller and directive:
myApp.controller("ctrl1",function($scope){
$scope.people=[1,4,6];
});
EDIT
or you could pass it in as an attribute and parse it to an array:
<body ng-app="myApp" >
<test-dir fam-people='[1,4,6]'> </test-dir>
</body>
Directive:
var myApp = angular.module('myApp', []);
myApp.directive('testDir', function() {
return { restrict: 'E',
//scope: { famPeople: '=' },
template: "<ol> <li ng-repeat='p in people track by $index'> {{p}}",
link:function(scope, element, attrs){
scope.people=JSON.parse(attrs.famPeople);
}
};
});
See fiddle.
JSON parse doesn't work as effective when the array contains string.
For example:
<file-handler fh-services="['BOX','DROPBOX']"></file-handler>
In the directive you can use scope.$eval to convert what appears in the attribute to an array.
scope.$eval(attrs.fhServices)
what about:
<body ng-app="myApp">
<test-dir fam-people='1;4;6'> </test-dir>
<test-dir fam-people='2;1;0'> </test-dir>
</body>
and
var myApp = angular.module('myApp', []);
myApp.directive('testDir', function() {
return { restrict: 'E',
//scope: { famPeople: '=' },
template: "<ol> <li ng-repeat='p in people track by $index'> {{p}}",
link:function(scope, element, attrs){
scope.people= attrs.famPeople.split(';');
}
};
});
cleanest solution?

Resources