What I am trying to achieve is to add extra interface for input fields to be able to increace and decrease numeric value in them by clicking + and - buttons.
(In essence it is what input[type=number] fields have on chrome, but I want this to be cross-broswer compatible and also have full control of presentation accross all browsers).
Code in view:
<input data-ng-model="session.amountChosen" type="text" min="1" class="form-control input-small" data-number-input>
Directive code:
app.directive('numberInput', function() {
return {
require: 'ngModel',
scope: true,
link: function(scope, elm, attrs, ctrl) {
var currValue = parseInt(scope.$eval(attrs.ngModel)),
minValue = attrs.min || 0,
maxValue = attrs.max || Infinity,
newValue;
//puts a wrap around the input and adds + and - buttons
elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
//finds the buttons ands binds a click event to them where the model increase/decrease should happen
elm.parent().find('a').bind('click',function(e){
if(this.text=='+' && currValue<maxValue) {
newValue = currValue+1;
} else if (this.text=='-' && currValue>minValue) {
newValue = currValue-1;
}
scope.$apply(function(){
scope.ngModel = newValue;
});
e.preventDefault();
});
}
};
})
This is able to retrieve the current model value via scope.$eval(attrs.ngModel), but fails to set the new value.
Aftermath edit: this is the code that now works (in case you wan't to see the solution for this problem)
app.directive('numberInput', function() {
return {
require: 'ngModel',
scope: true,
link: function(scope, elm, attrs, ctrl) {
var minValue = attrs.min || 0,
maxValue = attrs.max || Infinity;
elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
elm.parent().find('a').bind('click',function(e){
var currValue = parseInt(scope.$eval(attrs.ngModel)),
newValue = currValue;
if(this.text=='+' && currValue<maxValue) {
newValue = currValue+1;
} else if (this.text=='-' && currValue>minValue) {
newValue = currValue-1;
}
scope.$eval(attrs.ngModel + "=" + newValue);
scope.$apply();
e.preventDefault();
});
}
};
})
Link: The link function deals with linking scope to the DOM. Using Code for Compile. While defining a custom directive we have the option to define a link against which either we can define a function or we have the option to assign an object which will have pre & post function.
ngModel is a directive which binds input, select and textarea, and stores the required user value in a variable and we can use that variable whenever we require that value.
The ngRef attribute tells AngularJS to assign the controller of a component (or a directive) to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM element to the scope. The ngRepeat directive instantiates a template once per item from a collection.
The link option is just a shortcut to setting up a post-link function. controller: The directive controller can be passed to another directive linking/compiling phase. It can be injected into other directices as a mean to use in inter-directive communication.
ngModelController methods should be used instead of $eval() to get and set the ng-model property's value.
parseInt() is not required when evaluating an attribute with a numeric value, because $eval will convert the value to a number. $eval should be used to set variables minValue and maxValue.
There is no need for the directive to create a child scope.
$apply() is not needed because the ngModelController methods ($render() in particular) will automatically update the view. However, as @Harijs notes in a comment below, $apply() is needed if other parts of the app also need to be updated.
app.directive('numberInput', function ($parse) {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
var minValue = scope.$eval(attrs.min) || 0,
maxValue = scope.$eval(attrs.max) || Infinity;
elm.wrap('<div class="number-input-wrap"></div>').parent()
.append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
elm.parent().find('a').bind('click', function (e) {
var currValue = ctrl.$modelValue,
newValue;
if (this.text === '+' && currValue < maxValue) {
newValue = currValue + 1;
} else if (this.text === '-' && currValue > minValue) {
newValue = currValue - 1;
}
ctrl.$setViewValue(newValue);
ctrl.$render();
e.preventDefault();
scope.$apply(); // needed if other parts of the app need to be updated
});
}
};
});
fiddle
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With