Passing scope variable to directive's controller - angularjs

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

Related

Template doesn't get applied for a directive inside a directive

I got a directive inside a directive that looks like this:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<body ng-app="myApp">
<test-directive></test-directive>
<script>
var app = angular.module("myApp", []);
app.directive("testDirective", ["$compile", function($compile) {
return {
restrict: 'AE',
template: '<div>just a test</div>',
link: function (scope, element, attrs) {
let autocomplete = $compile('<test-chart></test-chart>');
let content = autocomplete(scope);
element.append(content);
}
};
}]);
app.directive('testChart', function () {
return {
restrict: 'E',
transclude: true,
controllerAs: 'chartCtrl',
template: '<div><div id="container"></div><ng-transclude></ng-transclude></div>',
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
var hc = Highcharts.chart('container', {
});
}]
};
})
</script>
</body>
</html>
https://www.w3schools.com/code/tryit.asp?filename=FHYNMDW67ST5
The problem I'm having is when the inner directive is trying to initialize the highchart using:
var hc = Highcharts.chart('container', { });
This issues a highchart error #13 which is when highchart can't find an element to create a chart on. In this example: <div id="container">
Looking at the document inside the controller for the inner directive it seems to be missing the template of the directive. Thats why highchart is getting #13.
Why is not the inner directive template getting applied?
Take a closer look at your testDirective's linking function:
let autocomplete = $compile('<test-chart></test-chart>');
// Your test chart is detached here and thus its controller can't find container in the DOM
let content = autocomplete(scope);
// This line of code is never executed because the previous one throws that's why you never see your test chart being appended to the DOM
element.append(content);
To fix that you first need to append your autocomplete to the DOM. And only then perform compilation and linking. Basically you can go like that:
let content = angular.element('<test-chart></test-chart>');
element.append(content);
$compile(element.contents())(scope);

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: '='
}

Directive inside directive and ngModel

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'
},

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.

How to check if a method argument of a directive is specified in AngularJS?

I've created a custom directive which contains a button. This button calls a method from parent scope specified by 'callback' attribute.
<!DOCTYPE html>
<html ng-app="app">
<head>
<title>Simple directive</title>
<script src="js/lib/angular/angular.js"></script>
<script type="text/javascript">
var app = angular.module('app', []);
app.controller('TestController', function($scope) {
$scope.doSomething = function(param) {
alert('Something called with: ' + param);
}
})
app.directive('myDirective', function() {
var ret = {
restrict: 'E',
scope: {
user: '#',
callback: '&' // bound a function from the scope
},
template: '<div>Hello {{user}}<button ng-show="hasCallback()" ng-click="callback({userData: user})">Callback</button>',
controller: function($scope) {
$scope.hasCallback2 = function() {
var t = typeof $scope.callback;
return t == 'function';
}
$scope.hasCallback = function() {
return angular.isDefined($scope.callback);
}
}
};
return ret;
});
</script>
</head>
<body ng-controller="TestController">
<my-directive user="cat" callback="doSomething(userData)"></my-directive>
<my-directive user="dog" callback="doSomething(userData)"></my-directive>
<my-directive user="pig"></my-directive>
</body>
</html>
My question is:
How can I control visibility of button inside template? I'd like to hide it if callback attribute not specified in custom tag (see 3rd my-directive tag).
When I check typeof of callback, I always get 'function' and angular.isDefined(...) also returns true.
Using '&?' returns undefined if the attribute has not been set.
'&' = callback function is defined always.
'&?' = callback function is defined only when attribute is defined in html template.
bindToController: {
callback: '&?'
},
controller: function() {
if (this.callback === undefined) {
// attribute "callback" was not defined
}
}
Note: Works in Angular 1.4.8. I'm not sure if it works in older versions.
Looking at angularjs source code, I see this:
case '&':
parentGet = $parse(attrs[attrName]);
isolateScope[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
The parentGet is the bound function expression. Unfortunately, this is a local variable which is only available to the function assigned to isolateScope[scopeName] via closure.
Instead of trying to find a way to get that variable, a simple solution is just to check the attrs. Try:
link: function(scope,elem,attrs) {
scope.hasCallback = function() {
return angular.isDefined(attrs.callback);
}
}
DEMO

Resources