Untouching methods doesn't work in my directive - angularjs

I've write a directive for textarea Html tag to submit a description to an API. I need to set this tag untouched after I submit the data. I use $scope.$setPristine() and then $scope.apply() but this doesn't work!I also tried ngModelCtrl.$setUntouched() and I got same result.
The Directive tag is as below:
<div class="col-sm-12">
<dp-text-area
dp-tabindex="11"
label="Description"
placeholder="Description"
ng-model="vm.cityModel.description">
</dp-text-area>
</div>
Here is the directive JavaScript code:
function dpTextArea($timeout) {
return {
restrict: 'E',
require: "ngModel",
scope: {
label: '#',
placeholder: '#',
externalNgModel: '=ngModel',
editDisabled: '#',
ngRequired: '=',
dpTabindex: '='
},
link(scope, elm, attr, ngModelCtrl) {
scope.safeApply = function (fn) {
var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if (fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}};
scope.$watch(() => {
return scope.editDisabled;
}, (n, o) => {
// if (n == o)return;
if (scope.editDisabled === 'false') {
scope.editDisabled = false;
}
});
scope.isTextareaTouched = false;
scope.isTextareaInvalid = false;
if (scope.ngRequired)
scope.isTextareaInvalid = true;
scope.blurFunc = () => {
scope.isTextareaTouched = true;
};
//#region watch $error
scope.$watch(
function () {
let rtn = JSON.stringify(ngModelCtrl.$error);
return rtn;
},
function (n, o) {
if (n == o) return;
if (Object.keys(ngModelCtrl.$error).length == 0) {
scope.isTextareaInvalid = false;
} else {
scope.isTextareaInvalid = true;
}
});
//#endregion
},
template: require('./dpTextArea.directive.html')
}}
export default angular.module('directives.dpTextArea', [])
.directive('dpTextArea', dpTextArea)
.name;
And here is the template of the directive:
<div class="form-group" ng-class="{'has-error':isTextareaInvalid && isTextareaTouched}">
<label class="control-label">
{{label || 'description'}}
<sup class="text-danger" ng-show="ngRequired">*</sup>
</label>
<textarea rows="3" type="text"
tabindex="{{dpTabindex}}"
class="form-control"
ng-class="{'edit-disabled':editDisabled}"
ng-disabled="editDisabled"
placeholder="{{placeholder || 'description'}}"
ng-model="externalNgModel"
ng-required="ngRequired"
ng-blur="blurFunc()"
></textarea>
<small class="help-block" ng-show="isTextareaInvalid && isTextareaTouched">this field is requred.</small>
I appreciate any help.

Related

AngularJs: NumberOnly directive

I want to allow numbers only and - sign in textbox.
When i type - sign on IPhone safari it remove value from input like a backspace.
But it is working fine on android and IPad safari.
Here is my directive:
app.directive('numberOnly', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
var pattern = /[^0-9]/g;
var digitsLength = attrs.digitsLength || 0;
var allowMinus = attrs.allowMinus || false;
if (allowMinus) {
pattern = /(?!^-)[^0-9]/g;
} else {
pattern = /[^0-9]/g;
}
scope.$watch(attrs.ngModel, function (newValue, oldValue) {
if (newValue) {
var result = newValue.toString().replace(pattern, '');
ngModel.$setViewValue(result);
if (digitsLength > 0) {
if (result.charAt(0) === '-') {
if (result.substring(1, result.length).length > digitsLength) {
ngModel.$setViewValue(oldValue);
}
}
else {
if (result.substring(0, result.length).length > digitsLength) {
ngModel.$setViewValue(oldValue);
}
}
}
}
ngModel.$render();
}, true);
}
}
});
app.html
<input type="text" ng-model="number" number-only digits-length="7" >
I think it would be a lot more easier if you use
<input type="number" name="input" ng-model="value">
and check validity of the input. Also you can set digits length by setting min and max values of the input.
$scope.example = {
max: 9999999,
min: 1000000
};
with the following min max settings only 7 digits numbers will be valid.
<input type="number" name="input" ng-model="example.value"
min="{{example.min}}" max="{{example.max}}">
You can try below custom directive which I've used. You can modify the regex here to filter the text.
<input type="text" maxlength="3" replacewith="[^0-9-]" ng-model="number">
app.directive('replacewith',replacewith);
replacewith.$inject = ['$timeout'];
function replacewith($timeout) {
return {
require: 'ngModel',
scope: {
regex: '#replacewith'
},
link: function(scope, element, attrs, model) {
model.$parsers.push(function(val) {
if (!val) {
$timeout(function(){
model.$setValidity('parse', true);
}, 0);
return;
}
var regex = new RegExp(scope.regex);
var replaced = val.replace(regex, '');
if (replaced !== val) {
model.$setViewValue(replaced);
model.$render();
}
return replaced;
});
}
};
}

