How do you remove the last model in a Collection?
if (this.Collection.models.length < newValue) {
console.log('adding');
this.Collection.models.add();
} else {
console.log('removing');
// this isnt working, by selecting it by the current collection array index
// basically this.Collection.remove([this.Collection.models.length-1])
//this.measuresCollection.models.get('beats').remove(this.measuresCollection.models[i].get('beats')[this.measuresCollection.models[i].get('beats').length]);
}
I don't know the id, so I would just like to remove the last one.
collection.pop([options])
Doc: Collection#pop.
It is below the remove method in the docs. It both removes the last model, and also returns it to you if you need it. If you just want to remove it, don't use the return value.
Related
EventEmitter in service
toshoppinglist = new EventEmitter<Ingredients[]>()
Emitting method
toshoppinglist() {
this.slservice.toshoppinglist.emit(this.item.ingredients);
}
ingredients : Ingredient []
Subscribing to emit and pushing emitted values
this.slservice.toshoppinglist.subscribe(
(ingredients: Ingredients[]) => {
for (let item of ingredients) {
this.ingredients.push(item);
}
}
)
Now, when pushing new values into the array,it's getting duplicated.It's work fine for first pushing,but getting duplicated after that.
Ok, first in my opinion you use wrongly EventEmitter. Eventemitters used only inside DUMB components to raise an event to Smart component. And not inside services. Second, yes it will be duplicated. Imagine you have button which will raise this eventemitter and we emit the same ingredient every time. Inside the subscribe you didnt check that new ingredient are different. And because you use list, it doesnt care if you have duplicates. So a solution is to add to the subscribe a check that it will push only - new- ingredients.
You need to add a check in your subscription function.
currentIngredients: Ingredients[];
this.slservice.toshoppinglist.subscribe(
(ingredients: Ingredients[]) => {
> //here you need the if condition comparing it to a current
> ingredients array to see if it exists or not
for(let item of ingredients) {
if(this.currentIngredients.includes(item) {
// in this case you should increment the count of ingredient
currentIngredients[item].count++;
}
else {
// this should only add non-existing items ingredients list
this.currentIngredients.push(item)
}
}
}
I know it's been asked million+1 times. But i've found no help in those questions/answers.
I have 2 arrays of 2 different objects one string property is used to uniquely identify them. This would be the key to sort about, but said object prop names are not equal (accessValue, modifiedOption). But their values are!
Object1: { ... accessValue, ... };
Object2: { ..., modifiedOption, ... };
array1:Object1[];
array2:Object2[];
I'd like to sort array1 based on the object indencies of array2.
So all of array1 items'd be in the same order as array2.
These two arrays are used to model a connected dropdown selection system, which can be added to are removed from. The Addition is screwing me over (lastly added item is appended to the first place and not the last) probably because of filter below?
What I use to add new dropdowns:
addFieldRow() {
this.fieldRows.push(0); // since desired selection is not known yet but need to populate the array that represents the 1st selection so a 2nd main selection dropdown will appear on screen
...
}
public onSelect() {
// if a selection is happened check the values of editOptions (contains data about all main selectable options)
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
this.disableSelected(); // disable already selected items (not related to my issue)
this.optionSelected = true; // this is just for button disabled toggle
}
So either i need to figure out my addRow logic (if it has any flaws) or implement a sorting utility to make sure that the objects of fieldRows are in the same order as selectedOptions' -> since this models the selection directly.
I cannot really add a stackblitz since it's hard to model my current state.
Okay I am a complete idiot!
Since I know the current index (since i am looping through fieldRows).
All I had to do is replace this:
public onSelect() {
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
With this:
public onSelect(index) {
this.fieldRows[index] = this.editOptions.find(option => this.selectedOptions[index].modifiedOption === option.accessValue);
this.disableSelected();
this.optionSelected = true;
}
Now it works correctly.
In my application I hava combobox which is holding some values from databse ( some real time updated list ). This ComboBox list is updated every 1 minute.
List dosen't have null values. When I'm setting this list to ComboBox..
ComboBox box = new ComboBox(items);
.. there is one extra "empty" row, which is perfectly fine because non value is selected.
Right after I'm selecting some value my "empty" value disappears from the list.
My main question is How to keep this value on the list?
Why this is a problem..
Scenerio values is selected in database, first application start
List is loaded ( wiht selected empty value ).
Value is selected.
During first background refresh, empty values disappears, and combobox value is selected to n+1, next value is selected.
If I want to select empty values I have to all clearSelection from selection model / by some extra control.
While it is an old question, I spent quite bit of time trying to solve the same issue in my application and thought i might as well add my solution here.
One possible workaround is to create an extra list that contains null and the items you wish to be selectable.
ObservableList<String> selectableActivities = FXCollections.observableArrayList("a", "b", "c");
ObservableList<String> selectableActivitiesWithNull = FXCollections.observableArrayList();;
selectableActivitiesWithNull.add(null);
selectableActivitiesWithNull.addAll(selectableActivities);
In order to support updates of the original list you would need a ListChangeListener that updates the extra list according to changes in the original list.
selectableActivities.addListener((ListChangeListener<String>)(change -> {
while (change.next()) {
if (change.wasPermutated()) {
selectableActivitiesWithNull.sort((a, b) -> {
return Integer.compare(selectableActivities.indexOf(a), selectableActivities.indexOf(b));
});
} else if (change.wasRemoved()) {
selectableActivitiesWithNull.remove(change.getFrom()+1, change.getTo()+2);
} else if (change.wasAdded()) {
selectableActivitiesWithNull.addAll(change.getFrom()+1, selectableActivities.subList(change.getFrom(), change.getTo()));
} else if (change.wasUpdated()) {
for (int i = change.getFrom(); i < change.getTo(); ++i) {
selectableActivitiesWithNull.set(i+1, selectableActivities.get(i));
}
}
}
}));
And finally you use the extra list for the ComboBox items.
ComboBox<String> combobox = new ComboBox<String>();
combobox.setItems(selectableActivitiesWithNull);
Now you can modify the original list as usual and the ComboBox will update accordingly while also having an empty selection as the first item. And most importantly your original list will not be polluted by placeholder objects that could cause issues in other parts of the application.
This will also work with other objects, assuming that you add an apropriate StringConverter to the ComboBox. Note that the converter must also be able to handle null values if using the above approach.
StringConverter<Object> activityConverter = new StringConverter<Object>() {
#Override
public String toString(Object object) {
return object != null ? object.toString() : "";
}
#Override
public ActivityDataRow fromString(String string) {
return null;
}
};
combobox.setConverter(activityConverter);
While this approach is not exactly what you desired, I believe this is a close you can get without implementing a custom combobox.
TL;DR: If I'm polling the entire collection of models from the server, how can I merge the changed attributes into each model and add/remove added/removed models from the collection?
In my backbone app, I am polling for the entire collection of models. I have a Backbone.Collection that I am basically calling reset on each time I get the array of models, so:
myCollection.reset(server_response);
The only problem with this is that it gets rid of the old models, kind of eliminating the benefit of events on a model. This is reset's purpose of course, but what I want to do is only modify the changed attributes of the models, and remove models not in the response, and add models that were in the response but not the collection.
Essentially I want a sort of merge of the data.
For models that are in the response and in the collection already, I believe I can just do model.set(attributes) and it takes care of seting only the ones that actually changed, triggering the change events in the process. This is great.
But how do I handle the cases where the models were in the response but not in the collection already, and vice versa, not in the response but in the collection?
My Proposed Solution
I don't know if backbone already has a way of doing this, and I may be overcomplicating which is why I'm asking, but I was thinking then of creating a method on my collection which gets passed the server_response.
It would get all of the id attributes of the server_response, and all of the id attributes of the models already in the collection.
The difference of the id's in response - collection would = added models, and vice versa would be removed models. Add and remove those models respectively from the collection.
The intersection of both sets of id's would be the modified models, so iterate through these id's and simply do a collection.get(id).set(attributes).
In pseudocoffeescript:
merge: (server_response) =>
response_ids = _.pluck(server_response, 'id')
collection_ids = #pluck('id')
added = _.difference(response_ids, collection_ids)
for add in added
#add(_.find(server_response, (model) ->
return model.id == add
))
removed = _.difference(collection_ids, response_ids)
for remove in removed
#remove(#get(remove))
changed = _.intersection(response_ids, collection_ids)
for change in changed
#get(change).set(_.find(server_response, (model) ->
return model.id == change
))
This technique is useful sometimes. We extend Collection with the following method. This should do what you're looking for. It's not in coffee, but you could easily port it. Enjoy!
// Take an array of raw objects
// If the ID matches a model in the collection, set that model
// If the ID is not found in the collection, add it
// If a model in the collection is no longer available, remove it
freshen: function (objects) {
var model;
// Mark all for removal
this.each(function (m) {
m._remove = true;
});
// Apply each object
_(objects).each(function (attrs) {
model = this.get(attrs.id);
if (model) {
model.set(attrs); // existing model
delete model._remove
} else {
this.add(attrs); // new model
}
}, this);
// Now check for any that are still marked for removal
var toRemove = this.filter(function (m) {
return m._remove;
})
_(toRemove).each(function (m) {
this.remove(m);
}, this);
this.trigger('freshen', this);
}
This just went into Backbone master 3 days ago:
https://github.com/documentcloud/backbone/pull/1220
I have a multiselect grid where I can get schools.getSelectionModel().getSelection();
there is an object called data, I want to get a field within the data; lets say school_name
How I'll do it?
I've tried
schools.getSelectionModel().getSelection().data
schools.getSelectionModel().getSelection(data)
schools.datagetSelectionModel().getSelection()
they did not work.
You have to use Ext.each to iterate over the array of records..
Ext.each(schools.getSelectionModel().getSelection(), function(record, index, allRecords) {
console.log(record.get('school_name');
});
This:
schools.getSelectionModel().getSelection()[0].get('school_name')
should give you a 'school_name' field from first row selected (which is also a first record in selection).
To iterate over all selected rows do:
var selectedSchools = schools.getSelectionModel().getSelection();
for (i in selectedSchools) {
console.log(schools[i].get('school_name')); //this will log school name to firebug console - you can do whatever you need
}