angularjs, dynamic directive form validation - angularjs

For example, I have a select, and an input (idcard) in the form. The select determines the id-card-type (Tax id, Car license id) of input, I want to add a directive into the input to custom validate its value, and the validation method is different by its type(id-card-type) (determined by the select).
I am thinking to dynamic change the directive to validate, or one directive do validation for all types, but how to pass the type information into the directive.
What should I do?

You could do it without a directive:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.type = 'tax';
$scope.yourForm = {};
$scope.yourForm.inputType = {};
$scope.yourForm.inputType.$valid = false;
$scope.validate = function(type, text) {
// return boolean based on your validation for the input string and type (tax or car)
$scope.yourForm.inputType.$valid = (type === 'tax');
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<form name="yourForm">
<select ng-model="type" ng-change="validate(type, text)">
<option>tax</option>
<option>car</option>
</select>
<input type="text" name="inputType" ng-model="text">
input valid: {{yourForm.inputType.$valid}}
</form>
</div>
In my example, validation of the input is false when 'car' is selected.
Of course, you can wrap all this into a directive.

Related

Push values to array in controller

I have input fields on the DOM that I'm capturing using ng-model.
In my controller, I have an array:
app.controller('mainCtrl', function() {
// set an empty array
self.manualEntry = [];
/**
* ensure form validation
* #returns boolean - ng-disabled value
*/
self.disableForm = function() {
if (self.manualEntry.length <= 0) {
return true;
}
else {
return false;
}
};
});
In my view, I have input fields:
<form>
<input placeholder="John" ng-model="mainCtrl.manualEntry.firstName"/>
<input placeholder="Smith" ng-model="mainCtrl.manualEntry.lastName"/>
</form>
<button type="submit"
ng-disabled="mainCtrl.disableForm()"
title="Submit">Submit
</button>
I thought that $scope automatically updated the model for use in the controller. I thought using dot notation in the DOM would push these values to the array.
When I update these values, the submit button on the form remains disabled (i.e. disableForm() returns true).
How can I push these values to self.manualEntry when they change or are updated on the DOM?
I am making a large assumption on what you want but it seems regular form validation is the way to go for this. Demo at http://plnkr.co/edit/Nzmk3R8eU0fmbBZRrTBa?p=info.
Controller Code
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
// set an empty array
var self = this;
self.manualEntry = {};
//Let HTML5 handle the form validation itself
self.printInfo = function() {
console.log(self.manualEntry);
};
});
HTML Code:
<body ng-controller="MainCtrl as mainCtrl">
<form ng-submit="mainCtrl.printInfo()">
<input placeholder="John" ng-model="mainCtrl.manualEntry.firstName" ng-required="true" />
<input placeholder="Smith" ng-model="mainCtrl.manualEntry.lastName" ng-required="true" />
<button type="submit" title="Submit">Submit
</button>
</form>
</body>

Angularjs calculate based on formula given

Im new in Angularjs
and I would like to do something like the following in directive
<!--get numb1&numb2 from user input-->
<div>
<input ng-model="numb1" type=number/>
</div>
<div>
<input ng-model="numb2" type=number/>
</div>
<!--result display on the following input box, not allow to edit-->
<div>
<input ng-model="result" formula="some formula here, can be anything" readonly/>
</div>
numb1 & numb2 can be change anytime, use $watch instead on ngChange.
Can anyone guide me on this?
You can achieve your requirement with the following code snippet.
The HTML
<div ng-app="demo">
<div ng-controller="DefaultController as vm">
<inputs formula="vm.formula"></inputs>
</div>
</div>
<script type="text/template" id="inputsTemplate">
<div>
<input type="number" ng-model="vm.a"/>
</div>
<div>
<input type="number" ng-model="vm.b"/>
</div>
<div>
<input type="number" ng-model="vm.result" readonly/>
</div>
</script>
The AngularJS code
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('InputsController', InputsController)
.directive('inputs', inputs);
function DefaultController() {
var vm = this;
vm.formula = 'a + b';
}
function inputs() {
var directive = {
restrict: 'E',
scope: {
formula: '='
},
template: function () {
return angular.element(document.querySelector('#inputsTemplate')).html();
},
controller: InputsController,
controllerAs: 'vm',
bindToController: true
};
return directive;
}
InputsController.$inject = ['$scope', '$parse'];
function InputsController($scope, $parse) {
var vm = this;
var expressionFunc = $parse(vm.formula);
$scope.$watchGroup(['vm.a', 'vm.b'], function (newValue, oldValue, scope) {
if (angular.isDefined(vm.a) && angular.isDefined(vm.b)) {
var result = expressionFunc(vm);
vm.result = result;
}
});
}
The jsfiddle link here
The coding style is followed from angular-styleguide by John Papa and from Pro AngularJS by Adam Freeman
Explanation:
There is a main controller which is having a formula set on its scope such as a + b and which is passed to a directive called inputs through an attribute called formula, this an element directive meaning that it is restricted to be applied only as an individual html element.
The inputs directive gets its template from a script tag having html content
by using the template property of the directive definition. The inputs directive has its own controller bound using the controller aliasing syntax.
The inputs directive has three objects on its controller scope they're a, b and result and the magic happens in the InputsController using the $parse service which returns a function when an expression is parsed which can indeed be evaluated on an object to execute that expression and get the result i.e.
var expressionFunc = $parse(vm.formula);
var result = expressionFunc(vm);
vm.result = result;
Also as we need to watch both a and b objects on the scope, we can use the $watchGroup function on the $scope object to handle the change events and update the view accordingly.
Note: we are only parsing once var expressionFunc = $parse(vm.formula); and executing the expression many times var result = expressionFunc(vm); this is an important factor and should be considered for quality of the code and for performance.
You are in the same scope, so, you can use ng-value in order to set a new value given numb1 and numb2. For example, you formula is numb1 + numb2, just try something like this:
<input ng-model="result" ng-value="{{numb1 + numb2}}" readonly/>