Predefine error message with angular validation custom directive

I am doing angular validation as follows:
<form name="form" ng-submit="vm.create(vm.job)" validation="vm.errors">
<input name="vm.job.position" type="text" ng-model="vm.job.position" validator />
When the form is submitted the directive gets the name of the property, e.g. position, from the ng-model. It then checks if vm.errors has a message for that property. If yes then adds a span with the error message after the input.
However, I would also like to use the same directive in another way:
<form name="form" ng-submit="vm.create(vm.job)" validation="vm.errors">
<input name="vm.job.position" type="text" ng-model="vm.job.position" />
<span class="error" validator="position"></span>
In this case I removed the validator from the input and added the span already allowing me to control where the error will be displayed. In this case I am using validator="position" to define to which model property the error message is associated.
I am not sure how should I add this functionality to my current code ... Any help is appreciated.
The following is all the code I have on my directives:
(function () {
"use strict";
angular.module("app").directive("validation", validation);
function validation() {
var validation = {
controller: ["$scope", controller],
replace: false,
restrict: "A",
scope: {
validation: "="
}
};
return validation;
function controller($scope) {
var vm = this;
$scope.$watch(function () {
return $scope.validation;
}, function () {
vm.errors = $scope.validation;
})
}
}
angular.module("app").directive("validator", validator);
function validator() {
var validator = {
link: link,
replace: false,
require: "^validation",
restrict: "A"
};
return validator;
function link(scope, element, attributes, controller) {
scope.$watch(function () {
return controller.errors;
}, function () {
if (controller.errors) {
var result = controller.errors.filter(function (error) {
if (error.flag == null)
return false;
var position = attributes.name.lastIndexOf(".");
if (position > -1)
return attributes.name.slice(position + 1).toLowerCase() === error.flag.toLowerCase();
else
return attributes.name.toLowerCase() === error.flag.toLowerCase();
});
if (result.length > 0) {
var error = element.siblings("span.error").first();
if (error.length == 0)
element.parent().append("<span class='error'>" + result[0].info + "</span>");
else
error.text(result[0].info);
} else {
element.siblings("span.error").first().remove();
}
}
}, true);
}
}
})();

How to call $setValidity in directive only if required attribute exists

