writing own directive with AngularJS - 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

Related

How to execute two different directives with $parsers and $validators on a same element?

Issue is that only the first directive is working and other one is not. Provided below are the two directives. Both works fine when working alone.
.directive("restrictInvalidInput", function($rootScope, utils) {
return {
restrict: "A",
require: "ngModel",
priority: 1,
link: function(scope, elem, attr, ctrl) {
var remove_invalid_chars = function() {
//blah blah
};
ctrl.$parsers.push(remove_invalid_chars);
}
}
})
.directive("validateUsername", function($rootScope, utils) {
return {
restrict: "A",
require: "ngModel",
priority: 2,
link: function(scope, elem, attr, ctrl) {
var validate_username = function(modelValue, viewValue) {
//blah blah
};
ctrl.$validators.valid_username = validate_username;
}
}
});
Seems like the problem is where you put blah blah. Are you sure, that parser returns a value? Because (from docs):
Returning undefined from a parser means a parse error occurred. In that case, no $validators will run
WORKING PLUNKER
angular.module('app', [])
.controller('Controller', ['$scope', function($scope) {
$scope.name = 'Some name';
}])
.directive("restrictInvalidInput", function() {
return {
restrict: "A",
require: "ngModel",
priority: 1,
link: function(scope, elem, attr, ctrl) {
var remove_invalid_chars = function(viewValue) {
console.log('Look, I\'m parsing');
return viewValue;
};
ctrl.$parsers.push(remove_invalid_chars);
}
}
})
.directive("validateUsername", function() {
return {
restrict: "A",
require: "ngModel",
priority: 2,
link: function(scope, elem, attr, ctrl) {
var validate_username = function(modelValue, viewValue) {
console.log('Look, I\'m validating');
return true;
};
ctrl.$validators.valid_username = validate_username;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="Controller">
<input type="text"
ng-model="name"
restrict-invalid-input
validate-username
/>
{{name}}
</div>
</div>

Capture value from Input field

I'm trying to get the value from one of the input fields in my form, but my code isn't working:
JavaScript:
angular
.module('myDirectives')
.directive('pwMatch', matchPassword);
function matchPassword() {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var modelIt = attrs.pwMatch;
var inputValue = attrs.modelIt;
console.log(inputValue);
}
};
};
HTML:
<input name="telephone" type="number" value="223344455">
<div pw-match="form.telephone"></div>
If you are trying to get the value of an input, use ng-model.
<input ng-model="form.telephone" type="number" value="223344455">
<div pw-match input-name="form.telephone"></div>
And if you want to get that value in a directive using a name on an attribute, use the$watch method on the scope.
JS
angular.module('myDirectives',[])
.directive('pwMatch', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(attrs.inputName, function(value) (
var inputValue = value;
console.log(inputValue);
};
}
}
});
.module('myDirectives')
needs to be
.module('myDirectives', [])
Even though you have no dependencies, you have to have the empty array.
Also, it's a really bad idea™ to use a variable function as a directive or something, it's just going to confuse you.
This works, too, and might make your application a bit easier to maintain:
angular.module('myDirectives', [])
.directive('pwMatch', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var modelIt = attrs.pwMatch;
var inputValue = attrs.modelIt;
console.log(inputValue);
}
}
});

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).

Passing Date/moment object via attribute in AngularJS

I have a directive and I'm trying to pass Date/moment object via attribute. I'm passing it like this: (I know, that I can create isolated-scope and bind it, it is not the case)
<form name="form">
<input name="field" ng-model="fieldModel" form-field-directive field-date="{{fieldDateModel}}" />
</form>
Without curly brackets the result is obvious, but with I'm getting such quoted string "2015-07-03T10:35:13.691Z".
Is there anyway to work with it?
UPDATE:
angular.module('app', [])
.controller('AppCtrl', function($scope) {
$scope.fieldDateModel = moment(); // new Date()
});
angular.module('app')
.directive('formFieldDirective', function() {
return {
restrict: 'A',
require: '^ngModel',
link: function(scope, iElement, iAttrs, ngModelCtrl) {
ngModelCtrl.$validators.fieldDate = function() {
if (angular.isUndefined(iAttrs.fieldDate)) {
return true;
}
console.log(iAttrs.fieldDate);
};
}
};
});
You can actually pull the value from the parent scope using $parse which is more reliable.
angular.module('app')
.directive('formFieldDirective', function($parse) {
return {
restrict: 'A',
require: '^ngModel',
link: function(scope, iElement, iAttrs, ngModelCtrl) {
ngModelCtrl.$validators.fieldDate = function() {
if (angular.isUndefined(iAttrs.fieldDate)) {
return true;
}
console.log(($parse(iAttrs.fieldDate)(scope)).format());
};
}
};
});
http://jsbin.com/qoheraloge/1/edit?js,console,output

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