update WinJS.Binding.List on Splice - javascript

I am building an App similar to the Grid App Template for Windows 8 using Javascript and the winJS framework. For one of my pages lots of my items have the same value and I have returned just 1 item for each of these different values using the .createFiltered. This is working great for adding new items because the .createFiltered method updates and checks to see if my new item has a new value.
The issue I am having is with removing items. When I splice the WinJS list there is a chance it is the removing the item which which the .createFiltered method has returned for the repeated value, but there are still other items about with that value. So i need the .createFiltered to run again and then pick a different item.
I will give you an example.
I have 5 items with 2 types's between them "Apple" and "banana".
On my home page I have sorted the items into Fruit / Vegetables / Sweets. These 5 items all come under fruit but instead of giving the different brands I just want it to say "Apple" and "Banana" so I have a function that will get one item for each type and bind it to a WinJS.Binding.Template and then use "textContent: Type" to display Apple / Banana.
Say I have 2 apples "Gala" and "Granny Smith" and I have bound the data of "Gala" to the home page and I then run out of "Gala" Apples (deleting it). I still have Apples in stock (Granny Smith) so I need to .createFiltered function to run again as the Home Page no longer displays "Apples" under Fruit.
Thank you

Since writing this I have managed to find a way to get it to work. What I have done is found the value of the item I am deleting and then gone through the array to see if I can find another item with that value. I have then removed that from the list and then re-added it causing .createFiltered to process the item.
The exact code:
function dashRefresh(toDelete) {
//Project Key of Deleted Item
var deletedProjectKey = toDelete.data.project.key;
//Use new item to bind project data
var newProjectBinder = 0;
var newProjectBinderIndex;
for (var i = 0; i < list.length; i++) {
var listItem = list.getAt(i);
if (listItem.project.key == deletedProjectKey) {
newProjectBinder = listItem;
newProjectBinderIndex = i;
i = list.length;
}
}
if (newProjectBinder != 0) {
list.splice(newProjectBinderIndex, 1);
list.push(newProjectBinder);
}
}

Related

Compare two arrays values, and populate if no match

