Ember data custom arrays push and delete - javascript

I was implementing an array for my ember data property
DS.JSONTransforms.array = {
serialize: function(value) {
return Em.isNone(value) ? [] : value ;
},
deserialize: function(value) {
return Em.isNone(value) ? [] : value ;
}
};
And I created this jsbin for test to add and remove items to the array http://jsbin.com/avENazE/4/edit
If I check the console
model.get('pages').push('hi');
console.log(model.get('pages'));
I can see that the new items are corectly add to the array, but are not displayed on the view.
Also the count property is not updated and this error shows on the console on save the model
Uncaught TypeError: You must pass a resolver function as the sole argument to the promise constructor

The make the view be aware of changes of the representing model data you need data binding to work properly. To get data binding to work properly you need to use the correct functions that are sensible to bindings, so in the case of operations done to an array you can't just use vanilla push but instead pushObject or the counterpart removeObject, the same applies for setting a new value to a property, while dot notation will work it will not update you bindings therefore .set() and .get() need to be used etc.
So that said, here your working jsbin.
Hope it helps.

Related

javascript: Update property value in Array() with index as parameter

Currently I've a react function that removes from a Array called rents the current rent perfect. The issue is that I need to update the rent row value called status and set property from 1 to 4 the code below works. I don't seem to get how to get the Index of rent to be able to update it.
removeItem (itemIndex) {
this.state.rents.splice(itemIndex, 1) // removes the element
this.setState({rents: this.state.rents}) // sets again the array without the value to the rent prop
console.log(itemIndex) // itemIndex
}
currently I'm adding this to the code to debug but get this error
console.log(this.state.rents[itemIndex].unique_key)
Stack Trace
TypeError: Cannot read property 'unique_key' of undefined
I need to be able to update the rent property value called status from 1 to 4 and setState again
To elaborate the comments, starting first with the most important:
Like #devserkan said, you should never mutate your state (and props), otherwise you start to see some really weird hard-to-make-sense bugs. When manipulating state, always create a copy of it. You can read more here.
Now for your question:
this.setState is asynchronous, so to get your state's updated value you should use a callback function
const rents = [...this.state.rents]; // create a copy
rents.splice(itemIndex, 1);
this.setState({ rents }, () => {
console.log(this.state.rents); // this will be up-to-date
});
console.log(this.state.rents); // this won't
Personally, I like using the filter method to remove items from the state and want to give an alternative solution. As we tried to explain in the comments and #Thiago Loddi's answer, you shouldn't mutate your state like this.
For arrays, use methods like map, filter, slice, concat to create new ones according to the situation. You can also use spread syntax to shallow copy your array. Then set your state using this new one. For objects, you can use Object.assign or spread syntax again to create new ones.
A warning, spread syntax and Object.assign creates shallow copies. If you mutate a nested property of this newly created object, you will mutate the original one. Just keep in mind, for this situation you need a deep copy or you should change the object again without mutating it somehow.
Here is the alternative solution with filter.
removeItem = itemIndex => {
const newRents = this.state.rents.filter((_, index) => index !== itemIndex);
this.setState({ rents: newRents });
};
If you want to log this new state, you can use a callback to setState but personally, I like to log the state in the render method. So here is one more alternative :)
render() {
console.log( this.state.rents );
...
}

Ember - dynamically pass array of dependent keys to computed property

