How to pass $scope in Angular directive - javascript

I am new to AngularJS. I want to return template to directive with addition and subtraction of two numbers. I am passing $scope in function but it is not working.
I'm learning from Angular Modules with Directive
here is the code :
<html>
<head>
<title>Angular JS </title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<body>
<div ng-app="Compl">
<input type=number ng-model="no1" placeholder="Enter the First Number" />
<input type=number ng-model="no2" placeholder="Enter the First Number" />
<NoSum></NoSum>
<NoSub></NoSub>
</div>
<script>
var app = angular.module("Compl", []);
app.directive("NoSum", function($scope) {
return {
template: "Sum of Two Number" + ($scope.no1 + $scope.no2)
};
});
app.directive("NoSub", function($scope) {
return {
template: "Sub of Two Number" + ($scope.no1 - $scope.no2)
};
});
</script>
</body>
</html>

If you're not using isolated scope, you should be able to just use no1 and no2 directly in the template. assuming those are variables in the parent scope.
<script>
var app = angular.module("Compl", []);
app.directive("noSum",function(){
return{
template : "Sum of Two Number {{(no1 + no2)}}"
};
});
app.directive("noSub",function(){
return{
template : "Sub of Two Number {{(no1 - no2)}}"
};
});
</script>
You should also rename your directives, since capitalized letters have special meaning in angular. So with my changed names above, your html should look like this:
<no-sum></no-sum>
<no-sub></no-sub>
Here is a Plunker showing it working as expected
This works because without isolated scope, your directives inherit the scope of their parent, which in your case is the $rootScope. If you used isolated scope instead, you would have to pass in the variables through html attributes.

What you can do is
a) use the 'parent scope' .. so te scope of the controller of the view which contains your directive and so like this:
app.directive("NoSum",function($scope){
return {
template: "Sum of Two Number" + (scope.no1 + scope.no2) //<-- don't use scope with $ ... but only scope
link: function(scope, element, attrs) {
element.bind('error', function() {
if (attrs.src !== attrs.errSrc) {
attrs.$set('src', attrs.errSrc);
}
});
}
};
});
2 - use 'isolated scope' .. so you force to pass the scope trought your item like so:
app.directive("noSum",function($scope){
return {
restrict:'EAC'
template: "Sum of Two Number" + (item.no1 + item.no2) //<-- here you use item and in html you use like: <no-sum item="myvarscoped"></no-sum>
scope:{ item : "="}
link: function(scope, element, attrs) {
element.bind('error', function() {
if (attrs.src !== attrs.errSrc) {
attrs.$set('src', attrs.errSrc);
}
});
}
};
});

As Vivz has already mentioned, you can't pass scopes. Best practice is to share data between controllers/directives is to use factories. See this link for particular example.

Related

How to access controller scope from dynamically created directive

Basically I am trying to access controller scope property from directive's controller function. I am doing it through $parent property. It works fine for static directive but not for dynamically created directive.
please have a look on my plunker
Dynamic Directive
In a plunker, when I click on folder with Id = 1. all goes good and folder path shows as "1 path". Same goes for folder with Id = 2.
But it does not work for dynamically appended folder with Id = n
I am somewhat new to angular. Any help would be much appreciated.
Updated Answer
In light of the latest requirement:
I am trying to call the directive function (i.e updateMap) from
controller.
You can use a Service to share variables between Controllers and Isolated Directives. In the example below, the Service holds the function that will be executed. Each directive when clicked will set the Service's function to it's own updateMap() function. Then the Controller in onFolderPathClicked() calls the Services executeFunction() function, which runs the previously set function.
script.js:
var module = angular.module('testApp', []);
module.service('updateMapService', function(){
var updateMapFunction = null;
this.executeFunction = function(){
updateMapFunction();
};
this.setFunction = function(fn){
updateMapFunction = fn;
};
});
module.controller('crtl', function($scope, updateMapService) {
$scope.onFolderPathClicked = function(){
updateMapService.executeFunction();
};
});
module.directive('folder', function($compile) {
return {
restrict: 'E',
scope: {
id: '#',
folderPath: "="
},
template: '<p ng-click="onFolderClicked()">{{id}}</p>',
controller: function($scope, $element, updateMapService) {
$scope.onFolderClicked = function(){
updateMapService.setFunction(updateMap);
addFolder();
};
var addFolder = function() {
$scope.folderPath = $scope.id + ":click here for calling update map";
var el = $compile('<folder id="n" folder-path="folderPath"></folder>')($scope);
$element.parent().append(el);
};
var updateMap = function() {
alert('inside updateMap()..' + $scope.id);
}
}
}
});
index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-app="testApp" ng-controller="crtl">
<div>FolderPath : <a ng-click="onFolderPathClicked()">{{ folderPath }}</a> </div>
<folder id="1" folder-path="folderPath"></folder>
<folder id="2" folder-path="folderPath"></folder>
</div>
</html>
You could also move folder-path into a Service to save from passing it in as an attribute. The code smell being that passing it in as an attribute means doing so twice, whereas in a Service it means setting it and getting it once (code reuse).

