Dynamic form Angular issue - javascript

I have written this code online dynamic form jsFiddle code
The total and grand total are not auto updating. I had a more simple example before and it was working with a single model item, but then I made an array and now it won't work. My real program I am building is going to have many more fields and I am trying to create a pre-example to show it will work. Can someone quickly see what dumb thing I am forgetting?
<div ng-controller="MyCtrl">
<form name="myForm">
<div ng-repeat="item in items track by $index">
<input type="text" ng-model="item.a">
<input type="text" ng-model="item.b">
<input type="text" ng-model="item.c">
<label>Total: </label><label ng-bind="total(item)"></label>
</div>
</form>
<div>
<label>Grand Total: </label><label ng-bind="grandTotal()"></label>
</div>
</div>
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.items = [
{
a: 0, b: 0, c: 0
},
{
a: 0, b: 0, c: 0
}];
$scope.total = function(item) {
var result = item.a * item.b * item.c;
return isNaN(result) ? 0 : result;
};
$scope.grandTotal = function() {
var result = 0;
angular.forEach($scope.items, function(item) {
result += $scope.total(item);
});
return isNaN(result) ? "" : result.toString();
};
});

ng-bind should be without $scope. You don't need to mention $scope in view bindings, it directly refers to $scope/this(context) of controller.
Other than that additionally change all input's ng-bind to ng-model to enable two way binding over all input fields.
Markup
<input type="text" ng-model="item.a">
<input type="text" ng-model="item.b">
<input type="text" ng-model="item.c">
ng-bind="total(item)"
Forked JSFiddle

Use
<input type="text" ng-model="item.a">
instead of
<input type="text" ng-bind="item.a">
Updated fiddle http://jsfiddle.net/Lhkedykz/17/

Related

AngularJS: How to get changed key and value from existing object in form?

