I hope u can help me.
I have a directive:
.directive('checkField', ['$http', function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, ele, attrs, c) {
scope.$watch(function() {
if (attrs.ngModel === 'data.gender_value' && ele.val() !== '') {
//valid
} else {
//error
}
if (attrs.ngModel === 'data.cardholder_value' && ele.val() !== '') {
//valid
} else {
//error
}
});
},
template: ''
};
}])
And i have multiple inputs in my html:
<input ng-model="data.cardholder_value" type="text" size="50" data-check-field />
<input ng-model="data.gender_value" type="text" ng-required="true" data-check-field />
The problem is that watch trigger only "see" the first input, no more.
I'm trying to use de same directive to multiple inputs, but doesn't work. If i do an alert, to check the attribute name of field, always display "data.cardholder_value", never other name field.
Thank u in advance.
Edit 1:
This is my html calling (ng-include):
<form method="post" id="formQuestion" name="formQuestion" ng-submit="sendForm()" novalidate ng-controller="questionForm">
{{data | json}}
<div class="slide-animate" ng-include="'/templates/default/partials/_fields/1_card_type.html'"></div>
<div class="slide-animate" ng-include="'/templates/default/partials/_fields/2_gender.html'"></div>
My app controller:
angular.module('app.controllers')
.directive('checkField', ['$http', function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, ele, attrs, ctrl) {
scope.$watch(attrs.ngModel, function(val) {
console.log(attrs.ngModel, attrs.name, val)
});
},
template: ''
};
}])
.controller('questionForm', ['$scope', '$http', 'fieldApiService', function ($scope, $http, fieldApiService) {
...
All you just need it to watch the value of ng-model directive attribute, right now you provided the watcher function as your validation function which mean when it sees function as first argument for the watch it will just only look for the return value from that function to determine if watch listener needs to run or not during every digest cycle.
scope.$watch(attrs.ngModel, function(val) {
if (!val) {
//valid
} else {
//error
}
});
Also remember if you want to catch the user entered values you can always use the existing $viewChangeListener property on ngmodel, it will avoid reuse of existing internal watcher and no need to explicitly create one.
c.$viewChangeListeners.push(function(){
console.log('viewChange', ctrl.$viewValue)
});
Demo
angular.module('app', []).directive('checkField', ['$http',
function($http) {
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, ele, attrs, ctrl) {
scope.$watch(attrs.ngModel, function(val) {
console.log(attrs.ngModel, attrs.name, val)
});
ctrl.$viewChangeListeners.push(function(){
console.log('viewChange', ctrl.$viewValue)
});
},
};
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<input ng-model="data.cardholder_value" name="cardholder" type="text" size="50" data-check-field />
<input ng-model="data.gender_value" name="gender" type="text" ng-required="true" data-check-field />
</div>
Related
This is what I wrote but angular keeps complaining about the $asyncValidators telling me is {}. I do not want to work with $http nor with $q. This is the faulty code I wrote:
.directive('checkEmail', ['toolBox', function(toolBox){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel){
ngModel.$asyncValidators.emailExists = function(userEmail){
var promise = toolBox.checkEmailExist(userEmail);
return promise.get(function success(res){
return true;
}, function error(res){
return false;
});
};
}
};
}])
Has any of you worked validation with $resource? Where I get it wrong? toolBox.checkEmailExist(userEmail) comes from a service that looks like this
angular.module('toolBoxService', ['ngResource'])
.factory('toolBox', ['$resource','$log', function($resource, $log){
var dataObj = {};
dataObj.checkEmailExist = function(email){
return $resource('api/users/email', {email: email});
};
return dataObj;
}]);
and the form element looks like
<!-- Email field -->
<div class="input-group">
<span class="input-group-addon" id="userEmail">*</span>
<label class="control-label" for="userEmailField"></label>
<input class="form-control"
name="userEmail"
id="userEmailField"
required=""
placeholder="Email"
type="email"
ng-model="data.email"
ng-model-options="{ debounce: { default : 300, blur: 0 }}"
check-email>
</input>
</div>
<!-- Validation of the email -->
<div class="help-block form-error-messages"
ng-messages="registerForm.userEmail.$error"
ng-show="registerForm.userEmail.$touched"
role="alert"
ng-messages-multiple>
<div ng-messages-include="FACETS/errors/errorMessages.html"></div>
</div>
The only solution I've came up with is this
// checking for email in the database
.directive('checkEmail', ['toolBox', function(toolBox) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$validators.checkEmail = function(modelValue, viewValue){
var currentVal = modelValue || viewValue;
toolBox.checkEmailExist(currentVal)
.get(function success(resp){
if(resp.email == currentVal) {
ngModel.$setValidity('checkEmail', false);
} else {
ngModel.$setValidity('checkEmail', true);
}
});
};
} // end link
}; // end return
}])
$resource is not working with the $asyncValidators unfortunately. Took me 2 days to realise this. I hope there was an answer, a more expert voice on the matter.
It might be that you have to return the $promise attached to the resource instance object, rather than the object itself.
If your api returns an OK 200 response when the username does exist and an error status (e.g. 404 Not Found) when it does not, then the following approach works:
.directive('usernameExists', ['$resource', function ($resource) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.usernameExists = function (userName) {
return $resource('/api/Account/:userName')
.get({ userName: userName }).$promise;
};
}
}
}])
I can solve it, it was matter of playing a bit more with promises
.directive('validateNbr',['$q','PreavisoService', function($q, PreavisoService) {
return {
require: 'ngModel',
restrict: '',
link: function(scope, elm, attrs, ngModel) {
ngModel.$asyncValidators.validateNbr = function(modelValue){
var def = $q.defer();
PreavisoService.checkUnitDigit({nbr:ngModel.$viewValue}).$promise.then(
function(result){
if(result.isValid === false){
def.reject();
}else{
def.resolve();
}
});
return def.promise;
};
}
};
}]);
hope it helps
I have the current section of html that is used to check a users password,
<div class="form-group">
<label for="auditName" class="col-lg-4 control-label">Current Password </label>
<div class="col-lg-8">
<input type="password" placeholder="Current Password"
name="currentPassword"
class="form-control"
ng-model="currentPassword"
required=""
password-new
ng-model-options="{ updateOn: 'blur' }">
</div>
<div class="col-lg-offset-4" ng-if="form.$pending.oldPassword">checking....</div>
<div class="col-lg-offset-4" ng-if="form.$error.oldPassword">Please create a NEW password</div>
</div>
{{currentPassword}}
My issue is that the currentPassword is not being updated, so nothing is being displayed on the screen. If I remove the model-options AND I remove the reference to the new-password directive it will display as you type - so both of these are for some reason stopping the model from updating the value.
The directive new-password looks like this, and is still in a basic format I found elsewhere until I get this working properly,
app.directive('passwordNew', function ($timeout, $q) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, elm, attr, model) {
model.$asyncValidators.oldPassword = function () {
//here you should access the backend, to check if username exists
//and return a promise
var defer = $q.defer();
$timeout(function () {
model.$setValidity('oldPassword', true);
defer.resolve;
}, 1000);
return defer.promise;
};
}
}
});
Any ideas?
The {{currentPassword}} in your HTML is outside the scope of the directive. You need to link the two scopes. Checkout "Isolating the Scope of a Directive" in https://docs.angularjs.org/guide/directive.
Put something like this on the directive
scope: {
currentPassword: '='
},
EXAMPLE
This is how I solve a similar problem
app.directive('availableEmail', [
'dataSvc', (data:otolane.direct.IDataService) => {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
ctrl.$setValidity('availableEmail', true);
//only check the db if value is an email
if (viewValue.length > 3 && !ctrl.$error.email) {
data.account.checkEmail(viewValue)
.then(() => {
// data method resolves if email is available
ctrl.$setValidity('availableEmail', true);
})
.catch(() => {
//returns error if email is in use
ctrl.$setValidity('availableEmail', false);
});
}
return viewValue;
});
}
};
}
]);
What would be the best way to show data returned by $asyncValidator when form field is valid?
I can show an error besides the input field with ngMessage but would also like to show the resulting response from Restangular when validation passes.
'use strict';
angular.module('app')
.directive('productionOrder', function(Restangular) {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attributes, ngModel) {
ngModel.$asyncValidators.exists = function(modelValue) {
return Restangular.one('production_orders', modelValue).get();
}
}
};
});
Html
<form name="workOrderForm">
<input type="text" name="productionOrderNo" ng-model="work_order.work_order.production_order_no" required production-order ng-model-options="{ debounce: 1000 }">
<div ng-messages="workOrderForm.productionOrderNo.$error">
<div ng-message="required">Production order no is mandatory</div>
<div ng-message="exists">Production order not found</div>
</div>
</form>
I'm sure this breaks at least a couple of best practises but this is how I got it working for now.
angular.module('oeeMaster')
.directive('productionOrder', function($q, Restangular) {
return {
restrict: "A",
require: "ngModel",
link: function($scope, element, attributes, ngModel) {
ngModel.$asyncValidators.exists = function(modelValue) {
var deferred = $q.defer();
Restangular.one('production_orders', modelValue).get().then(
function(response) {
ngModel.part_no = response.part_no;
deferred.resolve(response);
}, function() {
ngModel.part_no = null;
deferred.reject();
}
);
return deferred.promise;
}
}
};
});
HTML
<div ng-if="workOrderForm.productionOrderNo.part_no">Part number: {{ workOrderForm.productionOrderNo.part_no }}</div>
My two-way data binding using ng-model is not working.
AngularJS Docs
Relevant Question
Parent directive template:
<div class="form-group">
<label>Company Phone</label>
<input ng-model="formData.company_phone" type="phonenumber" class="form-control" placeholder="Company Phone">
</div>
And the child directive:
.directive('input', [function(){
return: {
restrict: 'E',
require: '?ngModel',//right now this is binding to this directive scope, not a parent one
link: function($scope, element, attr, ngModel){
if (attr.type !== 'phonenumber') {
return;
}
//some code to validate a phone number
$scope.$apply(function () {
//bind updated number, but I need this to reflect in the parent scope
$scope[attr.ngModel] = formattedNumber;
}
I used $parse to solve this issue:
.directive('input', ['$parse', function($parse){
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attr, ngModel){
var getter = $parse(attr.ngModel);
var setter = getter.assign;
if (attr.type !== 'phonenumber') {
return;
}
//code that validated a phone number
scope.$apply(function () {
setter(scope, formattedNumber);
});
}
}
}]);
I have a directive for an upload file that communicates with the server and returns a scope.dbInsertId. I want to watch this and then update the ng-model with this value. I am unable to get it to work using $apply. Here is my code:
scope.$watch('dbInsertId', function(newValue, oldValue) {
if (newValue)
scope.$apply(function() {
scope.ngModel = scope.dbInsertId;
});
console.log("I see a data change!");
return ngModel.$modelValue;
}, true);
Is my code wrong?
My answer for your question
<div ng-app="app">
<div ng-controller="MyController">
<form name="someForm">
<div this-directive ng-model="theModel"></div>
<div>theModel='{{ theModel }}'</div>
</form>
</div>
</div>
var app = angular.module('app', []);
app.controller('MyController', function($scope,$rootScope,$log) {
$scope.theModel = '';
$scope.$watch('theModel',function(newVal,oldVal){
$log.info('in *MyController* model value changed',newVal,oldVal);
});
});
app.directive('thisDirective', function($compile, $timeout, $log) {
return {
scope: {
ngModel: '='
},
require: 'ngModel',
template: '<input type="text" ng-model="ngModel" /><div child-directive ng-model="ngModel"></div>',
link: function(scope, element, attrs, ngModel) {
}, // end link
} // end return
});
app.directive('childDirective', function($compile, $timeout, $log) {
return {
scope: {
ngModel: '='
},
template: '<input type="text" ng-model="ngModel" />',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
},
} // end return
});
jsfiddle.net/dXL4w/3
You need scope:
scope.$watch('dbInsertId', function(newValue, oldValue, scope) { // here
if (newValue)
scope.$apply(function() {
scope.ngModel = scope.dbInsertId;
});
console.log("I see a data change!");
return ngModel.$modelValue;
}, true);