Directive showing empty tab

I want to implement directives in my app, but I can't even start with the simplest one. Can someone tell me why?
JS:
angular
.module('app.admin.catalog.nutritional_facts')
.directive('nutritionalInfo', nutritionalInfo);
function nutritionalInfo(){
return{
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world {{ '{{quantity.qty}} {{unit.u}}' }}</div>"
};
}
HTML:
<nutritional-info quantity="{qty:4}" unit="{u:'g'}"></nutritional-info>
I'm somewhat new to Angular, so it is probably the simplest question ever. It only shows the empty label, it doesn't even show the Hello world. I already tried sending ints, strings and objects as attributes.
I've corrected your code as shown below to get your directive working as expected :
Working Fiddle
My Changes :
Corrected directive name to camel case
Corrected the angular binding in html
Corrected the objects syntax that are passed to the directive.
HTML :
<nutritional-info quantity="{qty:4}" unit="{u:'g'}"></nutritional-info>
Controller :
angular.module('app.admin.catalog.nutritional_facts', [])
.directive('nutritionalInfo', nutritionalInfo);
function nutritionalInfo(){
return{
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world {{quantity.qty}} {{unit.u}}</div>"
};
}
You have a few issues:
NutritionalInfo should be nutritionalInfo (camelCase). That's how Angular will know to associate that directive with the <nutritional-info> HTML tag. See Directive Normalization.
You're not passing in objects correctly to the directive. quantity="{qty=4}" should be quantity="{qty: 4}".
You're not evaluating the expression correctly in the template. {{ '{{quantity.qty}} {{unit.u}}' }} can simply be {{quantity.q}} {{unit.u}}. Angular expressions are interpreted like JavaScript code run on the current scope. So, you can even build expressions like {{quantity.q.toFixed(1) + ' ' + unit.u.toUpperCase()}} (resulting in 4.0 G).
With all those fixes, here's a working fiddle.
Here is my full solution:
<!doctype html>
<html lang="en" ng-app="app.admin.catalog.nutritional_facts">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body >
<nutritional-info quantity="{qty: 3}" unit="{u: 'g'}"></nutritional-info>
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.js">
</script>
<script>
function nutritionalInfo(){
return {
restrict: 'E',
scope: {
quantity: '=',
unit: '='
},
template: "<div> Hello world '{{quantity.qty}} {{unit.u}}'</div>"
};
}
angular
.module('app.admin.catalog.nutritional_facts', [])
.directive('nutritionalInfo', nutritionalInfo);
</script>
</body>
</html>

Share data between directive scope and controller $scope?