I need help with matching two arrays. I have TypeScript in the example code, but it can be understood since its about array operations more or less.
In short:
I have two arrays; myItems[] and allItems[]. myItems[] can only hold maximum 4 values.
I want to first check if the items in myItems[] is 4, AND/OR exist in the other array allItems[].
If NOT: populate myItems[] with values from allItems[] (until it contains 4 values) and/or replace the items that is missing (relative to allItems[]) with other items in allItems[] (I'm trying to use default values instead of randomly taking values in my example code).
Description:
I have a widgets (quick links) module that show 4 links at a time, but there are in total 20 different links (or more). All links is stored in a list and each has its own unique ID. In code, all links is extracted and returned in an array (like the allItems[] in above example).
The user can save the links he/she wants to show in the widget. The user settings is stored and returned as an array with the ID of the links that the user have saved. Like the myItems[] above,
Problem:
I have a solution that check the length of the myItems[], and if needed populates items from the allItems[] one. However, it does NOT check if the items in the user array exist in allItems[] and then populates it with the default links. In practical it means that the user can save links and it will be shown in the widget as intended. BUT if a link is removed in the list (which will then be removed in the allItems array) only 3 items will be shown as the myItems[] doesn't check with the allItems[] array to see if it exists there.
Code:
public async getUserWidgets(): Promise<Widget[]> {
let allWidgets = await this.getAllWidgets(); //Array with all the links ID from the list
let userRepository = new UserProfileRepository(this.absoluteWebUrl);
let userSettings = await
userRepository.getUserExtensionValues(this.context); //Extracting the user Settings which contains the ID of the saved linksvar
result:Widget[] = []; //the array where the result will go in
//if the user doesnt have any saved links, or if the user have less than 4 saved links
if (userSettings == null || userSettings.QuickLinksWidgets == null ||
userSettings.QuickLinksWidgets.length < 4)
{result = allWidgets.filter((w) => {return w.defaultWidget;}).slice(0,4);
}
else {var ids =userSettings.QuickLinksWidgets;
for (let i = 0; i < 4; i++) {
let id = '' + ids[i];let w = allWidgets.filter((e) => { return e.id == id;});
if (w.length == 0) {
continue;}
result.push(w[0]);}}
return new Promise<Widget[]>(async (resolve) => {resolve(result);});}
A simple way to check if an array holds a value is using the includes() method.
for(let value of myitems){
if(allitems.includes(value)){
console.log("Duplicate")
}
}
The above code will loop through each value in your myitems array and test if that value is in the allitems array.

Ember.js - Remove Only One instance of Object with removeObject

Explanation
I have a very simple calorie tracking app that uses the Nutritionix API to search for food items based on the user's input. The results are added to a results array, which is then displayed to the user. When a user clicks the "Add" button next to one of these items, the calories are added to a counter, and the food itself is added to a todaysFood array (using Ember's pushObject). This is then used to display which food the user has consumed today in a separate table.
When a user clicks the remove button next to one of the todaysFood items, it triggers an action, removeItem, and passes the index of the item clicked to removeItem. This index is used inside of Ember's removeObject to remove the item from the todaysFood array, and thus update the view (remove that item from the list and its calories from the counter).
Problem
When more than one of the same item are added to todaysFood, clicking remove on just one of those items removes ALL of the instances from todaysFood, and the view. This makes sense to me now, because of the docs' example:
var cities = ['Chicago', 'Berlin', 'Lima', 'Chicago'];
cities.removeObject('Chicago'); // ['Berlin', 'Lima']
cities.removeObject('Lima'); // ['Berlin']
cities.removeObject('Tokyo') // ['Berlin']
However, it also only removes the calories of ONE item, not all instances.
So, the question is: How do I remove only ONE instance of that item when remove is clicked? I.e., if two tacos are added, and I click remove on one, I only want that ONE to be removed (from the list and the calories).
Here is my removeItem action:
removeItem(index) {
var self = this;
// Store property paths for easy access
let todaysPath = this.get('healthData').todaysFood;
let caloriesPath = 'healthData.calories';
this.set(caloriesPath, this.get(caloriesPath) - Math.round(todaysPath[index].fields.nf_calories));
todaysPath.removeObject(todaysPath[index]);
}
Disclaimer
I'm aware that I may not be handling this correctly at all. I'm open to any suggestions to make this better. Thanks!
You have index of object to remove so you can try using removeAt() method:
todaysPath.removeAt(index);

Angular Sorting and counting, Dragula

I have the following scenario:
A JSON object array gets fetched with angular ajax and displayed as list with ng-repeat.
The list gets sorted by object property "index", which is set as default input value of the respective list item.
The user can change the input values.
On press of a button, the list gets redrawn, sorted by the updated input values.
Now there should be a counter, ++ whenever a list item changes position.
I so far managed to count all list items with updated values.
How do I register position changes for the other list items that change position?
For example: #3 changes position to #1, then #1 and #2 also change position.
Second, using Dragula, the list items are now draggable.
How can I resort the list after list items were dragged?
I also tired Angular Dragula without success.
here is my code on github.
Thank you for your help!
I had a similar issue. You need a way of getting the DOM indexing and updating the JavaScript object to match. I looped through the DOM elements, got their index values, found the corresponding item in the data object and then set the index value in the object to that of the DOM index value. Hope this example helps someone.
var updateIndexes = function() {
// this gets the index of any DOM element, like jQuery index()
function getIndex(el) {
for (var i = 0; el = el.previousElementSibling; i++);
return i;
}
// loop through the list DOM elements
for (var i = 0; i < dataArray.length; i++) {
var dataId = dataArray[i].id;
var domIndex = getIndex(document.getElementById(dataId));
dataArray[i].index= domIndex;
}
};

Batch adding items to Selectize.js control stalling the browser completely

