I am trying to access my scope variables in link but they appear undefined
/** #ngInject */
function tablePagination() {
return {
restrict: 'E',
template: require('./tablePagination.html'),
scope: {
currentPage: '=',
totalPages: '=',
totalCount: '=',
perPage: '='
},
link: function (scope) {
console.log(scope.currentPage, scope) // scope.currentPage is undefined
}
}
}
// controller for authlogin
module.exports = tablePagination
I also tried using # rather than = and changed the binding to using {{}} but its still undefined. I could use $observe but I want to get values for multiple attributes at once to do some computation. Whats the best way to do this?
HTML Code
<table-pagination
current-page="$ctrl.tableMeta.currentPage"
total-count="$ctrl.tableMeta.totalCount"
total-pages="$ctrl.tableMeta.totalPages"
per-page="$ctrl.tableMeta.perPage"></table-pagination>
UPDATE: I wonder if its because the directive is not getting updated values from $ctrl.tableMeta which is coming from API/Async
SOLUTION!: Oh I discovered my mistake was I need to use $watch otherwise it gets the old value which is by default undefined, since its not set async by API yet
scope.$watch('currentPage', () => {
scope.start = (scope.currentPage - 1) * scope.perPage + 1
scope.end = Math.min(scope.currentPage * scope.perPage, scope.totalCount)
})
Its just an example i hope it will clear few things up.
gridPagination.html
<label>current Page:</label><span>{{ currentPage }}</span>
<br>
<label>Total Pages:</label> {{ totalPages }}
app.js
var app = angular.module("myApp", []);
mainController.js
app.controller('mainController', ['$scope', function($scope) {
$scope.title = 'My Grid';
}]);
gridDirective.js
app.directive('grid', gridPagination);
function gridPagination() {
return {
restrict: 'E',
scope: {
currentPage: '=',
totalPages: '=',
totalCount: '=',
perPage: '='
},
templateUrl: 'gridPagination.html',
link: function(scope, element, attrs) {
console.log(scope.currentPage);
console.log(scope.totalPages);
console.log(scope.totalCount);
console.log(scope.perPage);
}
};
};
index.html
<html>
<head>
<link rel="stylesheet" href="main.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="mainController">
<grid current-page="3" total-pages= "30" total-count="10" per-page="2"></grid>
</div>
<script src="app.js"></script>
<script src="mainController.js"></script>
<script src="gridDirective.js"></script>
</body>
</html>
plunker
Related
I have a isolate scope directive that I am using inside ng-repeat, which is iterating over an array from the controller of that template. The template is as follows:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="bootstrap.min.css" />
<script src="angular.min.js"></script>
<script src="script1.js"></script>
</head>
<body ng-app="AddNewTest" ng-controller="AddNewController">
<div class="items" ng-repeat="row in rows">
<add-new-row data="row" index="$index"></add-new-row>
</div>
</body>
</html>
The directive is defined as follows:
angular.module('AddNewTest', []).
directive('addNewRow', function ($timeout) {
return {
controller: 'AddNewController',
link: function (scope, element, attribute) {
element.on('keyup', function(event){
if(scope.index + 1 == scope.rows.length) {
console.log('keyup happening');
$timeout(function () {
scope.rows.push({ value: '' });
scope.$apply();
});
}
})
},
restrict: 'E',
replace: true,
scope: {
index: '='
},
template: '<div class="add-new"><input type="text" placeholder="{{index}}" ng-model="value" /></div>'
}
}).
controller('AddNewController', function ($scope, $timeout) {
$scope.rows = [
{ value: '' }
];
});
But even after adding new row and doing a $apply() the ng-repeat is not rendering the new data added. Please help.
Plnkr Link Here
Pass array of rows to directive as follow:-
scope: {
index: '=',
rows :'='
},
<add-new-row rows="rows" index="$index"></add-new-row>
Working plunker
Each ng-repeat creates an isolated scope than you declare an isolated scope inside your directive that has the same controller as the div. You're swimming in the $scope soup :)
I would personnally make a clean and independent directive with its own controller.
angular.module('AddNewTest', []).
directive('addNewRow', function () {
return {
restrict: 'E',
controller: MyController,
controllerAs: '$ctrl',
template: '{{$ctrl.rows.length}}<div class="add-new"><pre>{{$ctrl.rows | json}}</pre><input type="text" placeholder="0" ng-model="value" /></div>'
}
}).
controller('MyController', MyController);
function MyController($scope) {
var vm = this;
this.rows = [ { value: '' } ];
$scope.$watch("value",function(value){
if(value)
vm.rows.push({ value: value });
});
}
http://plnkr.co/edit/AvjXWWKMz0RKSwvYNt6a?p=preview
Of course you can still bind some data to the directive using bindToController (instead of scope:{}) and if you need an ng-repeat, do it in the directive template directly.
I'm passing through a JavaScript object to an Angular.js directive. When logging attrs in the link function, I see my attribute there, but when logging attrs.myOptions there is no data:
var directive = function() {
return {
restrict: 'E',
replace: true,
scope: {
myOptions: '='
},
link: function(scope, element, attrs) {
console.log(scope)
console.log(attrs)
console.log(attrs.myOptions)
}
}
}
And i'm implementing it in my template like so:
<directive my-options="myObject" />
myObject is a JavaScript object that's set in the controller for the template.
And I'm getting this error in the console:
Syntax Error: Token 'myObject' is unexpected, expecting [:] at column 3 of the expression [{{myObject}}] starting at [myObject}}].
As suggested above you can use like this. only change that you have to access model though scope.
var myApp = angular.module('myApp', []);
myApp.directive("directive",function() {
return {
restrict: 'E',
replace: true,
scope: {
myOptions: '='
},
link: function(scope, element, attrs) {
console.log(scope)
console.log(attrs)
console.log(scope.myOptions)
}
}
});
myApp.controller('myCtrl', function($scope) {
$scope.myObject = [1,2,3,4,5];
console.log("app controller");
});
<!doctype html>
<html>
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<head>
</head>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<directive my-options="myObject" />
</div>
</body>
</html>
It seems like I get confused by isolated scopes in directives and hope you can help me out.
I tried to wrap a piece of code (which contains some custom directives) into a new directive to reduce code duplication. Obviously I needed to give some attributes like ng-model into my new directive as a parameter to make the directive reusable. ng-model does not like expressions though (I tried ng-model="{{myVariableWhichContainsDesiredNgModelString}}" at first) and thus I ended up at this article: AngularJS - Create a directive that uses ng-model.
While the accepted answer seems to work for a simple setup, I edited the plunker from the accepted answer to test out if it would work with nested directives as well: (in my app I need to wrap directives from a third party-library which I can not edit) Plunker. In my code each directive seems to generate its own scope and two-way-databinding by using = in the scope definition does not seem to work out as desired.
EDIT: Since it was not clear what i am asking I edited the Plunker above and will rephrase the question: In the Plunker I have three input-fields which are supposed to bind to the same model-value. This works initially, but as soon as I edit the third input-field it generates its own variable in its isolated scope instead of updating the initial value. Obviously the third input field refers to the new variable from that point on. How can I avoid that behaviour and keep the input linked to $scope.model.name?
Observation: removing the isolated-scope-directive from the template makes everything work as expected...
template: '<div><my-input ng-model="myDirectiveVar"></my-input></div>',
instead of
template: '<div><my-isolated-scope-directive><my-input ng-model="myDirectiveVar"></my-input></my-isolated-scope-directive></div>',
Plunker
HTML:
<!-- this binds to the model which i would like all my inputs to bind to.-->
<input ng-model="name">
<!-- Example 1: This seems to generate a new modelvalue in the isolated-scope directive. Can I avoid this without modifying that directive?-->
<my-isolated-scope-directive><my-input ng-model="name"></my-input></my-isolated-scope-directive>
<!-- Example 2: This is what i would like my code to look like in the end: One directive which uses the code-snippet of Example 1 as template and passes some parameters into that template.-->
<my-wrapper-directive my-directive-var="name"></my-wrapper-directive>
Directives:
my-input contains a modified input-field:
app.directive('myInput', function() {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
template: '<input class="some">',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
})
my-isolated-scope-directive is a placeholder-directive with its own isolated scope to simulate the behaviour for nested directives:
.directive('myIsolatedScopeDirective', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
something: '='
},
template: '<div ng-transclude></div>',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
})
my-wrapper-directive encapsulates both previous directives and accepts a parameter which should be used as ng-model value of the input field:
.directive('myWrapperDirective', function() {
return {
restrict: 'E',
transclude: false,
replace: true,
scope: {
myDirectiveVar: '='
},
template: '<div><my-isolated-scope-directive><my-input ng-model="myDirectiveVar"></my-input></my-isolated-scope-directive></div>',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
});
Any suggestions and hints on what I am missing are appreciated. Can I maybe somehow link ng-model to a service-instance without making my directive dependant on that service?
I wouldn't do it like this as it is old notation using scopes... I would use controllerAs and bindToController
script:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function() {
this.model = { name: 'World' };
this.name = "Felipe";
});
app.directive('myInput', function() {
return {
restrict: 'E',
replace: true,
// controllerAs: 'app',
require: 'ngModel',
template: '<input class="some">',
controller: function(){
}
};
})
.directive('myIsolatedScopeDirective', function() {
return {
restrict: 'E',
transclude: true,
controllerAs: 'app1',
bindToController: {
something: '='
},
template: '<div ng-transclude></div>',
controller: function(){
}
};
})
.directive('myWrapperDirective', function() {
return {
restrict: 'E',
transclude: false,
controllerAs: 'app2',
bindToController: {
myDirectiveVar: '='
},
template: '<div><my-isolated-scope-directive>'+
'<my-input ng-model="app2.myDirectiveVar"></my-input>'+
'</my-isolated-scope-directive></div>',
controller: function(){
}
};
});
index:
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl as main">
This scope value
<input ng-model="main.model.name">
<my-isolated-scope-directive>
<my-input ng-model="main.model.name"></my-input>
</my-isolated-scope-directive>
<my-wrapper-directive my-directive-var="main.model.name">
</my-wrapper-directive>
</body>
</html>
See the plunker:
http://plnkr.co/edit/VD0wXO1jivQc3JvfQFTh?p=preview
UPDATE
yes, good point, so if you want to use controllerAs, you need angular 1.2 as minimum, for bindToController you need angular 1.3
I have a parent directive in which its controller makes a call through a service to get some data.
sharedService.getData(options).then(function(data){
$scope.data = data;
});
Now i need this data in my child controller.
What i have already tried are the ff:
1) Through $timeout i get the data after sometime but it doesn't seem a good solution impacting performance
2) watchCollection() - i watched if newValue !== oldValue
problem being the data is huge so it takes a toll of performance
Now the issue i'm getting is the child directive gets executed after parent BUT before the data comes back from the service and i'm not able to get that data in my child directive via $scope.data.
Is there any solution to get data from parent directive to child directive when i have to wait for data to come in parent?
You can include your parent directive controller in your child directive by using require.
angular.module('myApp', [])
.directive('dirParent', function() {
return {
restrict: 'E',
scope: {},
controller: ['$scope', function($scope) {
}],
};
})
.directive('dirChild', function() {
return {
require: '^dirParent', // include directive controller
restrict: 'E',
scope: {
},
link: function(scope, element, attrs, paretCtrl) {
var data = paretCtrl.getMyData();
}
};
})
It's always a best to use service for communication and and business logic. Here is an example. Please check. This might solve your problem.
// Code goes here
angular.module('app', [])
.factory('messageService', function() {
return {
message: null
}
})
.directive('parentDir', function() {
return {
scope: {}, //isolate
template: '<input type="text" ng-model="PDirInput"/><button ng-click="send()">Send</button>',
controller: function($scope, messageService) {
$scope.send = function() {
messageService.message = $scope.PDirInput;
}
}
}
})
.directive('childDir', function() {
return {
scope: {}, //isolate
template: '<code>{{CDirInput.message}}</code>',
controller: function($scope, messageService) {
$scope.CDirInput = messageService;
}
}
})
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#*" data-semver="2.0.0-alpha.31" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<HR/>Parent Directive
<parent-dir></parent-dir>
<br/>
<HR/>Child Directive
<child-dir></child-dir>
<HR/>
</body>
</html>
How do i change the value of bar from directive2 so that it is reflected in directive1
If i make the scope:false it is happening.Is there any other way, to make this happen.(because in the code i am writting , i cannot make scope:false).
My basic requirement is to make one directive to talk to another.
Here you can try the plunkr version of the below code
HTML snippet
<body ng-controller="MainCtrl">
this is directive1: <div directive1></div>.<br/>
this is directive2: <div directive2></div>.
</body>
JS snippet
var app = angular.module('myApp', []);
app.directive('directive1', function() {
return {
restrict: 'A',
replace: true,
template: '<span>{{bar}}</span>'
}
});
app.directive('directive2', function() {
return {
restrict: 'A',
scope:{
},
replace: true,
link:function(s,e,a){
s.bar = "Changed value";
},
template: '<b>{{bar}}</b>'
}
});
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.bar ="original value";
});
You could simply use bar inside your isolate scope, that will do two way binding with your variable which is assigned to bar attribute, That means change inside a directive on bar variable will reflect the changes on controller scope variable.
Markup
<body ng-controller="MainCtrl">
this is directive1: <div directive1></div>.
<br />
this is directive2: <div directive2 bar="bar"></div>.
</body>
Directive
app.directive('directive2', function() {
return {
restrict: 'A',
scope:{
'bar': '=' //<-- Change here
},
replace: true,
link:function(s,e,a){
s.bar = "Changed value";
},
template: '<b>{{bar}}</b>'
}
});
Working Plunkr
I have modified your code in here
Share the variable in both directives by passing it as '=' in the scope, changing it in one directive will change it in the the other.
<body ng-controller="MainCtrl">
this is directive1: <div directive1 bar="bar"></div>.
<br />
this is directive2: <div directive2 bar="bar"></div>.
</body>
var app = angular.module('myApp', []);
app.directive('directive1', function() {
return {
restrict: 'A',
scope:{bar:'='},
replace: true,
template: '<span>{{bar}}</span>'
}
});
app.directive('directive2', function() {
return {
restrict: 'A',
scope:{bar:'='},
replace: true,
link:function(s,e,a){
s.bar = "Changed value";
},
template: '<b>{{bar}}</b>'
}
});
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.bar ="original value";
});
If they don't share the same $scope, the only way is using angularjs events.
In first directive you put:
$rootScope.$on('myEvent', function (event, data) {
$scope.bar = data.bar;
});
In the second one when bar change:
$scope.$emit('myEvent', {bar: bar});
Taking into account that are directives completely unrelated.