Pass ng-model as argument in directive angualjrs - javascript

I am writing a directive in AngularJs and I want to pass ng-model as an argument.
<div class="col-md-7"><time-picker></time-picker></div>
The directive is:
app.directive('timePicker', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="text" class="form-control time-picker" ng-model="emp.signin">',
link: function ($scope, element, form) {
$(element).timepicker({'timeFormat': 'H:i:s'});
}
}
})
It is working fine, and here the ng-model is emp.signin. I want to be able to pass this ng-model dynamically as argument
How is this possible?

You can use
<div class="col-md-7"><time-picker model-value="emp.signin"></time-picker></div>
Angular
app.directive('timePicker', function () {
return {
restrict: 'E',
replace: true,
template: '<input type="text" class="form-control time-picker"ng-model="modelValue ">',
scope: {
modelValue : '=',
}
link: function ($scope, element, form) {
$(element).timepicker({'timeFormat': 'H:i:s'});
}
}
})
Explaination
The “=” prefix will create a two-way binding between the parent and
directive scope and it’ll always expect the attribute value to be the
model name which means you cannot provide an expression as the value
of attribute mapped to “=” prefix.
For reference: "http://www.undefinednull.com/2014/02/11/mastering-the-scope-of-a-directive-in-angularjs/"

Related

AngularJS - Directive's class name cannot bring into inner template

I want to make a directive which take a class name conditionally. However, I found that my code can work only if I hardcode the class name into the class attribute. If I try to use it with any expression, its failed to work.
For e.g.
// HTML
// Doesn't work (cannot find class="editable" in the final output template)
<tit-txt ng-class="true ? 'editable' : ''" ng-model="mdEnt.phone"></tit-txt>
// Works! (I can find class="editable" in the final output template)
<tit-txt class="editable" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
ngModel: '=',
},
link: function (scope, element, attrs) {
scope.editable = element.hasClass('editable') ? 'editable' : '';
},
template: '<input ng-class="editable" ng-model="ngModel" />',
};
})
Anyone can explain to me that why is it happening? How can I use it with expression?
UPDATE 1
// HTML
// Doesn't work
<tit-txt ng-class="{'editable': true}" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
title: '#',
fieldName: '#',
ngModel: '=',
},
link: function (scope, element, attrs) {
console.log(element.hasClass('editable'));
scope.editable = element.hasClass('editable') ? 'editable' : '';
},
template: '<div><span>{{title}}: </span><input id="{{fieldName}}" ng-class="{editable: true}" name="{{fieldName}}" ng-model="ngModel" /></div>',
};
})
Anyone can explain to me that why I get false in the console.log(element.hasClass('editable'));?
With the class attribute, the element's class is set by JavaScript. With the ng-class directive, the class is set by the AngularJS framework. When there are more that one directive on an element, there is no guarantee of the order of execution of the code of the respective directives.
Avoid having AngularJS manipulate the DOM and having subsequent AngularJS manipulate the model based on the state of the DOM. With MVC frameworks the Model should be the single source of truth and the DOM should be directly determined by the Model.
<tit-txt inner-class="true ? 'editable' : ''" my-model="mdEnt.phone">
</tit-txt>
app.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
title: '#',
fieldName: '#',
innerClass: '<',
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(attrs.innerClass, function(newValue) {
console.log("inner-class=", newValue);
});
},
template: `<div><span>{{title}}: </span>
<input id="{{fieldName}}" ng-class="innerClass"
name="{{fieldName}}" ng-model="myModel" />
</div>`,
};
})
Notice how the directive uses one-way, '<', binding to compute the value of the inner-class attribute from an AngularJS Expression.
Also notice that I changed ng-model to my-model. The ng- prefix is reserved for core AngularJS directives. Use of ng-model should be specifically be avoided unless the custom directive properly integrates with the ngModelController.

How to pass evaluated values to a custom element directive?

I have a custom element directive with the following template:
<div>
<input value="{{dataFromRootScope}}" />
</div>
And definition:
dirModule.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
}
);
I would like to use the directive as shown below:
<my-directive my-value="{{dataFromScope}}"></my-directive>
i.e. I want to use the evaluated dataFromScope value inside my custom directive as dataFromRootScope. How can I reach this?
You can use isolated scope two-way binding:
dirModule.directive('myDirective', function() {
return {
scope: {
model: '=myValue'
},
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
});
Where directive template is
<div>
<input ng-model="model" />
</div>
and usage is
<my-directive my-value="dataFromScope"></my-directive>
Demo: http://plnkr.co/edit/Npiq2hCO4tQHmakG4IAe?p=preview
I want to use the evaluated dataFromScope value inside my custom
directive as dataFromRootScope. How can I reach this?
Well you have two options to achieve this.
Option-1: Create an isolated scope for your directive
This way, you would need to assign value of dataFromRootScope from myValue. The = operator ensures two-way binding.
app.directive('myDirective', function() {
return {
restrict: 'E',
scope:{
dataFromRootScope: '=myValue'
},
templateUrl: 'myDirective.html'
};
}
);
'dataFromScope' will not be available in myDirective because it has isolated scope. You can access it via dataFromRootScope(see how its getting its value from myValue)
<div>
<input value="{{dataFromRootScope}}" />
</div>
Demo-1
Option-2: Enjoy shared scope.
In this case, you dont need to create an isolated scope. You can simply use dataFromScope in your directive template OR, if you really want to access it as dataFromRootScope in your template, simply assign it in your link function.
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'myDirective.html',
link:function(scope,ele,attr){
scope.dataFromRootScope = scope.dataFromScope
}
};
}
);
<div>
<input value="{{dataFromRootScope}}" />
</div>
Demo-2
You can use the '#' sign :
dirModule.directive('myDirective', function() {
return {
scope: { myValue: '#' },
restrict: 'E',
templateUrl: '/Scripts/app/directives/myDirective.html'
};
});
The '#' sign binds the evaluated value of the DOM attribute to the directive.
You can then use the directive as you asked :
<my-directive my-value="{{dataFromScope}}"></my-directive>