Here I created some sample for date picker, which is working fine but I need to set min and max date dynamically.. so i am passing the start and end date from
Html like this my-datepicker min="2013-07-23" max="2015-07-23" in directive scope i get the value and I need to set this value in controller $scope.datepickerOptions = { startDate :min, endDate:max} some thing like this..
var app = angular.module('myapp', ['ng-bootstrap-datepicker'])
app.directive('myDatepicker', function() {
return {
restrict: 'E',
template: '<input type="text" ng-datepicker ng-options="datepickerOptions" ng-model="ngModel">',
scope: {
date: '=',
ngModel: '=',
min: '=',
max: '=',
},
controller: function($scope) {
$scope.datepickerOptions = {
format: 'yyyy-mm-dd',
autoclose: true,
weekStart: 0,
startDate :'2013-07-23',
endDate:'2015-07-23'
};
}
};
})
app.controller('AppCtrl', ['$scope', function ($scope) {
$scope.date = '2013-08-12'
}]);
var appboot = angular.bootstrap(document, ['myapp']);
<link href="https://rawgit.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.css" rel="stylesheet"/>
<link href="http://netdna.bootstrapcdn.com/bootstrap/2.0.4/css/bootstrap.min.css" rel="stylesheet"/>
<script src="http://code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/2.0.4/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="https://rawgithub.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.js" charset="utf-8"></script>
<body>
<div>
<div ng-app="myapp" ng-controller="AppCtrl">
<my-datepicker ng-model ="date" min="2013-07-23" max="2015-07-23"></my-datepicker>
<input id="datepickerMirror" type="text" data-ng-model="date">
</div>
</div>
</body>
$scope in the directive controller IS the isolated scope from the directive. You can just grab the values from $scope.min and $scope.max.
Update The reason your code can't do this is because you're using the '=' binding which causes your directive to look for a variable named 2013-07-23 on your scope. You either need to put your value in a variable, or change the binding to '#' and use interpolation (the curly braces {{value}}), or surround your date value with single quotes inside the double quotes as in min="'2013-07-23'" max="'2015-07-23'".
https://plnkr.co/edit/Gp5SBtIAuLq5BzzIdKfp?p=preview
var app = angular.module('myapp', ['ng-bootstrap-datepicker'])
app.directive('myDatepicker', function() {
return {
restrict: 'E',
template: '<input type="text" ng-datepicker ng-options="datepickerOptions" ng-model="ngModel">',
scope: {
dateval: '=',
ngModel: '=',
min: '=',
max: '=',
},
controller: function($scope) {
$scope.datepickerOptions = {
format: 'yyyy-mm-dd',
autoclose: true,
weekStart: 0,
startDate : $scope.min,
endDate: $scope.max
};
}
};
})
app.controller('AppCtrl', ['$scope', function ($scope) {
$scope.dateval = '2013-08-12';
$scope.min = '2013-07-23';
$scope.max = '2015-07-23';
}]);
var appboot = angular.bootstrap(document, ['myapp']);
html
<!DOCTYPE html>
<html>
<head>
<link href="//rawgit.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.css" rel="stylesheet"/>
<link href="//netdna.bootstrapcdn.com/bootstrap/2.0.4/css/bootstrap.min.css" rel="stylesheet"/>
<script src="//code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/2.0.4/js/bootstrap.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="//rawgithub.com/cletourneau/angular-bootstrap-datepicker/master/dist/angular-bootstrap-datepicker.js" charset="utf-8"></script>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div>
<div ng-app="myapp" ng-controller="AppCtrl">
<my-datepicker ng-model="dateval" min="min" max="max"></my-datepicker>
<input id="datepickerMirror" type="text" data-ng-model="dateval">
</div>
</div>
</body>
</html>
As mentioned in the previous answer's Updated comments, the reason it's not working on UPDATE is that you're "binding" (scope params with "=") to a string literal. You're likely also getting a console error when you attempt to set that variable, something along the lines of object "non-assign".
That being said, is there a reason your directive NEEDS an isolated scope? If you just set the directive up using "scope: true", then your directive will prototypically inherit from the parent scope. This reduces portability, but it doesn't look like you're really shooting for that at this point.
To setup useful prototypical inheritance you'll need to also use the "as" syntax of ng-controller in your HTML view file. For the sake of example, let's say:
ng-controller="AppCtrl as appCtl"
Then move the initialization of datepickerOptions from your directive into your main AppCtrl controller. Personally I prefer the "dot" syntax as opposed to littering the scope with variables that are hard to track, so assign it to your controller instead of scope:
this.datepickerOptions = { /* min, max, etc */ }
Now in your directive (using scope:true), you can access that controller via the directive $scope. So in your directive's controller function:
$scope.datepickerOptions = $scope.appCtl.datepickerOptions
Note that I chose the "dot" syntax here because otherwise the prototypical inheritance would create a new scope element for datepickerOptions in your directive instead of traversing up and checking the scope chain. By using the dot syntax, the previous scope variable (appCtl) is accessed, and then the sub-object lookup (datepickerOptions) causes the app to traverse the scope chain up and get the object instead of shadowing it.
The last piece to address is ngModel. If you're following along up to now, all you have to do is set the ng-model (in your directive template) to read "appCtl.modelName" where modelName is the variable you want to use. Now your controller will have that variable assigned directly to it. If you want to do something when that value changes, add this to your AppCtrl controller:
// create a variable so that it can be used in callbacks where "this" changes
var _this = this;
// Create this by hand since its ng-model binding is added dynamically by the directive template
_this.modelName = null;
$scope.$watch(function() { return _this.modelName; }, function(val, oldVal)
{
// do something here, remembering that _this contains a reference to the controller itself
});
Also note that in doing this you can get rid of that other input (datepickermirror) since all your data is already in your controller, and simply accessed by the directive.
Hope that helps!