I've an object which contains 100 keys, value pairs.
$scope.obj = {
key1: value1,
key1: value1,
.
.
.
key100: value100
}
I have 100 inputs
<form>
<input value="obj.key1" ng-model="obj.key1" />
<input value="obj.key2" ng-model="obj.key2" />
<input value="obj.key3" ng-model="obj.key3" />
.
.
.
<input value="obj.key100" ng-model="obj.key100" />
<button ng-click="submit> </button>
</form>
When I sumbit the data will send to server. Some time I'll change values and sometimes not. If I change a value in one input I want that key,value from object. So that I can send server call with that data not to send entire data.
I would achieve this by using the default ngForm directives like $dirty and a "save state" object of your original input values. This is a easy solution to achieve what you want. ngForm itself does not provide the original values so you have to create a "save object" and compare them manually.
View
<div ng-controller="MyCtrl">
<form name="myForm" ng-submit="submit()">
<input type="text" name="key1" ng-model="obj.key1"><br />
<input type="text" name="key2" ng-model="obj.key2"><br />
<input type="text" name="key3" ng-model="obj.key3">
<input type="submit" id="submit" value="Submit" />
</form>
</div>
AngularJS application
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.obj = {
key1: 'test 1',
key2: 'test 2',
key3: 'test 3',
}
var originalValues = angular.copy($scope.obj);
$scope.submit = function () {
var paramsChanged = {};
angular.forEach($scope.obj, function (value, key) {
if ($scope.myForm[key].$dirty && originalValues[key] !== value) {
paramsChanged[key] = value;
}
});
console.log(paramsChanged);
}
});
> demo fiddle
Have a copy of the variable and just before sending it to the server, compare the model object with the copied variable using a compare function like the following:
// At the time of initialization
$scope.obj = [ ... ];
var copiedObj = angular.copy($scope.obj);
// At the time of submit
var changed = {};
Object.keys(copiedObj).forEach(key => {
if(copiedObj[key] !== $scope.obj[key]) {
changed[key] = $scope.obj[key];
}
});
// Submit `changed` to the server
Assuming copiedObj is the copy of original $scope.obj, changed will contain the keys which are actually changed. So you can send changed to the server.
You can use $watch to watch for specific changes in $scope.
$scope.$watch('obj.key1', function(newValue, oldValue) {
console.log('Key1 was updated from' + oldValue + ' to ' + newValue');
});
You can do this way have a ng-change bind to your HTML and whenever the ng-change happens for a particular Element write a code to push into an array and send that array to the server.
A sample Code
<input value="obj.key100" ng-model="obj.key100" ng-change="selectedValue(obj.keyx)" />
Angular Code
$scope.tempValues=[];
$scope.selectedValue = function(x) {
$scope.tempValues.push(x);
}
You can use ng-change to detect any changes, as well as a (key, value) syntax in ng-repeat to list your inputs. Here is a demo:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.obj = {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
var toSend = [];
$scope.select = function(key) {
var s = {};
s[key] = $scope.obj[key]
toSend.push(s); // or just the value: $scope.obj[key]
}
$scope.submit = function() {
console.log(toSend)
$scope.sent = toSend;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<form ng-submit="submit()">
<div ng-repeat="(key,value) in obj">
<input ng-change="select(key)" ng-model="value" /><br>
</div>
<button type="submit">Send</button>
</form>
<pre>{{sent | json}}</pre>
</div>

How to detect changes in form and set new state?

I have HTML form with set of elements(inputs).
So, how to detect changes in any input and set new statement in object var data?
For example:
<input type="text" name="parameter" value="30">
When I make chnages in input I need to get output object data as:
var = data = ["paraeter" : 30];
for watch the value on change in angular you should use ng-change and ng-model
var app = angular.module("app", []).
controller("ctrl", function($scope) {
$scope.info = {
parameter: 30,
parameters: {
children: 40
}
};
$scope.callBackIfChange = function(){
console.log($scope.info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<form>
<label>empty value</label>
<input type="text" ng-model="info.name" ng-change="callBackIfChange()">
<br>
<br>
<label>has default value</label>
<input type="number" ng-model="info.parameter" ng-change="callBackIfChange()">
<br>
<br>
<label>nested</label>
<input type="number" ng-model="info.parameters.children" ng-change="callBackIfChange()">
</form>
<hr>
<b>output:</b> {{info | json}}
</div>
Here is the Jquery code covering your cases (onload and input change) without hard-coding the name attributes of inputs tags:
var inputValuesObj = {}; // << Your Object
$(document).ready(function() {
getDefaultInputValues();
});
$(document).on("load keyup", "input", function() {
getDefaultInputValues();
});
function getDefaultInputValues() {
$("input").each(function(index) {
inputValuesObj[$(this).attr("name")] = $(this).val();
});
$("div#res").html(JSON.stringify(inputValuesObj));
}
Your test here - https://jsfiddle.net/uo08fedh/20/
Hope it helps!

Setting data-ng-model in JavaScript

I need to set the data-ng-model attribute of an html input field via javascript.
I know I can't do
element.data-ng-model = "...";
because of the dashes. So I tried
element.["data-ng-model"] = "...";
and
element.dataNgModel = "...";
and
element.datangmodel = "...";
None of these seem to work properly.
Any suggestions?
Try:
element.setAttribute("ng-model", "...");
or if you have JQuery:
$(element).attr("ng-model", "...");
If you need to set the model with javascript you can set it in the controller see below From the angular docs
https://docs.angularjs.org/api/ng/directive/ngModel
(function(angular) {
'use strict';
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;
}
};
}
]);
})(window.angular);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<div ng-app="getterSetterExample">
<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>
</div>

Angular UI-Validate without input field (validate expression)

Im using ui-validate utils
https://github.com/angular-ui/ui-validate
The problem is to validate expression on the form without input field.
For example? I have an object
$scope.item = { field1 : 0, field2: 0, field3: 0 };
I would like to receive the error, provided expression: field1 + field2 + field3 == 0
It is common validation for the whole form. Not for some input.
You can write a small function like this (not really sure, you need to use ui-validate for this):
$scope.validate = function () {
var sum = 0;
// Sum every non Angular key
angular.forEach($scope.items, function (value, key) {
// You can also add another check below "angular.isNumber(value)" if you have some text fields
if (key.charAt(0) !== '$') {
// Call "parseInt()" method here if values are in string
sum += value;
}
});
return sum !== 0;
}
Now, display it at somewhere in your form:
<form>
<div ng-show="!validate()">There is some error. Sum can't be zero</div>
<!-- Your fields below -->
</form>
ui-validate can only be used within input tags as there is a requirement for ng-model. ng-show binding to a function would work.
Here is an example:
http://codepen.io/ctwoodwa/pen/eJmyYg
angular.module('ngExample', ['ngMessages'])
.controller('elemController', Controller1);
function Controller1() {
vm = this;
vm.item = { field1 : 0, field2: 0, field3: 0 };
vm.validate = validate
function validate() {
// Determine if the form is valid.
return (vm.item.field1 + vm.item.field2 + vm.item.field3 == 0);
};
}
<div ng-app='ngExample' ng-controller="elemController as vm">
<form name="exampleForm">
<label for="field1">Field1</label>
<input type="number" name="field1" ng-model="vm.item.field1"/>
<label for="field2">Field 2</label>
<input type="number" name="field2" ng-model="vm.item.field2"/>
<label for="field3">Field 3</label>
<input type="number" name="field3" ng-model="vm.item.field3"/>
<div ng-show="vm.validate()">
<div class="error">This form is not valid</div>
</div>
<button>Submit</button>
</form>
</div>

AngularJS: Filter by regex

I want to filter a list of elements by regex (user will type a regex in the input search field) using AngularJS filter.
I wrote everything that seemed to be necessary for me, but I can't manage to make the regex work correctly.
Here is what I've done so far:
View.html
<div ng-controller="listCtrl">
<input type="text" placeholder="search by any" ng-model="search.$">
<input type="text" placeholder="search by fist name" ng-model="search.fname">
<input type="text" placeholder="search by last name" ng-model="search.lname" >
<input type="text" placeholder="search by tel" ng-model="search.tel" >
<input type="text" placeholder="search by date" ng-model="search.date">
<table>
<tr ng-repeat="user in users|regex : search as res">
<td>{{user.fname}}</td>
<td>{{user.lname.toUpperCase()}}</td>
<td>{{user.tel}}</td>
<td>{{user.date | date: 'EEE'}}</td>
</tr>
<tr ng-if="res.length < 1">
<td>No elements found...</td>
</t>
</table>
</div>
app.js
...
app.filter('regex', function() {
return function(input, property) {
var patt;
var field;
var out = [];
if(input === undefined || property === undefined) {
return out;
}
angular.forEach(property, function(key, value) {
patt = new RegExp(key);
field = value;
});
for (var i = 0; i < input.length; i++){
if(patt.test(input[i][eval(field)]))
out.push(input[i]);
}
return out;
};
});
...
The listCtrl will just add some elements to $scope.
What is wrong here?
You should just be able to call a function as part of your ng-repeat filter expression, ie.
then in your controller you'd have something like:
function textRegEx(row) {
var regex = new RegExp("row");
return regex.test(row)
}
This is for the entire row. You'd have to adapt it to work on just one of the fields. I posted a codepen earlier today here: http://codepen.io/anon/pen/yOjdJV?editors=1010 with a dropdown for selecting the field you could adapt. Obviously if you're enabling searching on multiple fields you could concatenate the calls to regex.test into a single line.

Categories

Resources