Angular - Update model without using $parsers - javascript

So I know that I can have the model be different than the view value by using $parsers.
But what if I want to have the model change, without using $parsers?
For example:
html
<input tel-input ng-model="data.phone">
{{data.phone}}
js
.directive('telInput', function($compile) {
return {
restrict: 'A',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs) {
elem.on('paste', function() {
scope.ngModel = 'special model value after pasting';
});
}
};
});
The problem with this is that when scope.ngModel = 'pasting not allowed occurs, it changes the value in the input.
With $parsers, it changes the value in the model, but the view value remains the same.
There isn't any $setModelValue method, which seems like it would serve this purpose.
Any ideas?

Try this,
.directive('telInput', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
elem.on('paste', function() {
ngModel.$setViewValue('special model value after pasting');
ngModel.$render();
});
}
};
});

Related

passing data through Angular custom directive

I created a custom directive in angular. I would like to pass parent data through the directive using scope but I'm getting 'undefined' when I log scope and scope.questionId.
HTML
<a class="waves-effect waves-light btn" my-directive="" on-flag="someFunction" question-id="question">Flag</a>
Angular Directive
angular.module('myApp').directive('myDirective', function($http) {
return {
restrict: 'A',
scope: {
onFlag: '&onFlag',
questionId: '='
},
link: function(scope, element, attrs) {
element.click(function() {
console.log(scope);
console.log(scope.questionId);
return;
});
}
};
});
Try this
elem.bind:- It uses the JQLite which is lite version of JQuery. Here we are writing the code to handle the click event performed on the directive.
It is same like JQuery $("class or id").click(). (I hope this explains is sufficient)
angular.module('myApp').directive('myDirective', function($http) {
return {
restrict: 'A',
scope: {
onFlag: '&onFlag',
questionId: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
console.log(scope.questionId);
});
}
};
});

change controller $scope from a directive

I have a controller:
function myController($scope) {
$scope.clicked = false;
}
and a directive:
function myDirective() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
});
},
template: '<div>click me</div>';
replace: true;
}
}
and I´m using it like this:
<div ng-controller="myController">
<my-directive></my-directive>
</div>
How can I change the controller value of $scope.clicked ?
thanks!
As you don't use isolated scope in your directive, you can use scope.$parent.clicked to access the parent scope property.
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$parent.clicked = ...
});
},
I would not recommend using scope.$parent to update or access the parent scope values, you can two way bind the controller variable that needs to be updated into your directive, so your directive becomes:
function myDirective() {
return {
restrict: 'E',
scope: {
clicked: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
$scope.clicked = !$scope.clicked;
});
},
template: '<div>click me</div>';
replace: true;
}
}
now pass this clicked from parent:
<div ng-controller="myController as parentVm">
<my-directive clicked="parentVm.clicked"></my-directive>
</div>
function myController() {
var parentVm = this;
parentVm.clicked = false;
}
I would recommend reading up on using controllerAs syntax for your controller as that would really solidify the concept of using two way binding here.
I like to use $scope.$emit for such purposes. It allows to send data from directive to the controller.
You should create custom listener in your controller:
$scope.$on('cliked-from-directive', function(event, data){
console.log(data)
})
As you can see, now you have full access to your controller scope and you can do whatever you want. And in your directive just to use scope.$emit
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$emit('cliked-from-directive', {a:10})
});
Here I've created jsfiddle for you

Angular directive can not scope ng-model

I'm trying to fill a form with existing values using the controller $scope to pass those to the DOM.
Now, I'm using a directive to read and write values in a specific select menu (where I use bootstrap form helpers) menu:
app.directive('currencypicker', function () {
return {
restrict: 'A',
require: '?ngModel',
scope: {
ngModel: '='
},
link: function (scope, elem, attrs, ctrl) {
elem.addClass('bfh-currencies');
elem.addClass('bfh-selectbox');
elem.bfhselectbox({
filter: (elem.attr('data-filter') == 'true') ? true : false
}).on('change.bfhselectbox', function() {
return scope.$apply(function () {
return scope.ngModel = elem.val();
});
});
return elem.bfhcurrencies({
flags: (elem.attr('data-flags') == 'true') ? true : false,
currency: scope.ngModel || 'EUR'
});
}
};
});
Here the HTML snippet
<div currencypicker id="cost-currency" data-flags="true" data-filter="true" ng-model="newEvent.cost_currency"></div>
The problem is that when I try to assign my model value to currency attribute with scope.ngModel it evaluates always to undefined and so 'EUR' value is assigned, I checked my scope with firebug, where ngModel value is "GBP".
I can't figure out why it happens.
The way you have to use your directive is the following :
app.directive('currencypicker', function () {
return {
restrict: 'A',
require: '?ngModel',
//template : you have no template nor url template?
link: function (scope, elem, attrs, ngModelCtrl) {
// You can have your "ngModelValue" into ngModelCtrl.$viewValue
// Also, you now have access to all the functions declared into ngModelController
}
};
});
And if you want more information on that topic, do not hesitate to have a look at the doc : https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

