So I have two forms inside the same controller.
<form name="myForm" id="myForm" class="form-horizontal" ng-submit="saveMyForm(myForm)" >
<input type="text" id="name" ng-model="name" />
//...etc
</form>
and another form
<form name="passForm" id="passForm" ng-submit="savePassForm(passForm)" >
<input type="password" id="oldpassword" name="oldpassword" ng-model="oldpassword" >
<input type="password" id="pw1" name="pw1" ng-model="pw1" >
<input type="password" id="pw2" name="pw2" ng-model="pw2" pw-check="pw1" >
</form>
<div class="msg-block" ng-show="passForm.$error">
<span class="msg-error loginError" ng-show="passForm.pw2.$error.pwmatch">
Passwords don't match.
</span>
</div>
To check if passwords match I have this directive
app.directive('pwCheck', [function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var firstPassword = '#' + attrs.pwCheck;
elem.add(firstPassword).on('keyup', function () {
scope.$apply(function () {
var v = elem.val()===$(firstPassword).val();
ctrl.$setValidity('pwmatch', v);
});
});
}
}
}]);
So my first form works fine.
In my second form, the one for passwords, I cannot grab the passwords from the fields to send them to the server. I do
var passData = {
"oldpassword" : $scope.oldpassword,
"newpassword" : $scope.pw2
}
$scope.changepassword = function(form){
if(form.$valid) {
var promisePass = passwordFactory.changePass(passData);
promisePass.success(function (data, status) {
//handle
When I check my console, there are no data, passData is empty.
What am I missing here? Is it the fact that there are two forms inside the same controller? Does the directive messes things up?
Please help me fix this.
Thanks
I see a couple issues. First, the function name you have specified here:
<form name="passForm" id="passForm" ng-submit="savePassForm(passForm)" >
Does not match the name in your controller:
$scope.changepassword = function(form){
Second, you create your passData object outside of the submit function. This means it's going to have the values of the scope variables when the controller first loaded, likely undefined. Move the creation of passData inside your function and then it will be created with the current values of the scope variables.
Related
I have an array of vertex values that I loop over and display to the user.
The values are stored as a space delimited string, like this:
vrtx = ["10.3 20.3 10.5", "13.2 29.2 3.0", "16.3 2.3 20.2", ...]
I want to provide a friendly interface to the user to edit the values.
This requires me to split the strings into three separate numbers and put them into three individual inputs.
I want to create a directive, as I am not able to store the values as separate values, but after the editing is done, I want to join the values back and update the scope to store the new values as strings.
This is the directive I have so far:
myApp.directive('fieldValue', function () {
return {
scope: {
field:'='
},
link: function (scope, element, attrs) {
var fields = scope.field.split(' ');
element.html("<input type='number' value='"+ fields[0] +"' class='vertex-input'/><input type='number' value='"+ fields[1] +"' class='vertex-input'/><input type='number' value='"+ fields[2] +"' class='vertex-input'/>");
}
}
});
This splits the value and shows the three inputs with the individual values, but my question is how do I join these values and save them back to the scope? Any suggestions would be greatly appreciated! TIA
You could use formatters/parsers to achieve what you want, but you may have to adjust your input to use ngModel, and I'm not quite sure how it would work with 3 separate input fields:
myApp.directive('fieldValue', function () {
return {
scope: {
field:'='
},
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var fields = scope.field.split(' ');
element.html("<input type='number' value='"+ fields[0] +"' class='vertex-input'/><input type='number' value='"+ fields[1] +"' class='vertex-input'/><input type='number' value='"+ fields[2] +"' class='vertex-input'/>");
ngModel.$formatters.push(function(value){
//return the value as you want it displayed
});
ngModel.$parsers.push(function(value){
//return the value as you want it stored
});
}
}
});
This way gives you a lot more flexibility in my opinion to use any strategy you want. Read more about it here: https://alexperry.io/angularjs/2014/12/10/parsers-and-formatters-angular.html
And, more official docs here: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
I think that your question is the continuation of this one, so I would like to suggest you a solution without a directive, based on the answer of your precedent question.
When the value of an input is modified, call a function that merged all your inputs:
<input type="text" ng-model="arr[0]" ng-change="update()" />
<input type="text" ng-model="arr[1]" ng-change="update()" />
<input type="text" ng-model="arr[2]" ng-change="update()" />
$scope.update = function() {
$scope.myString = $scope.arr[0] + ' ' + $scope.arr[1] + ' ' + $scope.arr[2];
}
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', ['$scope', function($scope) {
$scope.myString = 'My awesome text';
$scope.arr = $scope.myString.split(/[ ]+/);
$scope.update = function() {
$scope.myString = $scope.arr[0] + ' ' + $scope.arr[1] + ' ' + $scope.arr[2];
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<input type="text" ng-model="arr[0]" ng-change="update()" />
<input type="text" ng-model="arr[1]" ng-change="update()" />
<input type="text" ng-model="arr[2]" ng-change="update()" />
<br/>Result: {{myString}}
</div>
</div>
Try it on JSFiddle.
I have an AngularJS form.
I implemented typeahead script so when someone start typing into input field values are displayed above field. If defined values are banana, apple and beer and user starts to type "ba" list appears with value banana.
If user clicks on banana value from typeahead it's assigned into input field via javascript.
I want to make input field and entire form invalid if value selected isn't from typeahead list.
I have html:
<form id="form" name="form" ng-submit="formsubmit()" novalidate>
<input ng-model="food" ng-required="true" name="food" autocomplete="off" type="text" id="food" placeholder="Start typing" />
<p class="error validationerror" ng-show="form.food.$invalid && form.food.$touched">Required</p>
<div class="error validationerror" ng-messages="form.food.$error"><p ng-message="food">You must specify item from list</p></div>
<button type="submit" id="submit" class="btn large black" ng-disabled="form.$invalid">Submit</button>
</form>
I have controller:
var app = angular.module('app', ['ngRoute', 'ngMaterial', 'ngMessages', 'angular-loading-bar']);
app.controller('Food', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
$scope.formsubmit = function () {
console.log('submited');
};
$http.get('food.php')
.success(function (data) {
$scope.foods = data;
//typeahead script...
}]);
By now everything is working ok. Now we have to check if value of input field is defined in food list. No matter if it's pasted, typed or selected from typeahead list.
I defined directive:
app.directive('food', function (){
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
function updateFoodInfo(scope, elem){
var food1 = $('#food').val();
var data = scope.foods;
if (data.indexOf(food1) < 0) {
ngModel.$parsers.unshift(function (value) {
ngModel.$setValidity('food', data.indexOf(value) === -1);
return value;
});
}
}
setInterval(function(){updateFoodInfo(scope,elem);}, 1000);
}
};
});
I have no console errors and whatever I enter into input field, form is valid. Only if I remove everything form is invalid and input field is set as invalid because it is empty. But I simply can't set custom validation.
Here is working plunker too.
plunker
Here is a working plunker
http://plnkr.co/edit/IlJJLduidBwUfb2EZWvV?p=preview
angular 1.6.5 http://plnkr.co/edit/fIRBuijd0xWDqZvHB24Z?p=preview
Hope it helps.
You only need to add the validation to your input field <input food></input> and you were checking for input that wasn't matching anything in the list. Just change to ngModel.$setValidity('food', data.indexOf(value) !== -1);.
Here is the plunker: http://plnkr.co/edit/FnuNFbGiCNOXwkkuDPJ7?p=preview
I will start out stating that I have searched google and SO and I have not found an answer for this specific situation. Yes, there are other posts that sound the same but are more based on a "MoreThan / LessThan" mentality. This does not following that mentality at all so please do not mark this as a duplicate referring to them.
Check out the Plunker Example
I am attempting to make sure the user does not enter an address that already exists else where on the page. To do this I need to validate all the address fields since different locations may have the same street address. I need the validator to set all the related fields to valid if any are invalid once the address has been fixed to not be a duplicate. Currently it only sets the last field modified to valid and leaves the rest as invalid.
Plunker example demonstrates what is happening. I have tried many different approaches such as iterating through all fields and setting them to prestine and untouched and then setting them to dirty and touched to trigger validations again but I am having no luck getting this working.
Validator
angular.directive('ruleFunc', ['$parse', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, $element, $attrs, $ngModel) {
var validatorName = $attrs.ruleName;
var validatorFunc = $attrs.ruleFunc;
if (!angular.isDefined(validatorName)) {
throw Error("rule-name attribute must be defined.");
}
if (!angular.isDefined(validatorFunc)) {
throw Error("rule-func attribute must be defined.");
}
// in real code I passing a function call with the model as the param
// this example demonstrated the issue I am having though
var expressionHandler = $parse(validatorFunc);
// had to use viewChangeListener because changes to the model
// were not showing up correctly in the actual implementation
$ngModel.$viewChangeListeners.push(function() {
var valid = expressionHandler($scope);
$ngModel.$setValidity(validatorName, valid);
});
});
Form
<form name="AddressForm" novalidate>
<h1>Address Form</h1>
<div style="margin:20px">
<input id="Street" type="text" name="Street" placeholder="Street" data-ng-model="ctrl.address.street" rule-func="ctrl.checkVal()" rule-name="profileHasContact"> {{!AddressForm.Street.$error.profileHasContact}}
<br />
<input id="City" type="text" name="City" placeholder="City" data-ng-model="ctrl.address.city" rule-func="ctrl.checkVal()" rule-name="profileHasContact"> {{!AddressForm.City.$error.profileHasContact}}
<br />
<input id="State" type="text" name="State" placeholder="State" data-ng-model="ctrl.address.state" rule-func="ctrl.checkVal()" rule-name="profileHasContact"> {{!AddressForm.State.$error.profileHasContact}}
<br />
<input id="Zip" type="text" name="Zip" placeholder="Zip" data-ng-model="ctrl.address.zip" rule-func="ctrl.checkVal()" rule-name="profileHasContact"> {{!AddressForm.Zip.$error.profileHasContact}}
<br />
<div ng-if="(AddressForm.Street.$error.profileHasContact
|| AddressForm.City.$error.profileHasContact
|| AddressForm.State.$error.profileHasContact
|| AddressForm.Zip.$error.profileHasContact)">Address already exists in Main Contacts</div>
<button type="submit">Submit</button>
</div>
I did find a post that was close enough that I could hack together a solution.
Form validation - Required one of many in a group
Here is the updated plunker
Updated Validator
directive('ruleFunc', ['$parse', function($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, $element, $attrs, $ngModel) {
var validatorName = $attrs.ruleName;
var validatorFunc = $attrs.ruleFunc;
var groupName = $attrs.ruleGroup;
if (!angular.isDefined(validatorName)) {
throw Error("rule-name attribute must be defined.");
}
if (!angular.isDefined(validatorFunc)) {
throw Error("rule-func attribute must be defined.");
}
if(angular.isDefined(groupName)){
// setup place to store groups if needed
if (!$scope.__ruleValidationGroups) {
$scope.__ruleValidationGroups = {};
}
var groups = $scope.__ruleValidationGroups;
// setip group if needed
if(!groups[groupName]){
groups[groupName] = {};
}
var group = groups[groupName];
// assign model to group
group[$attrs.ngModel] = {
model: $ngModel
}
}
function updateValidity(valid){
if(angular.isDefined(groupName)){
// set all models in group to same validity
for(var prop in group){
if(group.hasOwnProperty(prop)){
group[prop].model.$setValidity(validatorName, valid);
}
}
}
else
{
// set this model validity if not in group
$ngModel.$setValidity(validatorName, valid);
}
}
var expressionHandler = $parse(validatorFunc);
$ngModel.$viewChangeListeners.push(function() {
var valid = expressionHandler($scope);
updateValidity(valid);
});
}
};
}]);
I am following a Year of Moo tutorial on async form validations and ngMessages (I'm using 1.3.0-beta.14 so I can't use the actual $async validator).
My validation is working, but the scope in the view is non-existent! On form submission, there is no username value and adding the appropriate {{username}} binding elsewhere in the view never returns a value. However, the console Log at the end of my directive does return the correct value, it just never transfers to the view?
Here is the directive, basically lifted from the article (the console.log before the final return does log the correct value from the view):
.directive('recordAvailabilityValidator', function($http) {
return {
require : 'ngModel',
link : function(scope, element, attrs, ngModel) {
var apiUrl = attrs.recordAvailabilityValidator;
function setAsLoading(bool) {
ngModel.$setValidity('recordLoading', !bool);
}
function setAsAvailable(bool) {
ngModel.$setValidity('recordAvailable', bool);
}
ngModel.$parsers.push(function(value) {
if(!value || value.length == 0) return;
setAsLoading(true);
setAsAvailable(false);
$http.get(apiUrl, { params: {attr : value }} )
.success(function(response) {
setAsLoading(false);
setAsAvailable(true);
})
.error(function() {
setAsLoading(false);
setAsAvailable(false);
});
console.log(value)
return value;
})
}
}
});
Here is the relevant part of the html template:
<p>
<label>Username</label>
<input type="text" ng-model="signup.username" class='form-control' name='username'
require minlength='3' record-availability-validator="/api/v1/validations/username"
ng-model-options="{ debounce : { 'default' : 500, blur : 0 } }">
</p>
<div ng-messages="signupForm.username.$error">
<div ng-message="required">You did not enter a username</div>
<div ng-message="minlength">Username must be at least 4 characters</div>
<div ng-message="recordLoading">Checking database...</div>
<div ng-message="recordAvailable">The username is already in use...</div>
</div>
{{signup.username}}
The debug {{signup.username}} never shows a value. If I change it to just {{signup}} the other values show fine. Also, if I add this directive to another input, like email the same strange behavior is there. I googled around and tried added scope: true to my directive, but nothing happened.
Trying to display a columnvalue from a gridcollection based on another value in that same row.
The user can select/change values in a modal which contains a grid with values. When the modal closes the values are passed back. At that moment I would like to set a value for 'Also known as':
html:
Also known as: <input type="text" `ng-model="displayValue(displayNameData[0].show,displayNameData[0].value)">`
I created a function on scope to select the value only when the 'show' value is true:
$scope.displayValue = function (show, val) {
if (show) {
return val;
}
else {
return '';
}
}
However when I close the modal I get an error:
Error: [ngModel:nonassign] Expression 'displayValue(displayNameData[0].show,displayNameData[0].value)' is non-assignable.
plnkr reference:http://plnkr.co/edit/UoQHYwAxwdvX0qx7JFVW?p=preview
Using ng-value instead of ng-model worked for me.
As HackedByChinese mentioned, you can't bind ng-model to a function, so try like this:
<input type="text" ng-if="displayNameData[0].show"
ng-model="displayNameData[0].value">
Or if you want this control to be visible you can create directive, add function to $parsers that will set empty value according to show:
angular.module('yourModule').directive('bindIf', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
function parser(value) {
var show = scope.$eval(attrs.bindIf);
return show ? value: '';
}
ngModel.$parsers.push(parser);
}
};
});
HTML:
<input type="text" bind-if="displayNameData[0].show"
ng-model="displayNameData[0].value">
You can bind ng-model to function
Binding to a getter/setter
Sometimes it's helpful to bind ngModel to a
getter/setter function. A getter/setter is a function that returns a
representation of the model when called with zero arguments, and sets
the internal state of a model when called with an argument. It's
sometimes useful to use this for models that have an internal
representation that's different from what the model exposes to the
view.
index.html
<div ng-controller="ExampleController">
<form name="userForm">
<label>Name:
<input type="text" name="userName"
ng-model="user.name"
ng-model-options="{ getterSetter: true }" />
</label>
</form>
<pre>user.name = <span ng-bind="user.name()"></span></pre>
</div>
app.js
angular.module('getterSetterExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var _name = 'Brian';
$scope.user = {
name: function(newName) {
// Note that newName can be undefined for two reasons:
// 1. Because it is called as a getter and thus called with no arguments
// 2. Because the property should actually be set to undefined. This happens e.g. if the
// input is invalid
return arguments.length ? (_name = newName) : _name;
}
};
}]);