Angular update object in array - angularjs

I wanna update an object within an objects array. Is there another possibility than iterating over all items and update the one which is matching? Current code looks like the following:
angular.module('app').controller('MyController', function($scope) {
$scope.object = {
name: 'test',
objects: [
{id: 1, name: 'test1'},
{id: 2, name: 'test2'}
]
};
$scope.update = function(id, data) {
var objects = $scope.object.objects;
for (var i = 0; i < objects.length; i++) {
if (objects[i].id === id) {
objects[i] = data;
break;
}
}
}
});

There are several ways to do that. Your situation is not very clear.
-> You can pass index instead of id. Then, your update function will be like:
$scope.update = function(index, data) {
$scope.object.objects[index] = data;
};
-> You can use ng-repeat on your view and bind object properties to input elements.
<div ng-repeat="item in object.objects">
ID: <input ng-model="item.id" /> <br/>
Name: <input ng-model="item.name" /> <br/>
</div>

Filters that help in finding the element from the array, can also be used to update the element in the array directly.
In the code below [0] --> is the object accessed directly.
Plunker Demo
$filter('filter')($scope.model, {firstName: selected})[0]

Pass the item to your update method. Take a look at sample bellow.
function MyCtrl($scope) {
$scope.items =
[
{name: 'obj1', info: {text: 'some extra info for obj1', show: true}},
{name: 'obj2', info: {text: 'some extra info for obj2', show: false}},
];
$scope.updateName = function(item, newName){
item.name = newName;
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app>
<table ng-controller="MyCtrl" class="table table-hover table-striped">
<tr ng-repeat="x in items">
<td> {{ x.name }}</td>
<td>
Update
<div ng-show="showUpdate" ><input type="text" ng-model="someNewName"> <input type="button" value="update" ng-click="updateName(x, someNewName); showUpdate = false;"></div>
</td>
</tr>
</table>
</body>

Going off your plunker, I would do this:
Change
Edit
to be
Edit
Then use the array index within your $scope.selectSubObject method to directly access your desired element. Something like this:
$scope.selectSubObject = function(idx) {
$scope.selectedSubObject = angular.copy(
$scope.selectedMainObject.subObjects[idx]
);
};
If however, you only have the id to go off of, then you can use the angular filterService to filter on the id that you want. But this will still do a loop and iterate over the array in the background.
See angularjs documentation for ngrepeat to see the variables that it exposes.

Related

How to remove object from array within ng-repeat with AngularJS?

I am having an array with objects like [{...}, {...}] which I am outputting with ng-repeat. Then I have a delete button with a function to delete it.
Is there a simple way to delete it in AngularJS, perhaps with $index? Or I need to specify an ID on every object as an property?
If you don't apply a filter to reorder or filter your array, you can do this:
<div ng-repeat="item in items" ng-click="delete($index)">{{item}}</div>
And the delete function:
$scope.items = [...];
$scope.delete = function (index) {
$scope.items.splice(index, 1);
}
Another way to do it without filter problems: (ONLY IE9+)
<div ng-repeat="item in items | orderBy: 'id'" ng-click="delete(item)">{{item}}</div>
And the delete function:
$scope.items = [...];
$scope.delete = function (item) {
$scope.items.splice($scope.items.indexOf(item), 1);
}
http://jsfiddle.net/oymo9g2f/2/
Here is another example, using Jade too:
template.jade:
label All Items
ul.list-group
li.list-group-item(ng-repeat="item in items | orderBy: '_id'")
strong {{item.name}}
a.trash(ng-click='deleteItem(item)')
//a.trash is a bootstrap trash icon, but you don't need to use it.
controller.js:
$scope.deleteItem = function (item) {
$scope.items.splice($scope.items.indexOf(item),1);
}
removeWith
comparison for each element in a collection to the given properties object,
returning an array without all elements that have equivalent property values.
$scope.collection = [
{ id: 1, name: 'foo' },
{ id: 1, name: 'bar' },
{ id: 2, name: 'baz' }
]
<tr ng-repeat="obj in collection | removeWith:{ id: 1 }">
{{ obj.name }}
</tr>
<tr ng-repeat="obj in collection | removeWith:{ id: 1, name: 'foo' }">
{{ obj.name }}
</tr>
First try to do it this way, but the listing was not actualized at runtime.
$scope.delete = function (index) {
delete $scope.items[index];
}
Then with the answer given above by Facundo Pedrazzini did work properly for me.
$scope.delete = function (index) {
$scope.items.splice(index, 1);
}
Version: AngularJS v1.6.4
In blade.php
<table style="width:100%;">
<tr ng-repeat="name in planFormData.names track by $index">
<td>
<div class="form-group">
<label>Plan Name<span style="color:red;">*</span> </label>
<input type="text" class="form-control" ng-model="planFormData.names[$index].plan_name" name="plan_name" id="status-name" placeholder="Plan Name" autocomplete="off" required>
</div>
</td>
<td>
<i class="icon-plus" ng-click="addRow($index)" ng-show="$last"></i>
<i class="icon-trash" ng-click="deleteRow($event,name)" ng-show="$index != 0"></i>
</td>
</tr>
</table>
In controller.js
$scope.deleteRow = function($event, name) {
var index = $scope.planFormData.names.indexOf(name);
$scope.planFormData.names.splice(index, 1);
};
In Angular 6, I did similar for Multi Dimensional Array. It's working
RemoveThisTimeSlot(i: number, j: number) {
this.service.formData.ConsultationModelInfo.ConsultationWeekList[i].TimeBlockList.splice(j, 1);
}

Select All directive in AngularJS

I am using Checklist model directive with my App. I have issue, if -
I click select all button though it write up the array but its not
selecting checkbox. Same issue with Uncheck All though it empty the
model but it doesn't uncheck checkboxes.
If I select 2 or 3 randomly checkbox and click Select All Button it doesn't select All check-boxes.
Though its writing values to pushArray. But issues are checking and unchecking checkboxes.
$scope.items = [{id:1, name:"abc"},{id:2, name:"def"},{id:3, name:"ghi"}];
$scope.pushArray = [];
<table>
<tr ng-repeat="e in items">
<td class="text-right">
{{e.id}}
<input type="checkbox" checklist-change="imChanged()" checklist-model="pushArray" checklist-value="e.id" >
</td>
</tr>
</table>
I think you are pushing the complete list of object which is wrong. You just need to map the list and pass the id to the $scope
Edit: Works fine when you use $scope.pushArray as an object instead of array.
Working Plnkr
HTML
<body ng-controller="selection">
<table>
<tr ng-repeat="e in items">
<td>
<input type="checkbox" checklist-model="pushArray.ids" checklist-value="e.id"> {{e.name}}
</td>
</tr>
</table>
{{pushArray.ids | json}}
<br />
<button ng-click="select_all();">Select All</button>
<button ng-click="unselect_all();">Unselect All</button>
</body>
JS
var app = angular.module('app', ["checklist-model"]);
app.controller('selection', ['$scope', function($scope) {
$scope.items = [{
id: 1,
name: "abc"
}, {
id: 2,
name: "def"
}, {
id: 3,
name: "ghi"
}];
$scope.pushArray = { ids: []}; // Works fine when using it as an object
//$scope.pushArray = [];
$scope.select_all = function() {
$scope.pushArray.ids = $scope.items.map(function(item) { return item.id; });
};
$scope.unselect_all = function() {
$scope.pushArray.ids = [];
};
}]);
Hope it works for you!
I updated the examples on checklist-model and fix this issue. Check them out http://vitalets.github.io/checklist-model/

How to add checkbox values to an array in AngularJS?

Someone help me please!
Lets say I have an a list of a checkboxes, each checkbox have an ID.
I would like to $scope an array with checked checkboxes IDs.
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<table>
<tr ng-repeat="(key, value) in providers">
<td><input type="checkbox" ng-model="ids[value.Id]"> {{value}} </td>
</tr>
</table>
{{ids}}
</div>
And my controller:
var app = angular.module('myapp',[]);
app.controller('ctrlParent',function($scope){
$scope.providers = [{Id:5},{Id:6},{Id:8},{Id:10}];
$scope.ids = {};
});
Now my array output (if all check boxes are checked) is: {"5":true,"6":true,"8":true,"10":true}
And I would like: [{Id:5},{Id:6},{Id:8},{Id:10}]
this is a possible solution:
<input type="checkbox" ng-model="ids[$index]" ng-true-value="
{{value}}" ng-false-value="{{undefined}}"/>{{value}}
In this way, you will have an array of objects, because you are assigning ids[$index] = value.
There is a cons however: when you double check a checkbox, you will have an empty element in the array.
var app = angular.module('myapp', []);
app.controller('ctrlParent', function($scope) {
$scope.providers = [{
Id: 5
}, {
Id: 6
}, {
Id: 8
}, {
Id: 10
}];
$scope.ids = [];
$scope.$watchCollection('ids', function(newVal) {
for (var i = 0; i < newVal.length; ++i) {
console.log(newVal[i]);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<table>
<tr ng-repeat="(key,value) in providers">
<td>
<input type="checkbox" ng-model="ids[$index]" ng-true-value="{{value}}" ng-false-value="{{undefined}}" />{{value}}
</td>
</tr>
</table>{{ids}}</div>
</div>
http://jsfiddle.net/b9jj0re2/3/
<input type="checkbox" ng-model="ids[$index]" ng-true-value="{{value}}" >
Addign the ng-true-value directive will solve the problem
and in controller change the ids from object to array
plunker

update fields in ngRepeat based on another field in same index

I want to be able to populate a text input field inside an ng-repeat loop with the value of another field inside that same loops index when I click a button.
JSFiddle of what I have so far: http://jsfiddle.net/3FKMx/
When the Copy Names button is clicked I want each text box to be populated with the same value that's in the array. Currently it populates them all with the value of the last item in the array.
Controller:
var app = angular.module('myApp', []);
function someController($scope) {
$scope.names = ["name1","name2","name3"];
$scope.copyNames = function() {
angular.forEach($scope.names,
function (value){
$scope.newName = value;
}
);
};
}
Template:
<div ng-controller="someController">
<button class="btn btn-primary" ng-click="copyNames()">Copy Names</button>
<table>
<tr ng-repeat="name in names">
<td>{{name}}</td>
// I want to populate this input with {{ name }} when I click the button above.
<td><input type="text" ng-model="newName"/></td>
</tr>
</table>
</div>
Solution 1
With an updated data structure it's a bit nicer for looping through.
Solution 2
Create a new array to store the values. Set them by key and look them up by key in your curly braces.
html
<div ng-controller="someController">
<button class="btn btn-primary" ng-click="copyNames()">Copy Names</button>
<table>
<tr ng-repeat="name in names">
<td>{{name}}</td>
<td><input type="text" ng-model="models[name]"/></td>
</tr>
</table>
</div>
JavaScript
var app = angular.module('myApp', []);
function someController($scope) {
$scope.names = ["name1","name2","name3"];
$scope.models = {};
$scope.copyNames = function() {
angular.forEach($scope.names,
function (value, key) {
$scope.models[value] = value;
}
);
};
}
DEMO (updated your fiddle) is what you are looking for?
Tried using an object to hold the label and model:
$scope.names = [{label: "name1", model: ''},
{label: "name2", model: ''},
{label: "name3", model: ''}];
//Also using jQuery.each to break from the loop
//once we know which value to copy
$scope.copyNames = function() {
$.each($scope.names,
function (i, value){
if(value.model) {
angular.forEach($scope.names, function(name){
name.model = value.model;
});
//Break the loop if done copying
return false;
}
}
);
};
Note: jQuery is used as an external library which will be available in angular as is.

angular.js, can't edit dynamically created input fields

Using angular.js, I have a dynamic list of form fields I want to display to the user for editing (and later submission):
var app = angular.module('app', []);
app.controller('Ctrl', function($scope) {
$scope.fields = {
foo: "foo",
bar: "bar",
baz: "baz"
};
});
And the HTML:
<div ng-app="app" ng-controller="Ctrl">
<table>
<th>key</th>
<th>value</th>
<th>fields[key]</th>
<tr ng-repeat="(key,value) in fields">
<td>{{key}}:</td>
<td><input type="text" ng-model="value"/></td>
<td><input type="text" ng-model="fields[key]"/></td>
</tr>
</table>
</div>
See this fiddle. For a reason I don't understand, the text input boxes aren't editable. I've tried two different ways as seen above: value and fields[key]. value isn't editable at all, and fields[key] will allow one keystroke and then it blurs. What am I doing wrong? Thank you.
SET answered why it's happening, but a work-around to achieve the desired behavior would be to maintain a separate array of your keys, and run ng-repeat off those keys. I added some text fields for testing to add more properties to $scope.fields
You could use $watch to dynamically set the keys when the property count changes, if your requirements were that the field count may change.
http://jsfiddle.net/aERwc/10/
markup
<div ng-app="app" ng-controller="Ctrl">
<table>
<th>key</th>
<th>value</th>
<tr ng-repeat="key in fieldKeys">
<td>{{key}}:</td>
<td><input type="text" ng-model="fields[key]"/></td>
</tr>
</table>
<div><h6>Add a field</h6>
key: <input type="text" ng-model="keyToAdd" /><br />
value: <input type="text" ng-model="valueToAdd" />
<button ng-click="addField()">Add Field</button>
</div>
</div>
controller
var app = angular.module('app', []);
app.controller('Ctrl', function($scope) {
$scope.fields = {
foo: "foo",
bar: "bar",
baz: "baz"
};
$scope.fieldKeys = [];
$scope.setFieldKeys = function() {
var keys = [];
for (key in $scope.fields) {
keys.push(key);
}
$scope.fieldKeys = keys;
}
$scope.addField = function() {
$scope.fields[$scope.keyToAdd] = $scope.valueToAdd;
$scope.setFieldKeys();
$scope.keyToAdd = '';
$scope.valueToAdd = '';
}
$scope.setFieldKeys();
});
It is editable but after each key press your text field losing focus so that you have to click on it again to put another char.
And that happens because whole you template being re-rendered after each change in any of models. And after template re-rendered, currently there is no way to know which input should be focused. So you should create that way and you may want to write directive to hold focus on selected input.
You need to use an array of objects. Hopefully you can rework your model:
$scope.fields = [
{label: "foo", value: "foov"},
{label: "bar", value: "barv"},
{label: "baz", value: "bazv"}
];
<tr ng-repeat="field in fields">
<td>{{field.label}}:</td>
<td><input type="text" ng-model="field.value">
Fiddle.

Resources