One form and validation for many objects with different data - javascript

Is it possible to check that form is valid in js file, but for many objects? I want to have only one form in html and don't use any of ng-repeats or other loops in html, then check form is valid for all objects.
Exmaple
<!DOCTYPE html>
<html ng-app="AngularApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"> </script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller="mainController">
Click on table row if u want to change data in form
<table>
<thead>
<tr>
<td>
Index
</td>
<td>
Name
</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="element in testCollection"
ng-click="changeActiveElement(element)">
<td>{{ $index }}</td>
<td>{{ element.name }}</td>
</tr>
</tbody>
</table>
<br />
<br />
<br />
<form name="exampleForm">
<div class="row">
Name [required]: <input type="text" ng-model="activeObject.name" required>
</div>
<div class="row">
Phone [required]: <input type="text" ng-model="activeObject.phone" required>
</div>
<div class="row">
Active: <input type="checkbox" ng-model="activeObject.active">
</div>
</form>
<br />
<button ng-disabled="">
This button should be enable if all objects from table will pass form validation
</button>
but how to do this? Button should be know that every form is good or not, even if won't change object by clicking on table row.
</body>
</html>
Js:
var app = angular.module("AngularApp", []);
app.controller('mainController', function($scope) {
$scope.testCollection = [
{
name: 'Mike',
phone: 12345678,
active: true
},
{
name: 'Martin',
phone: '',
active: false
},
{
name: 'Anna',
phone: '',
active: ''
}
];
$scope.activeObject = $scope.testCollection[0];
$scope.changeActiveElement = function(element) {
$scope.activeObject = element;
};
});

Yes its possible, so lets say that 'many objects' are like this :
$scope.fianlObject = { manyObj1 : {}, manyObj2 :{}}
then do something like this with HTML :
<form id="frm1" name="frm1" ng-submit="submit()">
<div class="form-group required" ng-class="isInvalid('manyObj1', form1)">
....
</div>
<div class="form-group required" ng-class="isInvalid('manyObj2', form1)">
....
</div>
.
.
</form>
and something like this with script :
$scope.isInvalid = function (manyObj, form) {
if (form&& form.$submitted) {
return ( form[manyObj] && form[manyObj].$invalid) ? 'has-error'
: '';
}
return '';
}

Angular have FormController, that have property $invalid
$invalid
boolean
True if at least one containing control or form is invalid.
and you can use it like
<button ng-disabled="formName.$invalid" ... >

Yes, you can do that.
Just specify you validator function as a pure function which just accepts a plain object with the data and returns an array of errors (empty if everything is ok). This function should know about context it's used in.
Simple example:
function validateForm(data) {
var errors = [];
if (data.name === 'Joe') errors.push("You can't be Joe!");
return errors;
}
Then, every time you want to validate your form, convert your form data to JS object and use the function. Also, you can use such function in any other context, no matter who is initiator of the validation.

Related

Permit of submitting an input value only when it fulfils a pattern

I want to make a list of items, double-clicking on one item makes it editable. Currently, while editing an item, clicking outside (ie, blur) or enter by keyboard submits the new value.
I want to be able to submit the new change only when it is not empty or fulfil a pattern (eg, a file name with .).
I tried ng-required="true", it did not work.
Does anyone know how to set this restriction?
JSBin
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<style>
input {
font-size: 20px;
border:none;
background-color:transparent;
}
</style>
</head>
<body ng-app="app" ng-controller="Ctrl">
<table>
<tr ng-repeat="item in items">
<td>
<input type="text" value="{{item.name}}" ng-blur='eEditable = -1' ng-readonly='$index !== eEditable' ng-dblclick="eEditable = $index" ng-keypress="keypress($event)" ng-required="true"/>
</td>
</tr>
</table>
<script>
var app = angular.module('app', []);
app.controller('Ctrl', ['$scope', function ($scope) {
$scope.items = [{ name: "item #1" }, { name: "item #2" }, { name: "item #3" }];
$scope.eEditable = -1;
$scope.keypress = function ($event) {
if ($event.keyCode === 13)
$event.target.blur()
}
}])
</script>
</body>
</html>
Edit 1: the existing answers suggest to use form, but I don't want to use form or submit button.
One solution would be verifying the new value in a myBlur function: if the pattern is not satisfied, we could set the focus back to the input field and let users modify the value again. Here is another JSBin.
Does anyone know how to set the focus back to the input field?
Does anyone know how to set the focus back to the input field?
If you are validating the inputs in the ngKeypress event you can use the $event.
If the validation fails, set focus back to the input with
angular.element($event.currentTarget).focus();
use ng-pattern with a regular expression
<input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" />
I found this site helpful making regex http://regexr.com/
You could prevent submission by using a <form> with ng-submit, ng-required, and, ng-pattern directives:
<form name="myForm" ng-submit="submitClicked($event, myForm)">
<input type="text" ng-required="true" ng-pattern="/.)/">
<input type="submit">
</form>
$scope.submitClicked = function($event, form) {
if (!form.$valid) {
$event.preventDefault();
}
}

