V-Model not cleansed when adding Object to array (Vue-Js) - vue.js

I'm trying to add objects (simple key-value pairs) to a list.
However, the v-model is still bound to the previously added objects, so if I add "ObjectOne" vith "ValueOne", then try to add "ObjectTwo" with "ValueTwo", "ObjectOne" gets edited AND "ObjectTwo" gets added.
I am by no mean an expert in Javascript, so it might not be related to VueJS.
I can obviously make this work with a method per list .
The point is that my model has multiple lists of key value pair to be edited, so I tried making a generic method :
addToList: function(value, list){
console.log("Adding " + value + " to list "+list);
list.push(value);
value={};
},
This method works if used on "simple" lists (like an array of string), but not on "objects" list.
My guess is that as I try to clean "value" instead of "this.value", the reference still points to the same object, but since I don't know what "value" will be when called, I don't know how to do this.
Here is the fiddle with a re-creation of my issue.
My objective would be to be able to use the "addToList" function to add to any list, without having to re-write a function for each list.
Thank you for your help.

The above behaviour is because you are updating the value of same object whenever you add a new todo task.You need to set your object again to add new values as below.
addToList(value, todos){
this.todos.push(value);
this.anotherTodo={ text:'',
done:'false'}
}
Working fiddle here.

I post this as an answer, but if someone has a better way to do it, I'm all hear.
I solved the way by adding a watch on my list. When the list changes, I clean the model object that's added to it.
In my production work, I had to add a computed property, since I can't add a watch on an object's property, then a watch on said computed property :
watch:{
todos(){
this.anotherTodo={};
},
fiddle as demo

Related

How can I observe any change (property added, removed or changed) in a mobx observable map?

class FilterCriteria {
#observable filter = new Map();
}
let criteria = new FilterCriteria ();
// setting up a reaction when something in the filter changes
// (property added, removed, or changed)
reaction(()=>criteria.filter, data => console.log(data.toJSON()));
criteria.filter.set('name', 'John'); // setting a new property.
I would expect the above code to print out { 'name': 'John' }, but it seems that the reaction is not running.
I suspect that I set up the reaction in the wrong way. I want to react whenever a new key is added, an existing key is removed or a key value is changed. I don't know the keys or values at compile time.
How am I supposed to do that?
UPDATE
I changed my code to
class FilterCriteria {
#observable filter = new Map();
#computed get json(){ return this.filter.toJSON(); }
}
...
reaction(()=>criteria.json, data => console.log(data));
and now it seems to work properly. The reaction sideffect is executed whenever I add, remove or change a value in the Map.
So the question is why the reaction did execute in the second but not in the first example?
UPDATE 2
I changed my code again for a second time. I reverted to almost the first version but this time instead of reacting on criteria.filter and logging data.toJSON(), i react on criteria.filter.toJSON() and I log data (toJSON is moved from the sideffect to the value being watched). This time the reaction runs normally.
class FilterCriteria {
#observable filter = new Map();
}
reaction(()=>criteria.filter.toJSON(), data => console.log(data));
Again, I don't understand why. If criteria.filter is not an observable in itself then how does the watched expression is reevaluated when something inside criteria.filter is changed?
UPDATE 4 (hope the final one) SOLUTION
According to MobX documentation, mobx reacts to any existing observable property that is read during the execution of a tracked function.
reaction side-effect executes when the observable property changes. In my example, when reacting to criteria.filter , the observable property that is read here is filter, but the filter itself never changes. It is the same map always. It is the properties of filter that change. So the reaction is never run for criteria.filter.
But when I react on criteria.filter.toJSON() or mobx.toJS(criteria.filter), the reaction is executed correctly.
So why is that? criteria.filter doesn't change, and toJSON is not an observable property. It is a function. same for mobx.toJS. It seems no properties are read here. But this is not correct. As the documentation states (but not so emphatically), the properties of criteria.filter are indeed read when toJSON or mobx.toJS is executed, because both functions create a deep clone of the map (thus iterating over every property).
Now, in the beginning, the Map did not contain any property. So how is it that newly added properties are tracked, since they did not exist (to be read) when tracking begun? This is a map's feature. Maps provide observability for not yet existing properties too.
In MobX 5 you can track not existing properties of observable objects (not class instances) too, provided that they were instatiated with observable or observable.object. Class instances don't support this.
In mobx you have two options when you want to observe changes to something that is observable. reaction and observe. Reaction allows you to specify when you want some function to be called when a specific aspect of the observable changes. This could be changes to an array length, keys, properties, really anything. observe will trigger some function any time that the observable has changed.
I suspect the reason that your reaction hasn't been triggered is because of the first function. () => criteria.filter. This will not be triggered when a key is added/removed or a value changed. Instead, it will be triggered when filter actually changes. And since filter is really a reference to the Map, it will never change, even when the Map itself changes.
Here are some examples to illustrate my point:
If you want to trigger a reaction when a key has been added or removed, you may want your function to be:
() => criteria.filter.keys()
The result of this function will be different when a key has been added or removed. Similarly, if you want to trigger a reaction for when a value has been modified, something like this should work:
() => criteria.filter.values()
So some combination of those two should be what you need to listen to changes to keys/values. Alternatively, you could use observe, which will trigger on every change and require you to check what has changed to ensure that your specific conditions have been met to warrant calling a function (ie. key/value change)
UPDATE: Here is an example that illustrates the problem
#observable map = new Map();
Lets say that the value of map in memory is 5. So when you check map === map, it is equivalent to 5 === 5 and will evaluate to true.
Now, looking at the first code snippet you posted:
reaction(() => map, data => console.log(map.toJSON()));
Every time you add/remove a key or change a value, that first function will run. And the result will be 5, since that is what we said the value in memory is for this example. It will say: the old value is 5, and the new value is 5, so there is no change. Therefore, the reaction will not run the second function.
Now the second snippet:
reaction(() => map.toJSON(), data => console.log(data));
At first the result of the function will be: {} because the Map is empty. Now lets add a key:
map.set(1, 'some value');
Now, the result of the first function will be:
{"1": "some value"}
Clearly, this value is different than {}, so something has changed, and the second function of the reaction is called.

Dynamic Object Computed Values are not updating

I am having trouble with a computed property within dynamically populated array of objects.
I have an array of customers which are filled dynamically by the user in the form. The user can add as many customers as he likes.
Each customer has a price field calculated based on some other values in the object. Additionally, the price for each customer is based on some generic settings that apply to all customers.
Therefore I have a computed function which should update the price for each customer. The function starts like this:
this.booking.customers.forEach(function(customer) {
var price = 0;
self.booking.packages.forEach(function(package) {
... // based on the packages the price variable is changing
}
customer.price = price;
}
The calculation works - because if I open the vue devtools the value is suddenly applied, and then Vue also recognizes the dependencies (as soon as I change some value all prices adapt accordingly).
So my problem is: initially, Vue doesn't update the property correctly. As soon as I open devtools once, from that moment on it adapts accordingly.
Maybe I am not using computed properties in the right way, but using a method is also not what I need because I want the price value to change as soon as any dependency is changing.
Hope the problem is understandable. Thanks in advance for your help.
EDIT:
Here is a fiddle which maybe highlights the problem better. I think I am misusing computed values, but I have no idea how to tell Vue that the prices need to change whenenver some other values change.
http://jsfiddle.net/6bw50j9d/2/
ANOTHER EDIT:
I was able to solve my problem using deep watchers, I found this solution in another post. See Vue.js - How to properly watch for nested properties

Does NSIncrementalStoreNode updateWithValues's "values" argument require only the changed values or a complete new copy of the data?

I'm calling:
- (void)updateWithValues:(NSDictionary *)values
version:(uint64_t)version
in an NSIncrementalStore subclass in order to update the cache with update NSManagedObject values. My question concerns the values argument. Do I only need to put in the updated attributes or a complete new copy of the data?
The description in the documentation says: "Update the values and version to reflect new data being saved to or loaded from the external store. // The values dictionary is in the same format as the initializer."
It isn't clear to me whether or not the "values" that "reflect the new data" refers to only the updated attributes or all the attributes in the object.
It requires the complete data. I agree it wasn't very clear but I suppose the reason is so you can do the conflict handling first. The annoying thing is there is no way to get the values back from the node to merge in the new ones and set them again. Annoyingly this means you can't use the node as your cache object, I'm still learning the NSIncrementalStore so likely there reason for this design will come clear at some point.

List View Item Deleting parameters not passed to Object Data source

I have a ListView bound to an ObjectDataSource, I'm passing some custom parameters to the Insert and Update methods on my bound class methods by adding them to the event.Values map in the ListView ItemInserting/ItemUpdating events.
However when I try to do the same thing on the ItemDeletingEvent the additional parameters do not seem to be passed to the datasource ( If I register a listener for ObjectDataSource.ItemSourceDeleting I only see one parameter, effectively the 'id' of the row).
Is this an expected behavior? I can't see anything in the documentation that indicates as such.
I found a solution -
I Added a 'DeleteParameter' value with the same name as my desired 'custom' parameter to the ObjectDataSource declaration.
Then in the ItemDeleting Event get the ObjectDataSource.DeleteParameters["myparam"] and set the DefaultValue property. Seems like a hack, but it does work.

DoJo get/set overriding possible

I don't know much about Dojo but is the following possible:
I assume it has a getter/setter for access to its datastore, is it possible to override this code.
For example:
In the dojo store i have 'Name: #Joe'
is it possible to check the get to:
get()
if name.firstChar = '#' then just
return 'Joe'
and:
set(var)
if name.firstChar = '#' then set to #+var
Is this sort of thing possible? or will i needs a wrapper API?
You can get the best doc from http://docs.dojocampus.org/dojo/data/api/Read
First, for getting the data from a store you have to use
getValue(item, "key")
I believe you can solve the problem the following way. It assumes you are using a ItemFileReadStore, you may use another one, just replace it.
dojo.require("dojo.data.ItemFileReadStore");
dojo.declare("MyStore", dojo.data.ItemFileReadStore, {
getValue:function(item, key){
var ret = this.inherited(arguments);
return ret.charAt(0)=="#" ? ret.substr(1) : ret;
}
})
And then just use "MyStore" instead of ItemFileReadStore (or whatever store you are using).
I just hacked out the code, didn't try it, but it should very well show the solution.
Good luck
Yes, I believe so. I think what you'll want to do is read this here and determine how, if it will work:
The following statement leads me to believe the answer is yes:
...
By requiring access to go through
store functions, the store can hide
the internal structure of the item.
This allows the item to remain in a
format that is most efficient for
representing the datatype for a
particular situation. For example, the
items could be XML DOM elements and,
in that case, the store would access
the values using DOM APIs when
store.getValue() is called.
As a second example, the item might be
a simple JavaScript structure and the
store can then access the values
through normal JavaScript accessor
notation. From the end-users
perspective, the access is exactly the
same: store.getValue(item,
"attribute"). This provides a
consistent look and feel to accessing
a variety of data types. This also
provides efficiency in accessing items
by reducing item load times by
avoiding conversion to a defined
internal format that all stores would
have to use.
...
Going through store accessor function
provides the possibility of
lazy-loading in of values as well as
lazy reference resolution.
http://www.dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/what-dojo-data/dojo-data-design
I'd love to give you an example but I think it's going to take a lot more investigation.