I have a directive where I call this:
ngModel.$setValidity('required', isFinite(scope.inputValue) && scope.inputValue != null);
to set whether the value has been filled by the user or not. I'm pretty sure this used to only make the form invalid if my directive had required or ng-required="trueExpression" on it, but since upgrading to Angular v1.5.7 the form appears to become invalid regardless of whether the required attributes exist or not.
So how can I detect if required or ng-required="trueExpression" are on my directive so I only call this line then?
Here is my entire directive for reference:
(function () {
'use strict';
angular.module('app').directive('bigNumberInput', bigNumberInput);
bigNumberInput.$inject = [];
function bigNumberInput() {
return {
restrict: 'EA',
require: 'ngModel', // get a hold of NgModelController
transclude: true,
scope: {
label: '#'
},
templateUrl: 'app/directives/bigNumberInput.html',
link: function (scope, element, attrs, ngModel) {
//scope.inputValue = null;
ngModel.$formatters.push(function (modelValue) {
var inputValue = null;
var sizeValue = "";
if (modelValue != null) {
if (modelValue / 1000000000 >= 1.0) {
inputValue = modelValue / 1000000000;
sizeValue = "B";
} else if (modelValue / 1000000 >= 1.0) {
inputValue = modelValue / 1000000;
sizeValue = "MM";
} else if (modelValue / 1000 >= 1.0) {
inputValue = modelValue / 1000;
sizeValue = "K";
} else {
inputValue = modelValue;
sizeValue = "";
}
}
return { inputValue: inputValue, sizeValue: sizeValue };
});
ngModel.$render = function () {
scope.inputValue = ngModel.$viewValue.inputValue;
scope.sizeValue = ngModel.$viewValue.sizeValue;
};
scope.$watch('inputValue + sizeValue', function () {
ngModel.$setViewValue({
inputValue: scope.inputValue,
sizeValue: scope.sizeValue
});
ngModel.$setValidity('required', isFinite(scope.inputValue) && scope.inputValue != null);
});
ngModel.$parsers.push(function (viewValue) {
var inputValue = viewValue.inputValue;
if (inputValue != null) {
if (viewValue.sizeValue === 'K') {
inputValue = inputValue * 1000;
} else if (viewValue.sizeValue === "MM") {
inputValue = inputValue * 1000000;
} else if (viewValue.sizeValue === "B") {
inputValue = inputValue * 1000000000;
}
}
return inputValue;
});
}
};
}
}());
And here is the HTML template for it:
<div layout="row">
<md-input-container flex>
<label>{{label}}</label>
<input type="number" step="any" ng-model="inputValue"/>
<div ng-transclude></div>
</md-input-container>
<md-input-container>
<label>Type</label>
<md-select ng-model="sizeValue" md-on-open="valuationTypeOpen = true" md-on-close="valuationTypeOpen = false">
<md-option value="">{{valuationTypeOpen ? 'Verbatim (=)' : '='}}</md-option>
<md-option value="K">{{valuationTypeOpen ? 'Thousands (K)' : 'K'}}</md-option>
<md-option value="MM">{{valuationTypeOpen ? 'Millions (MM)' : 'MM'}}</md-option>
<md-option value="B">{{valuationTypeOpen ? 'Billions (B)' : 'B'}}</md-option>
</md-select>
</md-input-container>
</div>
I think I found the answer on my own. I believe it's just a matter checking if the "required" attribute returns true like this:
ngModel.$setValidity('required', !attrs.required || (isFinite(scope.inputValue) && scope.inputValue != null));
It appears to be working for hard-coded required attributes as well as using ng-required attributes.

Angular modal dialog set inside list items