ng-select options are doubled when used with a custom directive

I am trying to implement dynamically configurable fields. I will get validation rules ng-required, ng-hidden, ng-disabled etc attributes as json from the server and set them dynamically through a directive.
I have the following directive code. It displays select values doubled JsBin link is http://jsbin.com/jiququtibo/1/edit
var app = angular.module('myapp', []);
app.directive('inputConfig', function( $compile) {
return {
require: 'ngModel',
restrict: 'A',
scope: '=',
compile: function(tElem, tAttrs){
console.log("compile 2");
tElem.removeAttr('data-input-config');
tElem.removeAttr('input-config');
tElem.attr('ng-required',true);
return {
pre: function (scope, iElement, iAttrs){
console.log('pre');
},
post: function(scope, iElement, iAttrs){
console.log("post");
$compile(tElem)(scope);
}
}
}
};
});
How can I solve this issue? I should be able to add directive dynamically.
To solve your problem you need to remove the following line from your post function:
$compile(tElem)(scope);
It's not clear to me why you are compiling here so I'm not sure if there will be any unintended side effects from this.
I found a solution following code is working.You should first clone, remove directive, prepare dom and compile
app.directive('inputConfig', function( $compile) {
return {
require: '?ngModel',
restrict: 'A',
compile:function (t, tAttrs, transclude){
var tElement = t.clone() ;
tElement.removeAttr('input-config');
tElement.attr('ng-required',true);
t.attr('ng-required',true);
return function(scope){
// first prepare dom
t.replaceWith(tElement);
// than compile
$compile(tElement)(scope);
};
}
}
});

How to create a custom input type?

I would like to create a custom input type similar to the way AngularJS implements "email", for example.
<input type="email" ng-model="user.email" />
What I would like to create is an input type like this:
<input type="path" ng-model="page.path" />
Any ideas on how this can be accomplished? So far, I've only been able to figure out how to implement custom directives where 'path' is the name of the tag, attribute or class.
For example, I can get this to work but it is inconsistent with the other form fields and I'd really like them to look the same.
<input type="text" ng-model="page.path" path />
app.directive('path', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) { ... }
};
});
You can create your own input type="path" by creating an input directive with custom logic if the type attribute is set to "path".
I've created a simple example that simply replaces \ with /. The directive looks like this:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
if (attr.type !== 'path') return;
// Override the input event and add custom 'path' logic
element.unbind('input');
element.bind('input', function () {
var path = this.value.replace(/\\/g, '/');
scope.$apply(function () {
ngModel.$setViewValue(path);
});
});
}
};
});
Example
Update: Changed on, off to bind, unbind to remove jQuery dependency. Example updated.
An alternative solution can be achieved by using the $parsers property of the ngModelController. This property represents a chain of parsers that are applied to the value of the input component before passing them to validation (and eventually assigning them to the model). With this, the solution can be written as:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
if (attr.type !== 'path') return;
ngModel.$parsers.push(function(v) {
return v.replace(/\\/g, '/');
});
}
};
});
Note that there is another property $formatters which is a pipeline of formatters that transform a model value into the value displayed in the input.
See here for the plunker.
Considering compile function is the first in line, would it not be better with:
module.directive('input', function() {
return {
restrict: 'E',
require: 'ngModel',
compile: function Compile(tElement, tAttrs) {
if (tAttrs.type !== 'path') return;
return function PostLink(scope, element, attr, ngModel) {
// Override the input event and add custom 'path' logic
element.unbind('input');
element.bind('input', function () {
var path = this.value.replace(/\\/g, '/');
scope.$apply(function () {
ngModel.$setViewValue(path);
});
});
}
}
};
});

Categories

Resources