Resetting form controls after action in Angular

Need a bit of help with this. I am trying to get Angular to reset value of my input box after add control is invoked. This is kind of important when it comes to drop down select boxes. I want to be able to clear what user has selected after the option is pushed to a collection.
I also seems to have an issue with user.fName updating whatever I previously added to the users array but it's probably related to the above.
Also can you see what I am doing wrong with ng-show?
html
<div ng-controller="UserController" name="form">
<div ng-show="form.fName.$touched">Hello {{ user.fName }}</div>
<input type="text" name="fName" ng-model="user.fName">
<a ng-click="addUser(user)">Add</a>
<br>
<div ng-repeat="user in users">{{ user }}</div>
</div>
javascript
function UserController($scope) {
//$scope.user = {
// fName: "Joe",
// lName: "Doe"
//};
$scope.users = [];
$scope.addUser = function (user) {
if($scope.users.indexOf(user) === -1){
$scope.users.push(user);
} else {
// trigger error/notification message
console.log(user, ' already added');
}
user = {}; //clear user
};
}
ngModel will try to bind to the property given by evaluating the expression on the current scope. If the property doesn't already exist on this scope, it will be created implicitly and added to the scope.
You are currently not using the scoped property in your click action but a copy that is passed as a parameter. Change user to $scope.user in your addUser method
Note: your object comparison to identify already added users won't work as you always create new objects that will never match. Change it to some different comparison method like comparing the fName properties.
html
<div ng-controller="UserController" name="form">
<div ng-show="form.fName.$touched">Hello {{ user.fName }}</div>
<input type="text" name="fName" ng-model="user.fName">
<a ng-click="addUser()">Add</a>
<br>
<div ng-repeat="user in users">{{ user }}</div>
</div>
javascript
function UserController($scope) {
$scope.users = [];
$scope.addUser = function () {
if($scope.users.indexOf($scope.user) === -1){
$scope.users.push($scope.user);
} else {
// will never match as indexOf() will always return -1
console.log($scope.user, ' already added');
}
$scope.user = {}; //clear user
};
}
http://plnkr.co/edit/N5FXJdWRl42YrVwJsUuR?p=preview
As for your ng-show question: $touched was introduced in AngularJS 1.3 and you're referencing AngularJS 1.2.x in your Plunker code.

Callback on 2 Inputs

I have a 2 form input (firstName, lastName) and I want to check if they are unique (form validation) by querying my database. So I basically want to have a callback when two of my input elements is inputted.
What is the angular way of doing this?
I'm thinking of getting the element and using .on('blur') while checking if the other input is valid but this feels very jQuery like.
use ng-blur on the inputs.
Here's an example:
HTML:
<div ng-app='myApp' ng-controller='myCtrl'>
Username : <input type='text' ng-model='model.username' ng-blur='checkInputs()' /><br/>
Password: <input type='text' ng-model='model.password' ng-blur='checkInputs()' /> <br/>
</div>
JS:
var app = angular.module('myApp',[]);
app.controller('myCtrl', function($scope){
$scope.model = {username:'', password:''};
$scope.checkInputs = function(){
if($scope.model.username != '' && $scope.model.password != ''){
if($scope.model.username == 'testuser' || $scope.model.password == 'testpass')
{
alert('insert unique fields!!!!')
}
}
}
});
func will sit in your controller in this case and will have access to the username\password. You need to perform an ajax to check this, but I assume you got the point..
Here's a Fiddle.

Dynamically add input fields with Angular and perform validation on blur

Form at start has 1 input field for address. Bellow there is a link that when clicked adds another input and so on. There is no limitation in number of fields.
Is there more elegant way of adding new elements in model collection in Angular? I'm currently using an array of null elements, and on the link click I just push another null in that array so ng-repeat picks it up. And when I want to submit form I go through that array and filter out elements that are not null.
When input field is focused out/blurred there should be validation performed. I'm currently calling a function from controller on ng-blur event but I'm having trouble passing it current input text value.
Fiddle
HTML:
<div ng-app="TestApp">
<div ng-controller="MainCtrl">
<div ng-repeat="field in fields track by $index">
<input type="text" placeholder="enter the address" ng-model="fields[$index]" ng-blur="validate()" />
</div>
+ Add another input
<br/>
<br/>
List addresses
</div>
</div>
JS:
var app = angular.module("TestApp", []);
app.controller("MainCtrl", function($scope){
$scope.fields = [null];
$scope.addresses = [];
$scope.addField = function(){
$scope.fields.push(null);
};
$scope.listAddresses = function(){
for(var i in $scope.fields){
if($scope.fields[i] !== null){
$scope.addresses[i] = $scope.fields[i];
}
}
alert("Addresses: " + $scope.addresses);
};
$scope.validate = function(){
console.log("Should validate current input");
};
});
Instead of using two arrays, use one and store objects:
$scope.items =
[
{ address: '' }
];
It will now be clearer what the model of the input is, as you don't have to use $index. You can also pass the item to the validate function:
<div ng-repeat="item in items">
<input type="text" ng-model="item.address" ng-blur="validate(item)" placeholder="enter the address" />
</div>
Adding item:
$scope.addItem = function() {
$scope.items.push({ address: '' });
};
Demo: http://plnkr.co/edit/sPlPO2DfrgNHf5AasYlH?p=preview

Resources