Angular Directive parameter Issue

I am creating a directive which will take the arguments passed to it from html and populate the fields in the template. The code for directive is like below.
(function(){
angular.module("MyApp",{})
.directive("myDirective",function(){
var MyLink = function(scope,element,attr){
scope.title = attr.title;
}
return{
restrict : 'EA',
transclude:true,
templateUrl:"directive.html",
link:MyLink
}
});
}())
The directive.html is like
<div >
<span data-ng-bind="title"></span>
</div>
The main page is like
<div ng-app="IndexApp" ng-controller="IndexController">
<my-directive title="hello"></my-directive>
this is a test
</div>
My issue is that why is hello not getting displayed ?
Link to plunker is here
Your module declaration was wrong, you were using {} instead of []. If you also wanted to declare the directive in another module then you need to add the dependency to your indexApp (I've done so in this plunker).
http://plnkr.co/edit/s02VmgFfWhstmVhVVrUa?p=preview
index.js
(function(){
angular.module("IndexApp",['myDirectives'])
.controller("IndexController",function($scope){
$scope.title = "this is to test";
});
}())
directive.js
(function(){
angular.module("myDirectives", [])
.directive("myDirective",function(){
var MyLink = function(scope,element,attr){
scope.title = attr.title;
}
return{
restrict : 'EA',
templateUrl:"directive.html",
link:MyLink
}
});
}())

AngularJS - How to use a service from within a directive

I'm trying to get my head around scopes and after dredging through a number of blogs, stack overflow answers and the docs I am still stuck.
angular.module('app', [])
.factory('alphabet', function () {
data = [
'c',
'b',
'a'
];
return {
get : function () {
return data;
},
set : function (val) {
data.push(val);
}
};
})
.controller('AlphaCtrl', function (alphabet) {
this.alphabet = alphabet;
})
.directive('sortableTable', function () {
return {
scope : {
"param" : '#'
},
link : function (scope) {
console.log(scope.param);
}
};
})
;
HTML:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div ng-controller="AlphaCtrl as alpha">
<table sortable-table param="{{alpha.alphabet}}">
<thead>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</body>
</html>
What I would like to do is be able to access a service/factory that's being used in an outer controller from within a directive. So for example when I handle a click event I can add items to the data. That seems to be a good way of keeping things decoupled but I am open to suggestions there.
The problem at hand is that 'param' is undefined. I've also tried using '&' but that's not doing anything for me. Could someone put me on the path to Angular righteousness?
I would inject the service directly into the directive:
.directive('sortableTable', function (alphabet) {
return {
scope : {},
link : function (scope) {
console.log(alphabet);
}
};
})
This is indeed a good way of keeping things decoupled, if this is a directive that is used across controllers and views.
Edit for a bit more complex solution: It is possible to do it by injecting the service into the scope of the directive, though I would not recommend it if you don't need to switch services on the fly, since the method above is easier. I could see some use cases though, if you would want to input a different service (with the same get/set structure) in another controller for example. Here's how you could do it via scope:
.controller('myController', function($scope, alphabet) {
$scope.alphabet = alphabet;
})
.directive('myDirective', function(){
return {
scope: {
service: '='
},
template: '<div ng-bind="service.get()"></div>'
}
})
And in the template:
<div data-my-directive service="alphabet"></div>
The trick here is using service: '=' as this creates a two-way binding between the scope-variable in the controller (which is bound to the service) and the scope-variable in the directive. http://jsfiddle.net/vt52bauu/2/
I don't think the get/set is going to work the way you are expecting in an Angular factory.
this.alphabet = alphabet.get();

Categories

Resources