Pass additional parameter to template using AngularJS

I have the following directive:
app.directive("actTemplate", function() {
return {
restrict: 'A',
templateUrl: "/views/myTemplate.html"
};
});
how can i pass additional parameter to myTemplate so:
<div>
{{aditionalParam}}
...
</div>
takes the value?
Define
app.directive("actTemplate", function() {
return {
restrict: 'A',
templateUrl: "/views/myTemplate.html"
scope: {
foo: '=boo'
}
};
});
Template
<div>
{{foo}}
</div>
Use
<actTemplate boo="lalala" />
You have to specify that your directive should create it's inner scope. Scope variable could then be shared in single or double binding way (see directive scope binding doc)
app.directive("actTemplate", function() {
return {
restrict: 'A',
scope: {
additionalParam: '='
},
template: "<div>{{additionalParam}}</div>"
};
});
Then "call" your directive with this dashed syntaxe:
<div act-template additional-param="foobar">
You can have one-way binding (controller -> directive) like in this jsfiddle example.
Or two way data binding (controller <-> directive) , like in this one.

AngularJS; Send a function from the parent into directive scope

I'm creating a reusable component/widget as a directive using a template and isolated scope. I'd like to be able to also send a callback into the directive and call it in the widget. Is this possible?
Something like...
mainView template:
<my-widget callback="someFunction"></my-widget>
directive:
return {
restrict: 'E',
scope: {
callback: '='
},
templateUrl: '/partials/widget.html',
}
And the template:
<input type="text" ng-change="callback()" />
So when the widget value is changed, it triggers the callback function that was passed in the main view
What you're looking for is &. Quoting the old angular docs: "& or &attr - provides a way to execute an expression in the context of the parent scope".
Try this as your directive code:
return {
restrict: 'E',
scope: {
callback: '&'
},
templateUrl: '/partials/widget.html',
}
You can also $watch for it in the link or controller.
.directive('myDirective', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) { {
scope.$watch(attrs.myDirective, function(value){ ... });
}
...
Turns out I needed to do as suggested and use an & rather than an =, but also this still wouldn't work until I added ng-model as well to my input:
<input type="text" ng-model="myValue" ng-change="callback()" />

AngularJS : Transcluded directive and access to model

I'm creating a smart input directive which will wrap a text input element and it requires access to the model on the input element in order to manipulate some classes on the directive.
Because the input element can be one of many types (text, email, password), I need to transclude the directive and be able to add different types of validation for each.
The problem I'm having (as with many others on the Internet), is scope inheritance.
Here's what my current code looks like
HTML
<smart-input ng-model="username">
<span ng-show="isTyping">{{ placeholder }}</span>
<input type="text" name="username" ng-model="username" ng-minlength="4" ng-maxlength="20" required />
</smart-input>
JS
angular.module('myApp').directive('smartInput', function ($compile) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
model: '=ngModel'
},
template: '<div class="text-input" ng-class="{typing: isTyping}" ng-transclude>' +
'</div>',
link: function(scope, element, attrs) {
scope.isTyping = false;
scope.$watch('model', function(value) {
console.log(value);
scope.isTyping = value.length > 0;
});
}
};
});
Basically, the value inside the $watch function is undefined so obviously I'm not doing this correctly.
So, how can I bind a model to the input field, while have the directive have a reference to the same object and be able to watch it's value?
When you use isolate scope with transclusion, your scopes do not have parent/child relationship. It looks like this:
<controllerScope>
<smartInputScope>
<transcludedContentScope>
That's why in order to access smartInputScope's model property, we have to access $$prevSibling.model In your first example ng-model="username" works because this scope inherits from controllerScope, it's accessing a parent's scope property.
Check out my solution with custom transclusion: http://plnkr.co/edit/cV9urKJdcn4mKlpqPJTr?p=preview
app.directive('smartInput', function($compile) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
model: '=ngModel'
},
template: '<div class="text-input" ng-class="{typing: isTyping}">' +
'</div>',
compile: function(element, attr, linker) {
return {
pre: function(scope, element, attr) {
linker(scope, function(clone) { //bind the scope your self
element.append(clone); // add to DOM
});
},
post: function postLink(scope, iElement, iAttrs) {
scope.isTyping = false;
scope.$watch('model', function(value) {
console.log(value);
scope.isTyping = value.length > 0;
});
}
};
}
};
});
In the html, I don't need $$prevSibling anymore:
<input type="text" name="username" ng-model="model" ng-minlength="4" ng-maxlength="20" required />

Categories

Resources