AngularJS Directive does error when input is empty - angularjs

I'm using this directive to limit my input to 5 caracters for percentage
monApp.directive('awLimitLength', function () {
return {
restrict: "A",
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
attrs.$set("ngTrim", "false");
var limitLength = parseInt(attrs.awLimitLength, 10);// console.log(attrs);
scope.$watch(attrs.ngModel, function(newValue) {
if(ngModel.$viewValue.length>limitLength){
ngModel.$setViewValue( ngModel.$viewValue.substring(0, limitLength ) );
ngModel.$render();
}
});
}
};
})
and using it like this :
<input name="name" type="text" ng-model="nameVar" aw-limit-length="5"/>
BUt when the input is empty, firebug notice several errors like this :
Error: ngModel.$viewValue is undefined
Error: ngModel.$viewValue is null
How could i avoid theses errors ? Thank you.
I'd like the directive not to be started when an input is empty.
I've tried this : But it doesn't work :
monApp.directive('awLimitLength', function () {
return {
restrict: "A",
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
attrs.$set("ngTrim", "false");
var limitLength = parseInt(attrs.awLimitLength, 5);// console.log(attrs);
if(attrs.ngModel!=null){
scope.$watch(attrs.ngModel, function(newValue) {
if(ngModel.$viewValue.length>limitLength){
ngModel.$setViewValue( ngModel.$viewValue.substring(0, limitLength ) );
ngModel.$render();
}
});
}
}
};
})

Check ngModel.$viewValue exist before taking its length
scope.$watch(attrs.ngModel, function(newValue) {
if(ngModel.$viewValue && ngModel.$viewValue.length>limitLength){
ngModel.$setViewValue( ngModel.$viewValue.substring(0, limitLength ) );
ngModel.$render();
}
});

You have used
require: 'ngModel' in directive. so you can't use this directive without value in model variable.
Remove require: 'ngModel' & it will work.

Related

How to $setDirty() form using directive in AngularJS

When the inputbox will change, then I'd like to set my form to dirty. I want to make it using directive. This is what I already did.
HTML:
<form name="myForm">
<type="text" ng-model="myModel" set-form-dirty>
</form>
Directive:
app.directive('setFormDirty', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function(newValue) {
console.log(newValue);
// Here I should set form to dirty. But I don't know how?
});
}
};
});
#EDIT
I need this because myModel is changing by different function. And that function won't set form to $dirty
You can watch the modelvalue on the ngModelController.
.directive('setFormDirty', function(){
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(function(){
return ngModel.$modelValue;
},function(newValue, oldValue){
//Check stuff here and use below code to set it to dirty
ngModel.setDirty();
});
}
};
Solved in this way:
angular.module("qs.forms").directive("setFormDirty", function () {
return {
require: ["ngModel", "^form"],
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
return ctrls[0].$modelValue;
}, function (newValue) {
if (newValue) {
ctrls[1].$dirty = true;
}
});
}
};
});
Form could be passed by require: ["ngModel", "^form"],

Angular directive update view on different events (Character Counter)