I'm trying to set a list of two items that open separate modal dialogs in a node.js app. I'm using Jade.
Here's the Jade:
button.login-button(type='button' ng-app='ng-modal') Login
ul
li(open-dialog='modal-to-open') Login
// JUST WORKING ON SIGN UP FOR NOW
li Sign Up
modal-dialog(show='dialogShown' dialog-title='My Dialog' height='100px' width='100px')
p Working
div.loginForm
form(name='loginForm' method='post' action='#' enctype='text/plain')
label(for='user') Username or Email
input(type='text' name='username' id='username' size="39" placeholder='Username or Email' required)
label(for='password') Password
input(type='password' name='password' id='password' size='39' placeholder='Password' required)
I'm using Adam Brecht's modal dialog plugin. I have the js file and the css files attached.
I changed the declaration of the module in the js file to this:
app = angular.module("myApp", ["ngModal"])
I have the list set as a dropdown in my CSS. I wanted the form to display in a modal dialog when the link in the list is clicked, but at the moment the form displays below dropdown box.
What am I missing?
EDIT: This is the js file:
(function() {
var app;
app = angular.module("myApp", ["ngModal"])
app.provider("ngModalDefaults", function() {
return {
options: {
closeButtonHtml: "<span class='ng-modal-close-x'>X</span>"
},
$get: function() {
return this.options;
},
set: function(keyOrHash, value) {
var k, v, _results;
if (typeof keyOrHash === 'object') {
_results = [];
for (k in keyOrHash) {
v = keyOrHash[k];
_results.push(this.options[k] = v);
}
return _results;
} else {
return this.options[keyOrHash] = value;
}
}
};
});
app.directive('modalDialog', [
'ngModalDefaults', '$sce', function(ngModalDefaults, $sce) {
return {
restrict: 'E',
scope: {
show: '=',
dialogTitle: '#',
onClose: '&?'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
var setupCloseButton, setupStyle;
setupCloseButton = function() {
return scope.closeButtonHtml = $sce.trustAsHtml(ngModalDefaults.closeButtonHtml);
};
setupStyle = function() {
scope.dialogStyle = {};
if (attrs.width) {
scope.dialogStyle['width'] = attrs.width;
}
if (attrs.height) {
return scope.dialogStyle['height'] = attrs.height;
}
};
scope.hideModal = function() {
return scope.show = false;
};
scope.$watch('show', function(newVal, oldVal) {
if (newVal && !oldVal) {
document.getElementsByTagName("body")[0].style.overflow = "hidden";
} else {
document.getElementsByTagName("body")[0].style.overflow = "";
}
if ((!newVal && oldVal) && (scope.onClose != null)) {
return scope.onClose();
}
});
setupCloseButton();
return setupStyle();
},
template: "<div class='ng-modal' ng-show='show'>\n <div class='ng-modal-overlay' ng-click='hideModal()'></div>\n <div class='ng-modal-dialog' ng-style='dialogStyle'>\n <span class='ng-modal-title' ng-show='dialogTitle && dialogTitle.length' ng-bind='dialogTitle'></span>\n <div class='ng-modal-close' ng-click='hideModal()'>\n <div ng-bind-html='closeButtonHtml'></div>\n </div>\n <div class='ng-modal-dialog-content' ng-transclude></div>\n </div>\n</div>"
};
}
]);
}).call(this);
I just realized I haven't changed the template.

Handling Parameter of Directive in AngularJS

i am passing the parameter(id) to the directive as follows
<input type="text" ng-model="entry.sale_qty" required style="width: 200px" on-keyupfn="handleKeypress(id)" >
And i have the definition of the on-keyupfn directive is as follows
app.directive('onKeyupFn', function() {
return function(scope, elm, attrs) {
var keyupFn = scope.$eval(attrs.onKeyupFn);
elm.bind('keyup', function(evt) {
scope.$apply(function() {
keyupFn.call(scope, evt.which);
});
});
};
});
And the mentioned function(handleKeypress) for directive is as this
$scope.handleKeypress = function(key) {
$scope.keylog.push(key);
if ($scope.keylog[len - 1] == 17 && $scope.keylog[len] == 13)
$scope.addUser();
len = $scope.keylog.length;
}
Now can anybody help me with the accessing of passed parameter(id) from html in the function "$scope.handleKeypress = function(key) {}"???
You might want to use ng-keyup here:
html:
<input type="text" ng-model="entry.sale_qty" required style="width: 200px" ng-keyup="handleKeypress($event)" >
js:
app.directive('onKeyupFn', function() {
return function(scope, elm, attrs) {
scope.handleKeypress = function (e) {
$scope.keylog.push(e.which);
if ($scope.keylog[len - 1] == 17 && $scope.keylog[len] == 13) {
$scope.addUser();
}
len = $scope.keylog.length;
};
});

Resources