I would like to use directive's name inside the linking function. How could I get it?
app.directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
// How could I get directive's name here (i.e. 'myDirective')?
}
};
});
It is possible inside compile function of directive.
directives.directive('myNamedDir', ['$compile', function ($compile) {
return {
compile: function(cElem, cAttrs, transclude) {
var name = this.name;
return function linkFunction(){
//use name
}
}
}]);
Simply define it outside the injection:
var name = 'myDirective';
app.directive(name, function() {
return {
link: function(scope, element, attrs) {
console.log(name); // --> myDirective
}
};
});
app.directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
console.log(arguments.callee.directiveName); // --> myDirective
}
};
});
the arguments.callee.caller property deprecated in JavaScript
Related
I have this html fragment with my directive asScrollTop as attribute:
<div data-as-scroll-top>
<div data-ng-repeat="chatMessageOfUser in vm.chatMessagesOfUser">
<!-- use chatMessageOfUser -->
</div>
</div>
and this is my directive:
(function() {
'use strict';
angular
.module('myProject.common')
.directive('asScrollTop', asScrollTop);
function asScrollTop(validateService) {
var directive = {
restrict: 'A',
link: link
};
return directive;
////////////
function link(scope, element, attr) {
console.log(element);
element.on('scroll', function() {
if(element[0].scrollTop <= 0) {
// here I need vm.chatMessagesOfUser or the first entry of the
// vm.chatMessagesOfUser array
}
});
}
}
})();
My question now would be how I can make the the vm.chatMessagesOfUser array available in directive?
You can defined scope in directive like
var directive = {
restrict: 'A',
scope: { yourList: '=yourList' },
link: link
};
return directive;
function link(scope, element, attr) {
console.log(scope.yourList);
};
and set value in html markup
<div data-as-scroll-top your-list="vm.chatMessagesOfUser"></div>
Since your attribute directive uses the existing scope, the variables are on that scope.
function link(scope, element, attr) {
console.log(element);
element.on('scroll', function() {
if(element[0].scrollTop <= 0) {
// here I need vm.chatMessagesOfUser
console.log(scope.vm.chatMessaagesOfUser);
// or the first entry of the
// vm.chatMessagesOfUser array
console.log(scope.vm.chatMessangerOfUser[0]);
}
});
}
From reading Scopes (Part 2 of the AngularJS - from beginner to expert in 7 steps series): A $scope can contain both data and functions available in a view. If AngularJS cannot find a function on a local $scope, the containing (parent) $scope will be checked for the property or method there.
Given my implementation of a directive's compile function (based on Angularjs: understanding a recursive directive):
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
console.log(contents);
var compiledContents;
// postLink function.
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
// Get linking function.
compiledContents = $compile(contents);
}
// Link scope and the template together.
compiledContents(scope, function(clone) {
iElement.append(clone);
});
scope.myEvent = function() {
console.log("My Event handled!");
};
},
pre: function(scope, iElement, iAttrs) { }
}
}
In the code above, I have attached a function to the $scope of the instance element, and this is successfully called from the view. However I expected to be able to move the function definition from the instance element scope and into a parent controller's $scope:
angular.module('Myapp').controller('MyParentController', ['$scope',
function($scope) {
$scope.myEvent = function() {
console.log("My Event handled!");
};
}]);
However the parent controller's function is never called even though it is a parent of the directive for which I provided my own implementation of compile.
Updated to add code for the directive:
angular.module('Myapp').directive("my-directive", function(RecursionHelper) {
return {
restrict: "E",
scope: {
data: '=data'
},
templateUrl: 'view.html',
compile: function(tElement, tAttributes) {
return RecursionHelper.compile(tElement, tAttributes);
}
};
});
..and the RecursionHelper:
angular.module('Myapp').factory('RecursionHelper',
['$compile',
function($compile) {
var RecursionHelper = {
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
var compiledContents;
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function(clone) {
iElement.append(clone);
});
},
pre: function(scope, iElement, iAttrs) { }
}
}
}
return RecursionHelper;
}]);
change your scope to
scope: {
data: '=data'
myEvent: '=myEvent'
}
and then on your directive change this
angular.module('Myapp').directive("my-directive",
to
angular.module('Myapp').directive("myDirective",
then pass the function as in
<my-directive data="scope-data" my-event="scope-event-function()"></my-directive>
because your directive has an isolated scope, you can only access data in parent scope.
scope: {
data: '=data'
},
There are a few SO posts about refreshing a directive when a (model) value changes:
Angular Directive refresh on parameter change
AngularJS directive gets not updated if scope variable changes
The recommended approach is to watch a value on the scope in the linking function:
link: function(scope, element, attrs) {
scope.$watch("typeId",function(newValue,oldValue) {
// This gets called when data changes.
});
}
Given the following implementation of the recursive directive, <my-directive>:
angular.module('Myapp').directive("MyDirective", function(RecursionHelper) {
return {
restrict: "E",
scope: {
data: '=data'
},
templateUrl: 'view.html',
compile: function(tElement, tAttributes) {
return RecursionHelper.compile(tElement, tAttributes);
}
};
});
...and the RecursionHelper:
angular.module('Myapp').factory('RecursionHelper',
['$compile',
function($compile) {
var RecursionHelper = {
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
var compiledContents;
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function(clone) {
iElement.append(clone);
});
},
pre: function(scope, iElement, iAttrs) { }
}
}
}
return RecursionHelper;
}]);
I could add the listener to my linking function:
angular.module('Myapp').factory('RecursionHelper',
['$compile','MyService',
function($compile, MyService) {
var RecursionHelper = {
compile: function(tElement, tAttrs) {
var contents = tElement.contents().remove();
var compiledContents;
return {
post: function(scope, iElement, iAttrs) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function(clone) {
iElement.append(clone);
});
// The listener function.
scope.$watch(MyService.data,function(newValue,oldValue) {
// This gets called when data changes.
});
},
pre: function(scope, iElement, iAttrs) { }
}
}
}
return RecursionHelper;
}]);
I have added a Service, MyService, which contains data that may change and that I'm listening for. The first call to render the directive is started with main.html:
<my-directive data="data"></my-directive>
The directive's compile function is called and ultimately the template, view.html is rendered containing the recursive directive:
<span ng-repeat="item in data.items">
<my-directive data="item"></my-directive>
</span>
Since the <my-directive> data attribute has isolated scope, each nested directive will have its own 'sandboxed' scope.
Questions
My $watch function never gets called even though MyService.data is modified. Is this because the directive has isolated scope?
Assuming my $watch function is called, what is the actual implementation to trigger a recompilation? The SO posts just have comments of console.log statements, but I can't find anything on rendering the entire (root) directive again.
Assume that I have a directive like this
<div my-directive callback='doSomething(myArg)'></div>
angular.module('directives').directive('myDirective', function() {
return {
restrict: 'A',
scope: {
callback: '&'
},
link: function(scope, element, attrs) {
element.bind('someEvent', function() {
scope.callback({myArg: 'bla'});
});
}
}
});
If I want to pass a parameter to my scope's function, I have to do scope.callback({myArg: 'bla'}). I wonder if there's a way pass the argument without having to specify its name?
Use can use shared service in this case and inject it to directive:
angular.module("yourAppName", []).factory("mySharedService", function($rootScope){
var mySharedService = {};
mySharedService.values = {};
mySharedService.setValues = function(params){
mySharedService.values = params;
$rootScope.$broadcast('dataPassed');
}
return mySharedService;
});
And after inject it to directive. For example:
app.directive('myDirective', ['mySharedService', function(mySharedService){
return {
restrict: 'C',
link: function (scope, element, attrs) {
mySharedService.setValues(//some value//);
}
}
}]);
Then, you can get necessary value in controller.
function MyCtrl($scope, mySharedService) {
$scope.$on('dataPassed', function () {
$scope.newItems = mySharedService.values;
});
}
I am using ng-repeat and setting a model with it similar to the following
<div ng-repeat="thing in things" ng-model="thing" my-directive>
{{thing.name}}
</div>
then in my directive it looks something like this
.directive("myDirective, function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(model.name);// this gives me 'NAN'
}
}
})
My question is how can I access the values in the model? I tried model.$modelValue.name but that did not work.
If you want to bind in a scoped value then you can use the '=' in an isolated. This will appear on the scope of your directive. To read the ng-model directive, you can use =ngModel:
.directive("myDirective", function () {
return {
scope: {
model: '=ngModel'
}
link: function(scope) {
console.log(scope.model.name); // will log "thing"
}
}
});
.directive("myDirective", function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(attrs.ngModel); // will log "thing"
}
}
})
If your directive does not have isolated or child scope then you can do this:
.directive('someDirective', function() {
return {
require: ['^ngModel'],
link: function(scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0];
var someVal;
// you have to implement $render method before you can get $viewValue
ngModelCtrl.$render = function() {
someVal = ngModelCtrl.$viewValue;
};
// and to change ngModel use $setViewValue
// if doing it in event handler then scope needs to be applied
element.on('click', function() {
var val = 'something';
scope.$apply(function() {
ngModelCtrl.$setViewValue(val);
});
});
}
}
});