I'm trying to build a characterCounter attribute directive for input fields. My thoughts are to require: 'ngModel' to get the length of the modelValue in ng-model and to pass a max-length in the scope of the directive.
<input ng-model="inputModel" max-character-counter max-length="10"/>
I have my directive most of the way there I'm just struggling with how I get the view to update. Any help with this is greatly appreciated.
angular.module('app').directive('maxCharacterCounter', [function(){
return {
restrict: 'A',
require: '?ngModel',
scope: {
maxLength: "="
},
link: function (scope, elem, attrs, ngModel) {
if (!ngModel) return;
console.log(ngModel);
ngModel.$render = function() {
console.log('render');
var el = angular.element(attrs.$$element);
el.after('<span class="input-group-addon">' + scope.charactersLeft + '</span>');
}
elem.on('blur keyup change', function() {
scope.$evalAsync(read);
});
read();
function read() {
scope.charactersLeft = ngModel.$modelValue.length == undefined ? scope.maxLength : scope.maxLength - ngModel.$modelValue.length;
console.log('Characters Left:', scope.charactersLeft);
console.log('View Value: ', ngModel.$viewValue);
console.log('Model Value: ', ngModel.$modelValue);
updateViewValue();
}
function updateViewValue() {
//How do I update the view for scope.charactersLeft
//ngModel.$viewValue(scope.charactersLeft);
console.log('scope: ', scope);
}
}
}}]);
Plunker: http://plnkr.co/edit/F0PzE6?p=preview
Final Solution:
angular.module('app').directive('maxCharacterCounter', ['$timeout', '$log', function($timeout, $log) {
return {
restrict: 'A',
require: '?ngModel',
scope: {
maxLength: "#"
},
link: function (scope, elem, attrs, ngModel) {
if (!ngModel) {
$log.warn('ngModel doesn\'t exist. There is no way to calculate characters left');
return;
}
elem.wrap('<div class="input-group"></div>');
elem.after('<span class="input-group-addon"></span>');
elem.on('blur keyup keydown change', function() {
scope.$eval(updateCharacterCount);
updateViewValue();
});
$timeout(function(){
scope.maxLength = scope.maxLength || 140;
scope.$eval(updateCharacterCount);
updateViewValue();
});
function updateCharacterCount() {
scope.charactersLeft = !ngModel.$viewValue ? scope.maxLength : scope.maxLength - ngModel.$viewValue.length;
}
function updateViewValue() {
var element = elem.next('span');
element.text(scope.charactersLeft);
element.toggleClass('redText', scope.charactersLeft <= 0 ? true : false);
}
}
}}]);
Try like below. add the second parameter of module.
angular.module('app', [])
.directive('maxCharacterCounter', function() {
return {
restrict: 'A',
require: '?ngModel',
scope: {
maxLength: "="
},
link: function(scope, elem, attrs, ngModel) {
if (!ngModel) return;
ngModel.$render = function() {
console.log('render');
var el = angular.element(attrs.$$element);
el.after('<span ng-bind="charactersLeft" class="input-group-addon">' + scope.charactersLeft + '</span>');
}
elem.on('blur keyup change', function() {
scope.$evalAsync(read);
});
read();
function read() {
scope.charactersLeft = ngModel.$modelValue.length == undefined ? scope.maxLength : scope.maxLength - ngModel.$modelValue.length;
updateViewValue();
}
function updateViewValue() {
angular.element(attrs.$$element).next('span').text(scope.charactersLeft);
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="app">
<input ng-model="inputModel" max-character-counter max-length="10"/>
</body>
In Angular you can literally bind to the length of your input model, like so:
{{inputModel.length}}
Unless I'm missing something, isnt this all you need?

Cannot inject an Angular ngModel in directive in Kendo Grid

I am trying to add an input directive in order to trim all text inputs. So far this is the code of my directive:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "ngModel",
link: function link(scope, element, attrs, ctrl) {
element.on("focusout", function triggerChange(event) {
var input = event.target;
if (input.value && input.type === "text") {
ctrl.$setViewValue(input.value.trim());
ctrl.$render();
}
});
}
};
});
My issue is that the ngModel does not seem to be injected, as I get the error:
Error: [$compile:ctreq] Controller 'ngModel', required by directive 'input', can't be found!
Any idea why this happens, and how to fix it?
Update:
Actually, this is the interaction of Kendo Grid and AngularJS. The input I am testing is generated by Kendo Grid. The code of the column is standard:
{ field: "name", title: "titleName" }
You must have some input element in your HTML which does not have ng-model.
You can change your code to require: "?ngModel", and later check if ctrl is undefined or not, like:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "?ngModel",
link: function link(scope, element, attrs, ctrl) {
if (!ctrl) { return ;}
element.on("focusout", function triggerChange(event) {
var input = event.target;
if (input.value && input.type === "text") {
ctrl.$setViewValue(input.value.trim());
ctrl.$render();
}
});
}
};
You should have provided ng-model in your html when you use this directive because you wrote require: 'ngModel', in the directive. so in your case your directive name is input so it will be something like
<input ng-model="something"> </input>
My answer is not perfect, but it is the best I could find:
app.directive("input", function directive() {
return {
restrict: "E",
priority: 1,
require: "ngModel",
link: function link(scope, element, attrs, ctrl) {
element.on("focusout", function triggerChange(event) {
var input = $(event.target);
input.val(input.val().trim());
input.trigger("change");
});
}
};
});
So basically, we trim the input, and use input.trigger("change") to inform the system that the input has changed.
A warning though, it does not work with our validation system (valdr).

writing own directive with AngularJS

I have a problem with showing error message when the user enters a wrong number. I tried to write my own directive for that but i m getting an error
"Cannot read property 'value' of undefined".
I m totally a newbie, just trying to learn directives. Any idea where did i made a mistake or how should i made it ?
Template :
<div ng-controller="performanceController">
<input type="number" name="performance" class="typo-xl-l input-power"
ng-model="performance" min-performance="10" max-performance="30">
<span id = "errorText"></span>
</div>
Controller :
'use strict';
angular.module('myApp', [
'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/inputView.html'
})
}])
.controller('performanceController', function($scope) {
})
.directive("maxPerformance", [function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
var limit = parseInt(attrs.maxPerformance);
if (this.value.length > limit) {
document.getElementById('errorText').innerText("wrong number");
}
}
}
}])
.directive("minPerformance", [function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
var limit = parseInt(attrs.minPerformance);
if (this.value.length < limit) {
document.getElementById('errorText').innerText("wrong number");
}
}
}
}]);
UPDATE :
.directive("maxPerformance", [function() {
return {
restrict: "A",
require: 'ngModel', //this will make ng-model attribute compulsary on directive element.
link: function(scope, elem, attrs, ngModel) { //injected ngModel here 4th parameter
var limit = parseInt(attrs.maxPerformance);
angular.element(elem).on("keydown", function() {
if (ngModel.$viewValue.length > limit) {
document.getElementById('errorText').innerText("wrong number");
}
});
}
}
}])
.directive("minPerformance", [function() {
return {
restrict: "A",
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) { //injected ngModel here 4th parameter
var limit = parseInt(attrs.minPerformance);
angular.element(elem).on("keydown", function() {
if (ngModel.$viewValue.length < limit) {
document.getElementById('errorText').innerText("wrong number");
}
});
}
}
}]);
Still not working..
While getting the value of ngModel inside your directive you could actually have require: ngModel option inside directive which will give ngModelController value inside link function. And then easily you could fetch value of it using ngModel.$viewValue
.directive("maxPerformance", [function() {
return {
restrict: "A",
require: 'ngModel', //this will make ng-model attribute compulsary on directive element.
link: function(scope, elem, attrs, ngModel) { //injected ngModel here 4th parameter
var limit = parseInt(attrs.maxPerformance);
if (ngModel.$viewValue.length > limit) {
document.getElementById('errorText').innerText = "wrong number";
}
}
}
}])
.directive("minPerformance", [function() {
return {
restrict: "A",
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) { //injected ngModel here 4th parameter
var limit = parseInt(attrs.minPerformance);
if (ngModel.$viewValue.length < limit) {
document.getElementById('errorText').innerText = "wrong number";
}
}
}
}]);
Update
Rather going for creating custom directive, you could handle such a small thing by using ng-min & ng-max attributes on the input element which is of type="number".
<form name="myForm" ng-controller="performanceController">
<input type="number" name="performance" class="typo-xl-l input-power" ng-model="performance" min="10" max="30" />
<span id="errorText" ng-show="myForm.performance.$error.min || myForm.performance.$error.max">
Wrong Number
</span>
</form>
Demo Plunkr

How to incorporate a custom directive into angular's validation framework

I have a directive that is designed to be assigned to a normal text input.
<input type="text" ng-model="fooModel" foo-input size="30"
placeholder="insert value"></input>
I have lots of validation functions for it like testing the precision of the numbers and I use a $parsers to control the value that is submitted.
myApp.directive('fooInput', function () {
return {
restrict: 'A',
require: 'ngModel',
controller: function ($scope, $element, $attrs) {
this.errorMessage = ""
},
link: function (scope, element, attrs, ctrl)
return ctrl.$parsers.push(function (inputValue) {
var originalVal = element.val();
if (!testForOverPrecision(numericVal)) {
//do something here to set the directive as invalid
}
if (originalVal != inputValue) {
ctrl.$setViewValue(res);
ctrl.$render();
}
});
I have 2 questions:
How do I get this to work with the isValid service and do I have to have a controller scope for the error message
Is it correct for me to push the $parser inside a return statement
I am using Angular 1.2x and I created a directive to determine if the text contains the # symbol.
.directive('noAt', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (/#/.test(viewValue)) {
ctrl.$setValidity('noAt', false);
return undefined;
} else {
ctrl.$setValidity('noAt', true);
return viewValue;
}
});
}
};
})

Resources