Using the angular datepicker with ng-repeat

I'm trying to write something that lets me edit records with dates in them, I have this inside my table:
<tr ng-repeat="event in eventFixtures track by $index">
<td>{{event.date | date: 'dd/MM/yyyy'}}</td>
<td>
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text"
class="form-control"
ng-model="event.date"
is-open="fixture{{$index}}popup"
ng-click="openFixturePopup($index)"/>
</p>
</div>
</div>
</td>
</tr>
However when I go to click on one of the input boxes the datepicker doesn't show up.
The open function is here:
$scope.openFixturePopup = function(fixture) {
$scope["fixture"+fixture+"popup"] = true;
};
The variables are delcared like so:
for(var i = 0; i < data.length; i++) {
$scope["fixture"+i+"popup"] = false;
}
From having the values of the variables be printed out above the table, they are being changed and if I create an input that is linked to a specific fixture popup e.g. like this:
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text"
class="form-control"
uib-datepicker-popup="{{format}}"
ng-model="testDate"
is-open="fixture0popup"
ng-click="openFixturePopup(0)"/>
</p>
</div>
</div>
It works fine.
Does anyone know what might be causing this?
I have a hunch that trying to use interpolation for a variable name is what is throwing this off. I tried numerous things like $scope[dynamicVarName], $scope.$eval([dynamicVarName]), and others to no avail. I can't say for certain that is your issue, but I think I found a solution that you can try.
Set an isOpen flag on your event and look at that, rather than creating a bunch of scope variables for state. This is also advantageous because if the index changes, the open state moves with the array element, so there is less chance that you get invalid states.
Here's what the code could look like:
var app = angular.module("myApp", [])
.controller("myCtrl", function ($scope) {
$scope.eventFixtures = [
{name: "1", date: "1-1-01"},
{name: "2", date: "2-2-02"},
{name: "3", date: "3-3-03"},
{name: "4", date: "4-4-04"},
{name: "5", date: "5-5-05"}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="event in eventFixtures track by $index">
<td>{{event.date | date: 'dd/MM/yyyy'}}</td>
<td>
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text"
class="form-control"
ng-model="event.date"
is-open="{{event.isOpen}}"
ng-blur="event.isOpen = false;"
ng-focus="event.isOpen = true;"/>
isOpen? {{event.isOpen || false}}
</p>
</div>
</div>
</td>
</tr>
</table>
</div>

Using Form Values to Create Dynamic Link with AngularJS

So I have created a very simple HTML form with a couple of fields which I want to retrieve using AngularJS. With those retrieved values, I wish to create a dynamic link (which will eventually be used to create a custom Solr query).
Please keep in mind I am very new to Angular and development in general.
Thank you for your help!
HTML:
<html ng-app="solrApp">
<head>
<link link rel="stylesheet" href="bootstrap-3.3.5-dist/css/bootstrap.min.css" />
<link link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.4.3/angular.min.js"></script>
<script type= "text/javascript" src="app.js"></script>
</head>
<body>
<h1 class="headline">Logo or Something Here</h1>
<div class = "queryForm" ng-controller="FormController">
<input type="text" class="queryBox" id="mainQueryString" placeholder="Query String"><br />
<input type="text" class="queryBox" placeholder="Filter Query"><br />
<input type="text" class="queryBox" placeholder="Sort By"><br />
<h2>Extract only from rows:</h2>
<input type="text" class="halfQueryBox" placeholder="Start"><input type="text" class="halfQueryBox" placeholder="End"><br />
<input type="text" class="queryBox" placeholder="Field List (Separate by comma)"><br />
<input type="text" class="queryBox" placeholder="Raw Query Parameters (key1=val1&key2=val2)"><br />
<button type="button">Submit Query</button>
</div>
<div class = "results" ng-controller="SolrController">
<ul>
<li ng-repeat="item in items">
{{ item.key }} - <em>{{ item.value }}</em>
</li>
</ul>
</div>
</body>
</html>
JS:
(function(){
var app = angular.module('solrApp', []);
app.controller('SolrController', function($scope, $http){
$http.get('jsonURL')
.then(function(res){
$scope.items = res.data;
});
});
app.controller('FormController', function() {
this.fullQuery = {
queryString: '',
filterQuery: '',
sortBy: '',
startRow: '',
endRow: '',
fieldList: '',
rawQuery: ''
}
});
var jsonURL = function(fullQuery){
/*A function here that will put together a string into the form of a
URL query using all of the value inputs from the form above.
Ex: //http://localhost:8983/solr/CORE/select?q=QUERYSTRING&fq=FILTERQUERY
&start=START&rows=END&fl=FIELDLIST&wt=json*/
};
})();
You want to use the $scope model object and do data-binding. So in your controller.
$scope.fullQuery = {
queryString: '',
filterQuery: '',
sortBy: '',
startRow: '',
endRow: '',
fieldList: '',
rawQuery: ''
}
Then in your html do
<input type="text" class="queryBox" id="mainQueryString" placeholder="Query String" ng-model="fullQuery.queryString"><br />
Using ng-model binds the value of that html element to the variable in the $scope model in your controller.
In the javascript to access the elements you must use $scope. but in html you omit it for things like ng-model and {{ }}
So now in your function to build the url you can use $scope.fullQuery.queryString etc... like variables.

How to move functions to a separate file in MVVM?

I have following Kendo UI MVVM. Here the business functions are written inside the viewModel.How can we move these functions to a different file?
The challenge I am facing is due to the “this” keyword inside these functions which refers the model present inside the observable.
View Model
<script type="text/javascript">
var viewModel = kendo.observable({
// model definition
employees: [
{ name: "Lijo", age: "28" },
{ name: "Binu", age: "33" },
{ name: "Kiran", age: "29" }
],
personName: "",
personAge: "",
//Note: Business functions does not access any DOM elements using jquery.
//They are referring only the objects in the view model.
//business functions (uses "this" keyword - e.g. this.get("employees"))
addEmployee: function () {
this.get("employees").push({
name: this.get("personName"),
age: this.get("personAge")
});
this.set("personName", "");
this.set("personAge", "");
},
deleteEmployee: function (e) {
//person object is created using "e"
var person = e.data;
var employees = this.get("employees");
var index = employees.indexOf(person);
employees.splice(index, 1);
}
});
</script>
Head
<head>
<title>MVVM Test</title>
<script type="text/javascript" src="lib/kendo/js/jquery.min.js"></script>
<script type="text/javascript" src="lib/kendo/js/kendo.web.min.js"></script>
<!----Kendo Template-->
<script id="row-template" type="text/x-kendo-template">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td><button type="button" data-bind="click: deleteEmployee">Delete</button></td>
<td>
</tr>
</script>
<!--MVVM Wiring using Kendo Binding-->
<script type="text/javascript">
$(document).ready(function () {
kendo.bind($("body"), viewModel);
});
</script>
</head>
Body
<body>
<table>
<thead>
<tr>
<th>
Name
</th>
<th>
Age
</th>
</tr>
</thead>
<!--The data-template attribute tells Kendo UI that the employees objects should be formatted using a Kendo UI template. -->
<tbody data-template="row-template" data-bind="source: employees">
</tbody>
</table>
<br />
<br />
<form>
<input type="text" name="personName" placeholder="Name" data-bind="value: personName" /><br />
<input type="text" name="personAge" placeholder="Age" data-bind="value: personAge" /><br />
<button type="button" data-bind="click: addEmployee">
Add</button>
</form>
</body>
REFERENCES
The Kendo MVVM Framework - packtpub
MVVM / Remote binding -- demos.telerik
DropDownList / MVVM - demos.telerik
DataSource / Basic usage
Whether you define it in another javascript file or outside of your kendo.observable function, you just need to bind 'this' to the function.
This is untested but the first may work, while the second is sure to work!
function externalAddEmployee() {
this.get("employees").push({
name: this.get("personName"),
age: this.get("personAge")});
this.set("personName", "");
this.set("personAge", "");
}
kendo.observable({
// first solution: may not work because the 'this' you want is not properly defined
addEmployee: externalAddEmployee.bind(this);
// second solution: will work everytime
// the 'this' is properly defined, just proxy the call to the function
addEmployee: function() { return externalAddEmployee.apply(this, arguments); }
})
To improve the second solution, you can even make an helper function:
function proxy(fn) { return function() { return fn.apply(this, arguments); })
// and then
kendo.observable({
addEmployee: proxy(externalAddEmployee)
})

Clone elements in angularjs

I need to duplicate some input fields in order to handle data from clients. I have done it with jQuery http://jsfiddle.net/m7R3f/1/
HTML:
<fieldset id="fields-list">
<div class="pure-g entry">
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="input-1" name="input-1">
</div>
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="date" name="date">
</div>
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="input-2" name="input-2">
</div>
</fieldset>
<button id="add">Add</button>
JS
$(document).ready(function ()
{
$("#add").click(function ()
{
$(".entry:first").clone(false).appendTo("#fields-list");
});
});
However I just start learning Angular and want to convert these code to Angular.
I have read questions in stackoverflow and found the code with angularjs here: http://jsfiddle.net/roychoo/ADukg/1042/. However, it seem works only for ONE input field? Can I clone/duplicate several input fields using AngularJS? (in other word: convert my code above into AngularJS version?)
Thank you very much.
If you want to clone html element, the best way to use ng-repeat directive.
Your Controller
var App = angular.module('App', []).controller('Test', ['$scope',
function($scope) {
$scope.inputCounter = 0;
$scope.inputs = [{
id: 'input'
}];
$scope.add = function() {
$scope.inputTemplate = {
id: 'input-' + $scope.inputCounter,
name: ''
};
$scope.inputCounter += 1;
$scope.inputs.push($scope.inputTemplate);
};
}
])
<!DOCTYPE html>
<html ng-app="App">
<head lang="en">
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-controller="Test">
<fieldset id="fields-list">
<div class="pure-g entry" ng-repeat="input in inputs track by input['id']">
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="input" name="input-1">
</div>
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="date" name="date">
</div>
<div class="pure-u-1-5">
<input type="text" class="pure-input-1" id="input-2" name="input-2">
</div>
</div>
</fieldset>
<button type="button" id="add" ng-click="add()">Add</button>
</body>
</html>
Angular prevents of creation duplicated elements, to avoid this, use track by like in the example
You should create an array and use ng-repeat in your HTML. Each object in the array can contain the data necessary to populate your divs. If you want to start with three entries, then add the data for those three. If you want to add more, then simply push onto the array. Because of Angular's 2-way data binding your form field will appear once the element is pushed onto the array.
For more details on how to do this, checkout the To Do example on Angular's home page.
How about this(Fiddle)
add two more ng-model and push those models
$scope.add = function(){
$scope.items.push($scope.newitem1,$scope.newitem2,$scope.newitem3);
}

Categories

Resources