Angular array.push() creates a duplicate for ng-repeat - javascript

I have the follow HTML:
<div ng-repeat="contact in vm.contacts">
<input type="text" ng-model="contact.firstName">
<input type="text" ng-model="contact.lastName">
<button ng-click="vm.addNew()">add</button>
</div>
and the following code on my Angular controller:
vm.contacts = [];
vm.addNew = addNew;
init();
function init() {
addNew();
}
function addNew() {
vm.contacts.push({});
}
So, when the page is started, the controller adds an empty object to the vm.contacts array.
My problem is: once I fill the fields and click on the add button, instead of creating an array entry with an empty object, angular is duplicating the previuous array entry.
So, if I enter "John" for first name and "Smith" for last name, and then click on the add button, the resulting array will be:
[
{firstName: "John", lastName: "Smith"},
{firstName: "John", lastName: "Smith"}
]
And the same contact will be displayed twice.
How do I prevent this from happening?
I've tried both to use track by $index on the ng-repeat declaration, and angular.copy() on the addNew function, and nothing seems to work. I want to be able to add a new empty contact, I do not wish to replicate or duplicate one.
Thanks in advance!

Something like this:
VIEW
<div ng-app="app" ng-controller="MainController as vm">
{{vm.contacts}}
<div ng-repeat="contact in vm.contacts">
<input type="text" ng-model="contact.firstName">
<input type="text" ng-model="contact.lastName">
<button ng-click="vm.addNew()">add</button>
</div>
</div>
CONTROLLER
angular
.module('app', [])
.controller('MainController', MainController)
function MainController($timeout) {
var vm = this;
vm.contacts = [{}];
vm.addNew = addNew;
function addNew() {
vm.contacts.push({});
}
}
JSFIDDLE

I think the problem arises here:
<input type="text" ng-model="contact.firstName">
<input type="text" ng-model="contact.lastName">
Once the array receives the first and last contact names, the input after that can only be the first and last name stored in contact as the first values are being stored in contact and from there on can only use those values.

Related

Creating a single array from multiple inputs in vue application

I have a vue application that has a simple form section, for example, these two inputs:
<div class=" form-group col-lg-6">
<label>Name</label>
<input v-model="newUserName" class="form-control" type="text" name="newUserName">
</div>
<div class=" form-group col-lg-6">
<label>Email</label>
<input v-model="newUserEmail" class="form-control" type="text" name="newUserEmail">
</div>
So I have them set to their own v-models, and when I dump those in submission they indeed show the correct input values separately. the problem is, I want to use array.push or something similar so that, when the submit function is hit, it pushes them into a single 'details' array.
So for the example below, i want to push name and email to the details array and only show the array with both values in the console
data() {
return {
details: [],
newUserName:'',
newUserEmail: '',
}
},
methods: {
showDetails() {
let data = {
details: this.details
};
console.log(data);
}
}
Well, you can just push them to the array. When clicking on the submit button you can call some method submit() for example:
submit() {
this.details.push(this.newUserName)
this.details.push(this.newUserEmail)
console.log(this.details)
}
If you want to reset it on every submit you can just add this.details = [] in the begining of the method

newly added item in ng-repeat is changing

