AngularJS directive get inner element - angularjs

I've got next directive:
(function() {
'use strict';
angular
.module('myApp')
.directive('inner', inner);
function inner () {
return {
restrict: 'A',
scope: false,
link: linkFunc
};
function linkFunc (scope, element, attrs) {
}
}
})();
And HTML:
<span inner>{{vm.number}}</span>
How can I access vm.number's value in linkFunc? I need to take value exactly from content of the span tag.

There are various ways you can do this but here are the 2 most common ways:
ngModel
You could use ng-model like so in your template:
<span inner ng-model="vm.number">{{vm.number}}</span>
In your directive you require the ngModel where you can pull its value:
.directive( 'inner', function(){
return {
require: 'ngModel',
link: function($scope, elem, attrs, ngModel){
var val = ngModel.$modelValue
}
}
})
declare isolate scope properties
<span inner="vm.number">{{vm.number}}</span>
.directive( 'inner', function(){
return {
scope: { inner:'=' } ,
link: function($scope, elem, attrs){
var val = $scope.inner
}
}
})
Some less common ways:
use $parse service to get the value
Using the template again:
<span inner="vm.number">{{vm.number}}</span>
Let's assume you're going to Firstly you'll need to inject the $parse service in your directive's definition. Then inside your link function do the following:
var val = $parse(attrs.inner)
inherited scope for read only
I don't recommend this, because depending on how you defined your directive's scope option, things might get out of sync:
isolate (aka isolated) scopes will not inherit that value and vm.number will probably throw an undefined reference error because vm is undefined in most cases.
inherited scope will inherit the initial value from the parent scope but could diverge during run-time.
no scope will be the only case where it will stay in sync since the directive's $scope reference is the same scope present in the expression {{vm.number}}
Again I stress this is probably not the best option here. I'd only recommend this if you are suffering performance issues from a large number of repeated elements or large number of bindings. More on the directive's scope options - https://spin.atomicobject.com/2015/10/14/angular-directive-scope/

Well, In Angular directive, Link function can do almost everything controller can.
To make it very simple, we use one of them most of the time.
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.number = 5;
}).directive('inner', function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
var number = scope.number;
console.log(number);
}
}
});
Inside html :
<div inner ng-model="number">{{number}}</div>
https://plnkr.co/edit/YbXYpNtu7S3wc0zuBw3u?p=preview
In order to take value from HTML, Angular provides ng-model directive which is works on two way data binding concepts.
There are other ways which is already explain by #jusopi :)
cheers!

Related

Angular UI Typeahead custom directive - model not updated on select

I made custom directive that use Angular UI typeahead directive inside, but not working as expected. In my directive model is not updating on select. Anybody helps with that ? For testing i used static array instead of http service. Plunker HERE.
.directive('httpDictionary', ['$compile', function($compile){
return {
scope: {
},
restrict: 'A',
controllerAs: "dm",
controller: ['$scope', '$http', 'ARRAY', function($scope, $http, ARRAY){
var dm = this;
dm.dict = function(val){
return ARRAY; // for testing only
// return $http.get($scope.dictionaryUrl, { ...
}
}],
link: function(scope, element, attributes, ngModel) {
scope.dictionaryUrl = attributes.httpDictionary;
element.removeAttr('http-dictionary'); // avoid loop
element.attr('uib-typeahead', 'd for d in dm.dict($viewValue)');
$compile(element)(scope);
}
};
}])
To help with binding to a model, I usually use uib-typeahead setting typeahead-on-select. That way, you can check against or execute some additional code when the new model is set.
I made a plunker off your code found here but made some small tweaks:
I isolated the input text field into its own view so as to separate the functionality from the parent view.
I passed the test model to the directive's scope attribute and then bound it to the directives controller (using bindToController attribute) so if you ever needed the test model to communicate with the parent controller in the future, you could do so.
Hopes this helps.
I made this works like below. Plunker code here. I've bind ngModel in directive scope and used typeahead-on-select callback function. I don't think so this is elegant, but works. I've been thinking about using $watch, but without success. If You have a better soultion, i'll be glad.
.directive('httpDictionary', ['$compile', function($compile){
return {
scope: {
ngModel: '='
},
restrict: 'A',
controllerAs: "dm",
controller: ['$scope', '$http', 'ARRAY', function($scope, $http, ARRAY){
var dm = this;
dm.dict = function(val){
return ARRAY; // for testing only
// return $http.get($scope.dictionaryUrl, { ...
}
dm.select = function($model) {
$scope.ngModel = $model;
}
}],
link: function(scope, element, attributes, ngModel) {
scope.dictionaryUrl = attributes.httpDictionary;
element.removeAttr('http-dictionary'); // avoid loop
element.attr('uib-typeahead', 'd for d in dm.dict($viewValue)');
element.attr('typeahead-on-select','dm.select($model)');
element = $compile(element)(scope);
}
};
}])