i am using selectize.js to display a multiselect dropdown list witch zipcodes and cities. Its functioning well when searching and adding one item at a time, with the autocomplete search, but when im trying to add a batch of zipcodes from a multidimensional array, it totally frezzes the page, and takes more than 20 seconds to complete with insertion of a batch of 100 zip codes or so.
The following is my two add and remove functions, using data from my multidimensional array of zipcodes, with a parent region id. The events are fired via a dropdown of regions with the value of the first dimension in the region_relations array.
var control = new Array();
control.push($select[0].selectize);
control.push($select[1].selectize);
function addItemToselect(key_index, region){
var index;
for (index = 0; index < region_relations[region].length; ++index) {
control[key_index].addItem(region_relations[region][index]);
}
}
function removeItemToselect(key_index, region){
var index;
for (index = 0; index < region_relations[region].length; ++index) {
control[key_index].removeItem(region_relations[region][index]);
}
}
I can't seem to find a solution to this problem - i thought i could maybe use jQuery to set the original options checked and call the refreshItems() function in selectize, but it seems like selectize is actually emptying the original , and so options will only be present in this element if an item has been selected using selectize - the script itself includes a addItems() function, but it basically does the same as above, i hoped the selectize addItem had a efficient way of batch adding items, but as it seems right now, i might be out of luck, and we are talking a max of 100 items to add or remove at a time....?
Do you have any suggestions?
My problem can be demonstrated "real life" on this page: http://onlineboligsalg.dk/soeg-bolig/ , the controls are found on the left.
thanks in advance for your help

How to delete the last item of WinJS Group Binding List

I am writing a Windows 8 app with WinJS. I am using a Grouped Binding List
var groupedSortedListProjection = list.createGrouped(groupKey, groupData, groupSorter);
that will have two groups with keys of 'doing' and 'done'. So, when a user click on an item in group 'doing', an item will be added to 'done'.
I am trying to limit the number of items in group 'done' to only 5 items. So every time a new item is added to group 'done', the last (oldest) item will be deleted. I need an index to do that. However, I do not know how to get to the index for the last item.
What's more, I found this method in MSDN:
List.lastIndexOf
The example is:
var number = list.lastIndexOf(searchElement, fromIndex);
I think this is the answer but I have no idea how to use this method.
This is my HTML code for the ListView:
<div class="appViewContentMask">
<div class="appViewContent">
<div id="gridViewToDo" data-win-control="WinJS.UI.ListView" data-win-options="{
itemTemplate:select('#toDoTemplate'),
groupHeaderTemplate: select('#headerTemplate'),
layout:{type:WinJS.UI.GridLayout},
itemsDraggable : true,
itemsReorderable : true}"></div>
</div>
</div>
I wire up the data with Javascript:
Thank you for reading. I hope my question is clear enough. If you need more information, tell me and I will post more.
One way to be able to delete the oldest finished task is to add finishTime into the item and have it part of the sorting key . That way - the oldest item can be found at the end of the list. Sorting based on this key needs to be done in the createSorted projection.
Thereafter createGrouped projection needs to be used to group the items and order the groups.
The item is to be modified and removed in the iteminvoked handler.
For full details and code listing, refer here.
You can use the splice method (http://msdn.microsoft.com/en-us/library/windows/apps/hh700810.aspx) on the List which operates like that of an array. Once you find the index of the item you would like to remove you simply say:
groupedSortedListProjection.splice(index, 1);
In order to find the index you can ask the group, or in your case since you know the 'done' group is second and is the last group in the collection then the last element in that group will also conveniently be the last item in the collection, do your code is:
var index = groupedSortedListProjection.length - 1;
groupedSortedListProjection.splice(index, 1);
However, if it you didn't have that invariant and had to instead find the right group the code would look something like:
var groups = groupedSortedListProjection.groups;
var group = {}; // start with an empty object to make the for loop condition simpler
for (var i = 0; i < groups.length && group.key !== "done"; i++) {
group = groups.getItemAt(i);
}
var index = group.firstItemIndexHint + group.groupSize - 1;
groupedSortedListProjection.splice(index, 1);
Hope that helps.
-josh

Categories