I would like to create a component that takes a list of property names and then dynamically builds a computed property which has each value from the list as a dependent key.
{{buttons-list-component
title="Status"
propertyNames="active, expired, pending"
toggleProperty="toggleProperty"
active=active
expired=expired
pending=pending
}}
For the above example, I would like to to take the string passed as propertyNames, split it into an array and then have the optionsObject computed property watch each value in the array as if it was passed explicitly.
propertiesArray: function() {
return this.get('propertyNames').split(", ");
}.property('propertyNames'),
When the property active is updated, the code below will not run, because the propertyNames has not been updated, and therefore propertiesArray has not changed.
optionsObject: function() {
console.log("foo") // Does not run.
}.property('propertiesArray.#each'),
Is there a way to build the computed property, so that it would work in the same way as the code below, but without explicitly passing the string value of each property that optionsObject is dependant on?
optionsObject: function() {
console.log("bar") // Does run.
}.property('active', 'expired', 'pending''),
Your propertyNames is being passed a constant string propertyNames="active, expired, pending" -- so to update propertyNames when active changes, pass a computed property to propertyNames which is calculated based on the three properties.
propertyString: function() {
return `${active} ${active} ${active}`
}.property('active', 'expired', 'pending'),
so now when propertyString updates, your propertyNames will update, and that will trigger propertiesArray.
One more thing to note here, you need to observe propertiesArray.[] not #each -- #each is used when observing a child property.
Also, you should use the new computed property format - https://guides.emberjs.com/v2.18.0/object-model/computed-properties/#toc-toggle explains the two points I mention well

VueJS data initialization for non existent objects

I am using Ajax to populate data properties for several objects. As such, the properties I want to bind to do not exist at the time of binding.
eg:
<template>
<my-list v-bind:dataid="myobject ? myobject.data_id : 0"></my-list>
</template>
<script>
export default {
data () {
return {
myobject: {}
}
}
</script>
In the Vue docs https://012.vuejs.org/guide/best-practices.html it mentions to initialize data instead of using a empty object.
However I am using multiple Ajax created objects with tens of parameters and sub parameters. To initialize every sub-parameter on all objects like this:
myobject: { subp1: [], subp2: [] ...}
where myobject may be an object containing array of objects, or an array of objects containing sub-arrays of objects for example.
would take quite a bit of work. Is there a better alternative when binding to 'not-yet existing' objects?
First of all, an empty array is still "truthy", so your check here
v-bind:dataid="myobject ? myobject.data_id : 0"
always returns true. You should check for myobject.length instead. Your code should work now.
Also, you really don't need to define dummy objects for an array. Vue detects whenever you mutate an array.
https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection

Adding a property to an Angular object

I am running an Angular app that pulls its data from a Web API service. The API returns the objects as JSON and the Angular service (via $http.get() ) returns them to the controller as an array of objects. Pretty normal stuff.
What I'd like to do is add a property to each of the returned objects called "Selected". This property is purely for GUI purposes (is the object selected or not in the UI) and doesn't need to be persisted in any way. I figured the easiest thing to do was loop through the returned array of objects and just add it. So my code looks like this:
function getData() {
myService.getData()
.then(function(data) {
$scope.myData = data.results;
// Add a "Selected" property to each object
$.each($scope.myData, function(item) {
item.Selected = false;
});
}
When it gets to the line that says, "item.Selected = false" it throw an error message, saying "Cannot assign to read-only property Selected".
It is unclear to me why "Selected" is read-only? I didn't know if maybe Angular does some funky object processing when it reads data? What am I doing wrong here? Or should I be approaching this a completely different way?
Note (I'd like to avoid having to make Selected a part of the original object, as it's not representative of anything related to that object).
to add property to an object use underscorejs,
"each _.each(list, iteratee, [context]) Alias: forEach
Iterates over a list of elements, yielding each in turn to an iteratee function. The iteratee is bound to the context object, if one is passed. Each invocation of iteratee is called with three arguments: (element, index, list). If list is a JavaScript object, iteratee's arguments will be (value, key, list). Returns the list for chaining."
"extend _.extend(destination, *sources)
Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments."
$scope.myData = data.results;
// Add a "Selected" property to each object
_.each($scope.myData, function(ent) {
_.extend(ent, {
Selected : false
});
});
Your debugger screenshot actually gives you a more useful error message than what you posted (emphasis mine):
Cannot assign to read only property 'Selected' of 0
This shows that instead of the object, you're getting a number as your item variable (0 in this case). Assigning properties to primitives in strict mode throws this "Cannot assign to read-only property" error. (Without strict mode, it just fails silently.)
As JcT pointed out in a comment, this is because $.each calls the function with 2 params passed, the index first, the value second. See the documentation of $.each:
callback
Type: Function( Integer indexInArray, Object value )
So even though you named your parameter item, it received the value of the current index instead. This means your code can be fixed by just adding this missing first parameter to your callback, like this:
function getData() {
myService.getData()
.then(function(data) {
$scope.myData = data.results;
// Add a "Selected" property to each object
$.each($scope.myData, function(index, item) { //index was added here!
item.Selected = false;
});
}

Get object property value

I have an object that contains an array of objects from which I need to get a value of their properties.
As an example this is what I need to get:
Stronghold.bins.models[0].attributes.entity.title
Which returns "Stronghold Title 1"
function grabItemName(){
var itemName=$(Stronghold.bins).each(function(){
return this.models[0].attributes.entity.title == title;
console.log(itemName);
})
};
(if there is a better way for me to ask this question please let me know)
I apologize if this was poorly asked!
The current issue is that it does not understand the array value '[0]' and cannot read it as it is undefined. What do I need to do to grab the 'title' value of all items in the array?
What do I need to do to grab the 'title' value of all items in the array?
That's what .map [docs] is for. It lets you map each value in an array to another value.
In the following I assume you want to iterate over each Stronghold.bins.models, because iterating over Stronghold.bins does not make sense with the provided information:
var titles = $.map(Stronghold.bins.models, function(obj) {
return obj.attributes.entity.title;
});
// `titles` is now an array containing `.attributes.entity.title;` of
// each object.
The current issue is that it does not understand the array value '[0]' and cannot read it as it is undefined.
Well, that won't happend anymore ;) In your example you where iterating over the properties of the Stronghold.bins object. One of these properties is models itself (!) and I doubt that any other property value has a models property.
Try using the other version of the each function:
$.each(Stronghold.bins, function () {
});
The version you are using is for looping through an element on the page, e.g $('body div p').each(function() {}), which isn't what you want in this instance: You want to loop over the values contained in Stronghold.bins.

Categories