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
Related
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.
I'm having an object in my controller like the following:
$scope.colors = {
green: "Green",
yellow: "Yellow",
red: "Red"
};
I'm trying to create the radio inputs dynamically and then bind the value of the input to the object's key.
I'm trying something like this:
<label ng-repeat="color in colors">{{color}}
<input type="radio" ng-model="model.color" name="name" ng-value="{{color}}" ng-change="test()" />
</label>
but I can't make it work.
Here is my fiddle
You never define your model on the controller. I have updated your fiddle to do so: https://jsfiddle.net/Xsk5X/1380/
$scope.model = {"color":"test"};
I also added a <span> which displays the selected color to show it is working
I've added a new function and variable - $scope.createColors and $scope.colorsToBind.
The function will convert $scope.colors into an array of just the object keys, and then create a new array of Objects containing the key and value for that color, but as accessible fields; each will look like {key:"green", value: "Green"}. Once we have the array of these objects, the function will then set the value of $scope.colorsToBind to that array.
Your html is now using that new variable colorsToBind, and is displaying the value of each object but binding to the key of each one.
I managed to come with a cleaner solution.
<label ng-repeat="(key, value) in colors">
<input type="radio" ng-model="model.color" name="name" ng-value="key" /> {{value}}
</label>
here is the fiddle
you can do like this also :
<label ng-repeat="color in colors">{{color}}
<input type="radio" ng-model="colors" name="name" value="{{color}}" ng-change="test()" />
</label>
$scope.test = function() {
alert($scope.colors);
};
Jsfiddle
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.
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.
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.