i am trying to add a object in ng-repeat, as shown :
<div ng-controller="filtersController as datas">
<table>
<tr>
<th>Names</th>
<th>nos</th>
</tr>
<tr ng-repeat="data in datas.dataset">
<td>{{data.name}}</td>
<td>{{data.no}}</td>
</tr>
</table>
<input type="text" ng-model="models.name" placeholder="enter name" />
<input type="number" ng-model="models.no" placeholder="enter number" />
<input type="button" ng-click="datas.add(models)" value="click me!!!!" />
</div>
relevant javascript is :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (model) {
this.dataset.push(model);
$scope.$apply();
}}]);
Problem :
when i try to add new entries using textboxes, it will add first entry but after that i cannot add anymore entries in it, also the latest added entry changes when the values in the textboxes changes. Why is that?
Also if i change javascript like this :
app.controller('filtersController', ['$scope', function ($scope) {
this.dataset = [{ name: "Vishesh", no: 1 },
{ name: "pqrst", no: 2 },
{ name: "uvwxyz", no: 3 }]
this.add = function (names, nos) {
this.dataset.push({ name: names, no: nos });
$scope.$apply();
}}]);
and HTML :
<input type="text" ng-model="name1" placeholder="enter name" />
<input type="number" ng-model="no1" placeholder="enter number" />
<input type="button" ng-click="datas.add(name1,no1)" value="click me!!!!" />
it adds as many entries i want and works as expected.
What is wrong with the first approach?
ng-model="models.name" creates an object on the scope named "models". Changes to models.name and models.no will change values within that object, but it's still a single object -- so datas.add(model) winds up pushing a reference to that single object onto this.dataset. Further changes to model will change that original object, which is already in this.dataset, because it's all references to the same object.
Your other approach works correctly because you're using separate primitives for ng-model="name1" and ng-model="no1". Here, each time datas.add(name1,no1) runs, it's pushing a newly constructed object onto this.dataset. Further changes to name1 and no1 now won't modify old data, because they're primitives, not object references, and each time you push to this.dataset you construct a new object out of those primitives.
When you push the model object that you pass to your add() method onto the array you are actually pushing the reference to that object and now Angular is going to use two-way data binding on it. What you need to do is first copy the model so it disassociates it from the ng-model directives in your HTML.
this.add = function(model) {
var newModel = angular.copy(model);
this.dataset.push(newModel);
}
Here's a Plunker: http://plnkr.co/edit/SdtSesYxb828yJyDFwws?p=preview

Passing ng-model from view to controller angular js

I am trying to pass value like this from view to controller in angular js of this form. I do not wish to hardcode it in this way. How could it be done in proper manner?
angular.module('user').controller('UsersController', ['$scope', '$stateParams', 'Users',
function($scope, $stateParams, Orders) {
$scope.create = function() {
var user = new Users({
child: [
{ columnA: child[0].columnA, columnB: child[0].columnB, columnC: child[0].columnC },
{ columnB: child[1].columnA, columnB: child[1].columnB, columnC: child[1].columnC },
...
{ columnC: child[10].columnA, columnB: child[10].columnB, columnC: child[10].columnC }
]
});
}
}
});
<form data-ng-submit="create()">
<input type="text" data-ng-model="child[0].columnA">
<input type="text" data-ng-model="child[0].columnB">
<input type="text" data-ng-model="child[0].columnC">
<input type="text" data-ng-model="child[1].columnA">
<input type="text" data-ng-model="child[1].columnB">
<input type="text" data-ng-model="child[1].columnC">
......
<input type="text" data-ng-model="child[10].columnA">
<input type="text" data-ng-model="child[10].columnB">
<input type="text" data-ng-model="child[10].columnC">
</form>
It would be better if an reusable directive that may perform above function.
$scope.create = function() {
child: toJSON(child);
}
function toJSON(var a) {
//automatically search through the view for ng-model with child[index].columnName and change to the form above.
}
I wrote out a plunker that demonstrates one way to do something similar to what you are trying to do using angular practices.
You'll note that I eliminated all the duplication in the view by using ng-repeat, and have made the number of child elements dynamic. I initialized the users object with an empty array, but you could easily initialize the object with data from the server.
Note also that changes to the form are immediately reflected in the object, meaning in the create() function, you can serialize the users object, not the form values. In practice, this is probably not necessary, however, since if you use an angular library like $http, serialization to and from JSON is performed automatically.
$scope.users = {
child: [{}]
};
$scope.create = function() {
var data = angular.toJson($scope.users);
alert(data);
};
$scope.addUser = function() {
$scope.users.child.push({});
};
<form ng-submit="create()">
<div ng-repeat="user in users.child">
<input type="text" ng-model="user.columnA">
<input type="text" ng-model="user.columnB">
<input type="text" ng-model="user.columnC">
</div>
<button type="submit">Submit</button>
</form>
<button ng-click="addUser()">Add New User</button>
<pre> {{users}}</pre>
The main takeaway from this, however, should be that the view and the controller work together to eliminate duplication and unnecessary references. we are no longer referring to child[0] in the HTML, making the HTML more readable and maintainable.

