I have an array of objects. I want to remove multiple objects from that array.
I have used below code which is working absolutely fine, but I need to check with you guys if there is better way to do that or its fine.
I have done it with angularjs and js.
Orders is the main array on which operations are performed.
Order is array of selected items to remove from main array Orders
$scope.Order = {};
$scope.removeOrders = function () {
angular.forEach($scope.Order, function (data) {
for (var i = $scope.Orders.length - 1; i >= 0; i--) {
if ($scope.Orders[i].Name == data.Name) {
$scope.Orders.splice(i, 1);
}
}
});
}
You can make it quite a bit shorter using filter:
$scope.removeOrders = function () {
$scope.Orders = $scope.Orders.filter(function(order){
return !$scope.Order.some(function(remove){
return remove.Name === order.Name;
});
}); // remove the order from $scope.Orders, if it's name is found in $scope.Order
};
Related
My issue is a puzzling one. When I make use of ng-repeat to repeat through my data to create checkboxes it works as I expect. I will get all the things I referred to and they have duplicates. An snippet is below.
<label ng-if="project.score !='0'" data-ng-repeat="project in projects | unique:'c.projectRef' | orderBy:'c.projectRef'">
<input
id="project.c.projectRef"
type="checkbox"
ng-click="toggleSelection(project.c.projectRef)"
ng-checked="selectedRefs.indexOf(project.c.projectRef) > -1" />
<span>{{project.c.projectRef}}</span>
</label>
As you can see,'unique' is in there and I'm making use of Angular UI for that. There's numerous duplicate references and as I am using the checkboxes as a filter, I only want distinct checkboxes as opposed to as many checkboxes as the times it is referenced which is what ng-repeat alone will do.
It functions exactly like that if I remove unique but when I have unique in what happens is instead of giving me several checkboxes that are all unique, it just gives me one checkbox.
From what I've been testing, it appears this happens when you refer to (sorry I don't know the term for it) data when you have deeper references into it so project.c.projectRef as opposed to project.projectRef.
I'm stumped by this issue so any fixes as well as information on the issue would be greatly appreciated.
It was what I suspected in fact. Angular UI doesn't like nested references, you have to edit your angular-ui.js file and replace the unique function with this addition.
.filter('unique', function () {
return function (items, filterOn) {
if (filterOn === false) {
return items;
}
if ((filterOn || angular.isUndefined(filterOn)) && angular.isArray(items)) {
var hashCheck = {}, newItems = [];
var extractValueToCompare = function (item) {
if (angular.isObject(item) && angular.isString(filterOn)) {
var resolveSearch = function(object, keyString){
if(typeof object == 'undefined'){
return object;
}
var values = keyString.split(".");
var firstValue = values[0];
keyString = keyString.replace(firstValue + ".", "");
if(values.length > 1){
return resolveSearch(object[firstValue], keyString);
} else {
return object[firstValue];
}
}
return resolveSearch(item, filterOn);
} else {
return item;
}
};
angular.forEach(items, function (item) {
var valueToCheck, isDuplicate = false;
for (var i = 0; i < newItems.length; i++) {
if (angular.equals(extractValueToCompare(newItems[i]), extractValueToCompare(item))) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
if(typeof item != 'undefined'){
newItems.push(item);
}
}
});
items = newItems;
}
return items;
I have an observableArray that in its subscribe callback I need to filter the new array passed.
For example:
myArray.subscribe(function(elements) {
ko.utils.arrayFilter(elements, function(element) {
return element.x > 10
})
})
This, of course, doesn't work since arrayFilter nor the native filter() method doesn't change the original array. The problem is that i can't do this:
myArray.subscribe(function(elements) {
var newArray = ko.utils.arrayFilter(elements, function(element) {
return element.x > 10
})
myArray(newArray)
})
because this would be an infinite loop. How would I filter the array inside the subscription function?
The best solution is the one suggested by nemesv: Don't alter the array itself, but instead create a new computed observable that encapsulates the filter behavior.
var filteredArray = ko.computed(function () {
return ko.utils.arrayFilter(myArray(), function(element) {
return element.x > 10;
});
});
The simplest solution, if you're feeling lazy, would be to replace
myArray(newArray)
with
if (newArray.length !== elements.length) {
myArray(newArray);
}
as suggested in the comment by James Thorpe. The flaw here is that every subscriber might run twice, including the filter operation itself.
Your problem implies that you really want to encapsulate your data and not to expose it directly over the view-model.
Try something like this:
var arr = ko.observableArray();
this.AddItem = function(element) {
if (element.x > 10)
arr.push(element);
};
this.GetItems = function() {
return arr();
};
I can't seem to get this right: I've got an array with categories (objects) and a post object:
var categories = $http.get('/api/categories').success(function (data) {
$scope.categories = data;
});
// The code below uses a Rails gem to transport data between the JS and Rails.
$scope.post = gon.post;
// Suffice to say the $scope.post is an object like so:
...
_id: Object { $oid="54f4706f6364653c7cb60000"}
_slugs: ["first-post"]
author_id: Object { $oid="54ef30d063646514c1000000"}
category_ids: [Object, Object]
...
As you can see the post object has a property category_ids which is an array whit all categories associated with this post. In my view (haml) I have the following:
%label
%input{"type" => "checkbox", "ng-model" => "cat.add", "ng-change" => "addCategory(cat)", "ng-checked" => "currentCat(cat)"} {{cat.name}}
As you can see, the ng-checked fires the currentCat() function:
$scope.currentCat = function (cat) {
for (var i = 0; i < cat.post_ids.length; i++){
if (cat.post_ids[i].$oid == $scope.post._id.$oid) {
return true;
}
}
};
The function above loops through the categories in the post (the category_ids property of the post object) and compares it with the parameter given. It works fine with existing categories. The problem appears when I dynamically add a new category and push it in the categories array:
$scope.addCatBtn = function () {
var category = $scope.cat;
$http.post('/api/categories', category).success(function (data) {
$scope.categories.push(data.category);
$scope.cat = '';
$scope.addCategory(data.category);
});
};
The new category does not appear 'checked' in the view. What am I missing?
Any thoughts would be appreciated.
EDIT: Adding addCategory function:
$scope.addCategory = function (cat) {
var found = false;
for (var i in $scope.post.category_ids) {
if (cat._id.$oid === $scope.post.category_ids[i].$oid) {
$scope.post.category_ids.splice(i, 1); // splice, not slice
found = true;
}
}
if (!found) { // add only if it wasn't found
$scope.post.category_ids.push(cat._id);
}
console.log($scope.post);
}
ngModel and ngChecked are not meant to be used together.
You should be fine to just use ngModel.
I'm not really sure why my code isn't running correctly.. what I'm trying to do is create a grocery list object that has a couple of functions to add and remove items..
I can instantiate the objects with new items but my functions don't seem to work for some reason.
If you could save me the few hairs left in my head and tell me where the issue is I would greatly appreciate it.
var groceryList = function(itemNames,quantity) {
if (Array.isArray(itemNames)) {
this.items = itemNames;
this.quantity = quantity
this.addItems = function(newItems){
if ( Array.isArray(newItems) ) {
this.items.concat(newItems);
} else {
console.log("Please enter the items in an array fashion!");
};
};
this.removeItem = function(name) {
var listSize = this.items.length;
for (var i = 0; i < listSize; i++) {
if (this.items[i] == name) {
this.items.splice(i,1);
break;
} else {
console.log("Please enter the items in an array fashion!")
};
};
};
} else {
console.log("Please enter the items in an array fashion!")
};
};
.concat() returns a new array so you have to assign the result back to your instance variable.
So this:
this.items.concat(newItems);
needs to be changed to this:
this.items = this.items.concat(newItems);
or, you could actually use this to append to the array directly:
this.items.push.apply(this.items, newItems);
Because .push() can take more than one argument.
Then, in your .removeItem() function, you need to remove the item you actually found by changing this:
this.items.splice(2,1);
to this:
this.items.splice(i,1);
I am quite new to knockout.js, and I am enjoying learning how to make interfaces with it. But I have a bit of a wall while trying to make my interface more efficient. What I am trying to achieve is remove only the elements selected by $('.document_checkbox').serializeArray(), which contains the revision_id. I will then re-add the entries to the view model with a modified call to self.getDocument(), passing only the modified records which will be re-added. Can anyone help me how to remove the entries from the arrays based on the 'revision_id' values of $('.document_checkbox').serializeArray()
?
function Document(data) {
this.line_id = data.line_id
this.revision_id = ko.observable(data.revision_id);
this.status_id = ko.observable(data.status_id);
}
function DocumentViewModel() {
var self = this;
self.documents = ko.observableArray([]);
self.getDocument = function(){
//Reset arrays
self.documents.removeAll();
//Dynamically build section arrays
$.getJSON("/Documentation/Get-Section", function(allData) {
$.map(allData, function(item) {
var section = { name: item.array_name, display_name: item.display_name, documents: ko.observableArray([])};
self.documents.push(section);
})
//Add document objects to the arrays
$.getJSON("/Documentation/Get-Document", function(allData){
$.map(allData, function(item) {
var section = ko.utils.arrayFirst(self.documents(), function(documentSection) {
return documentSection.name === item.array_name;
});
section.documents.push(new Document(item));
});
});
});
}
self.updateStatusBatch = function(data,event){
$.post('/Documentation/Update-Status-Batch',
{
revision_id : $('.document_checkbox').serializeArray(),
status_id : event.currentTarget.value
}).done(
function(){
//This is where I get confused.
});
}
}
You should modify the /Documentation/Update-Status-Batch in order that it returns the deleted item id. So you will be able to remove it on the client side.
Try this "done" function:
function(removedItemId) {
self.documents.remove(function(doc){
return doc.status_id == removedItemId;
})
}
Take a look at the remove function.
I hope it helps.