How to pass object to directive through attribute in AngularJS?

I want to pass object (reference - two way binding) through ATTRIBUTE not by isolated scope. How can I do this? Because code bellow passing string instead of object:
HTML
<tr ng-form="rowForm" myDirective="{{row.data}}">
Directive
angular.module("app").directive("myDirective", function () {
return {
require: ["^form"],
restrict: "A",
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
return attrs.myDirective;
}, function (newValue, oldValue) {
// .....
Directives can do two-way data binding without parsing or compile anything manually, sorry for not delivering the plunker but it's rebelius and won't save for me
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.myObj = {name: 'Tibro', age: 255}
})
.directive('myDirective', function(){
return {
scope: {
'myAttribute': '='
},
template: '{{myAttribute}}',
link: function(scope){
scope.myAttribute.age= 31
}
}
})
HTML
<body ng-controller="MainCtrl">
controller: {{myObj}} <br/>
directive: <my-directive my-attribute="myObj"></my-directive>
</body>
OUTPUT
controller: {"name":"Tibro","age":31}
directive: {"name":"Tibro","age":31}
you can see from the output that passed object has been binded two-way and change made in directive is reflected on controller level
The result of {{ }} interpolation is a string. An object can't be passed like that.
Bindings are idiomatic here and thus preferable. The whole thing becomes messy when the directive is forced to use parent scope. However, it can be done by parsing scope properties manually with $parse:
$scope.$watch(function () {
var myDirectiveGetter = $parse($attrs.myDirective);
return myDirectiveGetter($scope);
}, ...);
This is a job for binding (< or =, depending on the case). If isolated scope isn't desirable, this can be done with inherited scope and bindToController:
scope: true,
bindToController: {
myDirective: '<'
},
controllerAs: `vm`,
controller: function ($scope) {
$scope.$watch('vm.myDirective', ...);
}
Notice that directive attribute is my-directive, not myDirective.
Couple of things:
myDirective in the tr should be my-directive, as per Angular's
conventions.
{{row.data}} prints the variable, you need to pass it without the
{{}} for it to go through as an object.

multiple directives on 1 element

Given that I have the following two directives.
1: Angular UI select - this directive uses isolate scope.
2: myDirective - in my custom directive i also use isolate scope to access the value of ngModel
I am getting the Multiple directive error cannot share isolate scope. This is how I declare the isolate scope in my directive.
require: 'ngModel',
scope: {
modelValue: "=ngModel"
},
link: function (scope, el, attrs, ctrl) {
And i use it like:
<ui-select myDirective multiple ng-model="GroupsModel" theme="select2" ng-disabled="disabled" style="width: 300px;" hidden-text-box ">
<ui-select-match placeholder="groups">{{$item}}</ui-select-match>
<ui-select-choices repeat="color in Groups ">
{{color}}
</ui-select-choices>
</ui-select>
My question is, how I can get access to the ngmodel value from my custom directive if multiple directives cannot be used together on 1 element, is there a work around that will still keep the binding ?
Updated
I cannot access the required ng models value in the following function of my directive if I don't use empty the scope: {},
scope.reset = function () {
var modelValue =ctrl.$viewValue;
$timeout(function () {
el[0].focus();
}, 0, false);
};
Here is my directive:
var app = angular.module('app');
app.directive('resetField', [
'$compile', '$timeout', '$http', function ($compile, $timeout, $http) {
return {
require: 'ngModel',
link: function (scope, el, attrs, ctrl) {
// compiled reset icon template
var template = $compile('<i ng-show="enabled" ng-mousedown="reset()" class="fa fa-floppy-o" style="padding-left:5px"></i>')(scope);
el.after(template);
scope.reset = function () {
var modelValue =ctrl.$viewValue;
$timeout(function () {
el[0].focus();
}, 0, false);
};
el.bind('input', function() {
scope.enabled = !ctrl.$isEmpty(el.val());
})
.bind('focus', function() {
scope.enabled = !ctrl.$isEmpty(el.val());
scope.$apply();
})
.bind('blur', function() {
scope.enabled = false;
scope.$apply();
});
}
};
}
]);
If you are using isolated scope only to get the ng-model for the select, you can do it without using isolated scope.
In the link function just use scope[attrs.ngModel], you can even put a watch over it (as long as the ngmodel is a object property ng-model=obj.prop1)
You can use multiple directives on one element, the issue is that applying multiple isolated scopes on one element is invalid, you can use require to require another directive in myDirective:
angular.directive('myDirective', [function(){
return {
scope: false,
require: 'ngModel',
link: function(scope, element, attr, ctrl){
scope.modelValue = ctrl.$viewValue;
}
}
}])
Use 'require' in the directive to be able to access the controller of another directive. Then you are able to inject that controller in the params of the directives implementation. However, you cannot use an isolate scope if you do this.
if there are two directives that you need to use, make sure that the second directive is not an isolated one.

Using "require" in Directive to require a parent Controller

I try to "require" a parent controller (not directive) but AngularJS returns an exception. The code is like this:
JS
app.controller("myController", function ($scole) {
...
});
app.directive("myDirective", function ($q) {
return {
require: "^myController",
template: "",
link: function (scope, element, attrs, myCtrl) {
...
}
};
});
HTML
<div ng-controller="myController as myCtrl">
...
<div my-directive>...</div>
...
</div>
Error
Error: [$compile:ctreq] Controller 'myController', required by
directive 'myDirective', can't be found!
Why?
Maybe, require property must be reference to a controller of directive?
Thanks
Require is of using other directives controllers in another directive , please refer the below example
var App = angular.module('myApp',[]);
//one directive
App.directive('oneDirective',function(){
return {
restrict: 'E',
controller:function($scope){
$scope.myName= function(){
console.log('myname');
}
}
}
});
//two directive
App.directive('twoDirective',function(){
return {
require:'oneDirective' //one directive used,
link : function(scope,ele,attrs,oneCtrl){
console.log(oneCtrl.myName())
}
}
})
Notation require: "^myController" means that your directive will try to access another directive called myController and defined on some of the ancestor tags as my-controller attribute or <my-controller> tag. In your case you don't have such directive, hence the exception.
This is not very conventional what you are trying to do, but if you really want to require outer controller in your directive you can require ngController:
app.directive("myDirective", function($q) {
return {
require: "^ngController",
template: "",
link: function(scope, element, attrs, myCtrl) {
// ...
console.log(myCtrl);
}
};
});
However, this is not very good idea. I can't imagine why you might need it like this. I would recommend to look into scope configuration properties and how you can pass executable function references into your directive from outer controller.
<div my-directive some-callback="test()"></div>
and in directive define scope:
scope: {
someCallback: '&'
}
where in controller you would have $scope.test = function() {};. Then you would not need to require controller explicitly in directive.

In an AngularJs directive, how do I call its controller's function contained within an directive element's attribute?

I am trying to call a function in a controller, which is part of a custom angular directive, following is the code,
Method 1: (Doesn't work: Controller's function doesn't write to the console)
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
When I remove the directive's controller from it and and create a new controller it works,
Method 2: (Works: Controller's function does write to the console)
HTML:
<div ng-app="MyApp" ng-controller="Ctrl">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
}
}
});
app.controller('Ctrl', function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
});
I would like know how to get the behavior of Method 2 in Method 1 i.e., to be able to call the directive's controller's function from directive's attribute.
Any help is greatly appreciated, Thank you!
In Method 1, you are creating an isolated scope and defining a scope value someCtrlFn that takes in a function from the parent scope that is using your directive. The function to use is specified by the attribute callbackFn.
The way directives work with these scope items is that they are expected to be assigned from things that are on the parent scope that is active when the directive is used. So, if you have a controller Ctrl as in your Method 2, then use the directive within that scope, your directive is trying to match the what you defined in the attribute to what is available on Ctrl's scope.
So, in your first example, it's looking for a function called ctrlFn on the parent scope, but there isn't one. It will not try to look for it on the directive's controller. This is why Method 2 works, because there is a parent scope where ctrlFn is defined, and the directive is able to properly invoke that expression.
The purpose of these scope attributes is to allow directives to bind to values or functions on a parent scope to facilitate communication. For example, to give the directive data that it will display or modify, or allow the parent to define a function the directive can invoke for a callback during an event or what have you. The parent scope cannot move into the directive's scope and force the directive's scope to use its own defined items (unless you set it up so your directive uses a default value or function if the attribute is omitted or whatever).
They are not used so a directive can define things on its scope that it uses internally. If these things are internal to the directive, you can simply add them to the scope during link or whatever is suitable.
Did you mean something like this?
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { },
link: function(scope, element, attrs) {
// defines ctrlFn that can be used later by this directive's template or controller
scope.ctrlFn = function(test) {
console.log(test);
}
// immediately invokes ctrlFn to log a message, just here to illustrate
scope.ctrlFn('Hello World!');
}
}
});
Achieved it using $rootScope instead of $scope within the directive's controller
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
<script>
var app = angular.module('MyApp', []);
app.directive('myDirective', function($rootScope) {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$rootScope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
</script>

Resources