AngularJS - Find all ngModel in the current scope

I have a HTML template like this:
<div ng-controller="MyCtrl">
<input ng-model="model.name" /> {{model.addr}}
<button ng-click="detect()">detect</button>
</div>
And this is the controller:
function MyCtrl($scope) {
$scope.detect = function() {
var fields = []; //['model.name', 'model.addr']
//call server with selected fields
}
}
The template can be customized by user and generated dynamically, not static template. I want to optimize the data model returned from server, only return values for needed fields in the template. The idea is I want to get all the ng-model fields from template and pass them to the server. The returned data will contain only values for these fields, instead of whole data model, for example:
{
name: 'Superhero',
addr: 'Mars'
}
If I change the template like this (remove model.addr):
<div ng-controller="MyCtrl">
<input ng-model="model.name" />
<button ng-click="detect()">detect</button>
</div>
The fields array should be ['model.name'] and the returned data from the server should be:
{
name: 'Superhero'
}
There are any ways to get all ng-model fields in the scope?
Thanks!
You can do 1 thing, wherever you are using ng-modal, take an object modal and assign values by using dot(.).
Suppose
<div ng-controller="MyCtrl">
<input ng-model="model.name1" />
<input ng-model="model.name2" />
<input ng-model="model.name3" />
<button ng-click="detect()">detect</button>
So you can get model in scope hence all the attributes of scope.

Angularjs ng-required call function

It is possible make the required value dependet of some funcion?
Something like this? I want to do this because I want to change the required attribute to some form inputs...
HTML:
Name: <input type="text" ng-model="user.name" ng-required="isRequired('name')" />
Age: <input type="text" ng-model="user.age" ng-required="isRequired('age')" />
JS:
$scope.isRequired(fieldName){
$scope.requiredFields = [];
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
for (i in requiredFields) {
if (requiredFields[i] == fieldName){
return true;
}
}
return false;
}
Updated Answer:
So based on your updated OP, what you want is certainly doable. The problem with what you were trying to do is that ng-required has no ability to execute a function, it only reads a boolean. But we can dynamically create variables based on data from the server to automatically set fields to required:
Updated Plunker
<form>
Name: <input type="text" ng-model="user.test" ng-required="name" /><br/>
<input type="text" ng-model="user.name" ng-required="age" />
<br/>
<button type="submit">Submit</button>
</form>
Note that I put a $scope property for each input in the ng-required attribute. Now we can dynamically create that $scope property and set it to true if our data says we need to:
$scope.isRequired = function(){
$scope.requiredFields = [];
$http.get('fields.json')
.success(function(data){
$scope.requiredFields = angular.fromJson(data);
console.log($scope.requiredFields.required)
for (i = 0; i < $scope.requiredFields.required.length; i++) {
$scope[$scope.requiredFields.required[i]] = true
}
console.log($scope[$scope.requiredFields.required[0]]);
})
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
}
$scope.isRequired()
So it is iterating over an array of required fields received from the server, and then dynamically creating a $scope property for each one that is required, and setting it to true. Any field that has that $scope property in it's ng-required will be required now. Anything not dynamically created will just return false, and ng-required doesn't trigger.
Original answer:
Plunker
As Pratik mentioned, ng-required only accepts a Boolean value, but we can toggle the value of that with a function.
HTML
<form>
Name: <input type="text" ng-model="user.name" ng-required="isRequired" />
<br/><button ng-click="toggle()">Required: {{isRequired}}</button>
<button type="submit">Submit</button>
</form>
code:
$scope.isRequired = true;
$scope.toggle = function() {
$scope.isRequired = !$scope.isRequired;
}
I know this is a couple of years old and so AngularJS may have changed, but the accepted answer as it stands today isn't correct. You can very easily execute a function within ng-required, as it takes an expression, which can be a function. For example:
index.html
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ X ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
script.js
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
In script.js, a function addExp is defined and added to the scope, and then it's called in the ng-click directive of the a tag, which also takes an expression as its argument.
This code is taken directly from the AngularJS documentation on expressions. It doesn't use ng-require directly, but any directive that takes an expression will work the same. I have used the same syntax to use a function for ng-require.

Categories