My model in the server-side has an attribute which is an array of objects. In the client-side I'm trying to fetch that model in a backbone's model.
After fetching the model from my server I get this object:
{
round: 17,
username: bob,
football_bets: [
0: { one: true, x: false, two: false },
1: { one: false, x: false, two: true },
2: { one: true, x: true, two: false },
]
}
However, I was expecting something like this:
{
round: 17,
username: bob,
football_bets: [
{ one: true, x: false, two: false },
{ one: false, x: false, two: true },
{ one: true, x: true, two: false },
]
}
I understand that this is something related with how backbone parses the result of a fetch action, but I don't know how I should deal with.
Should I override the parse method of the model to get the result as I expect?
Should I use collections instead of trying to model everything inside of a single model?
Should I use some third-party library to deal with nested objects?
I really appreciate any kind of suggestions!
The format google-chrome console adopts for arrays is like the first array I've posted in the question. I was thinking that the numbers appears before the object itself was something that backbone it was adding when you perform a fetch action ... I was thinking this because when I debugged my code I had an error that said me it was not possible to access to an attribute.
After more debugging I realize that there was an indexation problem trying to access to an undefined element ... So the conclusion of all this mess is how the google-chrome console formats arrays! Hope this helps somebody ...
Related
I might be experiencing either a bug, or I misunderstand something about general javascript syntax.
Using ServiceNow UI Builder, I'm trying to refresh the datasource of a specific data visualization component. Which requires me to use setState and send in an entire JSON blob.
The following works as expected:
api.setState('intAssignedDonut', {
"header": "Interactions assigned to one of my groups",
"datasource": [{
"isDatabaseView": false,
"allowRealTime": true,
"sourceType": "table",
"label": {
"message": "Interaction"
},
"tableOrViewName": "interaction",
"filterQuery": "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744",
"id": "intAssigned",
"elm": {}
}],
"metric": [{
"dataSource": "intAssigned",
"id": "intAssignedMetric",
"aggregateFunction": "COUNT",
"numberFormat": {
"customFormat": false
},
"axisId": "primary"
}],
"groupBy": [{
"maxNumberOfGroups": "ALL",
"numberOfGroupsBasedOn": "NO_OF_GROUP_BASED_ON_PER_METRIC",
"showOthers": false,
"groupBy": [{
"dataSource": "intAssigned",
"groupByField": "state",
"isRange": false,
"isPaBucket": false
}]
}]
});
However, I only need to alter a few properties, not the whole thing.
So I thought I'd just clone the thing into a temp object, change what I need, then pass the cloned object back.
let clientState_intAssignedDonut = api.state.intAssignedDonut;
clientState_intAssignedDonut.header = 'Interactions assigned to one of my groups';
clientState_intAssignedDonut.datasource[0].filterQuery = 'active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744';
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This seems to update the header properly, but the component doesn't refresh the datasource.
Even if I console.log api.state.intAssignedDonut it looks identical to the whole JSON blob.
EDIT: I also tried using spread operators, but I can't figure out how to target the datasource[0]
api.setState("intAssignedDonut", {
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource[0].filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744"
});
Javascript objects are passed by reference values, and react state is immutable:
let clientState_intAssignedDonut = api.state.intAssignedDonut;
api.setState("intAssignedDonut", clientState_intAssignedDonut);
This is mutating the state directly, and React will ignore your update if the next state is equal to the previous state, which is determined by an Object.is comparison to check if both objects are of the same value, see docs
Your second attempt is heading to the right direction using spread operator:
Update method one: first copy the nested object using: JSON.parse(JSON.stringify(object)), or you can find other method in this question: What is the most efficient way to deep clone an object in JavaScript?
let copied = JSON.parse(JSON.stringify(api.state.intAssignedDonut)); // copy a nested object
copied.header = "Interactions assigned to one of my groups";
copied.datasource[0].filterQuery = "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744";
setState("intAssignedDonut",copied);
Update method two:
setState("intAssignedDonut",{
...api.state.intAssignedDonut,
header: "Interactions assigned to one of my groups",
datasource: [{ ...state.datasource[0], filterQuery: "active=true^assignment_groupDYNAMICd6435e965f510100a9ad2572f2b47744" }]
});
Check out sandbox
I am trying to mock my EF Core API using Mirage but cannot figure out how to serialize my output. The front end is expecting something like:
[{id:1, name:"name1", weight: 150}, {id:2, name:"name2", weight: 140}]
but Mirage is returning something like:
data: {[{id:1, name:"name1", weight: 150}, {id:2, name:"name2", weight: 140}]}
I can explicitly return just the hardcoded array in the route handler and it works, but I want to be able to use Factories, Models, and Serializers to achieve this. What am I missing?
Thanks!
I solved this by using the root attribute to false in the serializer. By setting root to false, the result will not be rooted inside of an object. Note that I think embed must be set to true if you are going to do this as it is defaulted to false. The solution will look something like this:
createServer({
serializers: {
person: RestSerializer.extend({
embed: true,
root: false,
}),
},
//... rest of createServer
})
Inside my jenkinsfile I want to set multiple properties based on some dependencies.
So in the top of my jenkinsfile I am setting my first parameter:
properties([
parameters([
booleanParam(
defaultValue: false,
description: '...',
name: 'parameters1'
),
])
])
Some lines below I want to set another parameter if a condition is met
if(awesomeCondition) {
properties([
parameters([
booleanParam(
defaultValue: false,
description: '...',
name: 'parameters2'
),
])
])
}
The problem I am now running into is that the second parameter is overriding the first parameter. How to handle this correctly?
The properties step overrides the existing job properties so, as you noted, the second call overrides the previous one. This is expected behaviour.
What you need to do is to keep a list of new parameters and then use a single call to properties step:
def newParameters = []
newParameters.add([
$class: 'hudson.model.BooleanParameterDefinition',
name: "p1",
default: false,
description:"Some help text"
])
...
if(awesomeCondition) {
newParameters.add([
$class: 'hudson.model.BooleanParameterDefinition',
name: "p2",
default: false,
description:"Some help text"
])
}
...
properties([parameters(newParameters)])
The $class: 'hudson.model.BooleanParameterDefinition' is needed since we are creating the objects outside of the properties step. For other types of parameters see sub-classes to this class.
Hello emberjs experts :)
There is something that i don't understand.
Given the following route:
Evibe.MemberShowRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/api/user').then(function(user) {
return Ember.Object.create(user);
});
}
});
The call to the api simply returns a user object containing properties. One of this property is an array of picture objects. Like that:
{
username: "A nice user",
pictures: [
{id: 1, is_main: true, url: 'http://www.test.com/img1.jpg'},
{id: 2, is_main: false, url: 'http://www.test.com/img2.jpg'},
{id: 3, is_main: false, url: 'http://www.test.com/img3.jpg'},
{id: 4, is_main: false, url: 'http://www.test.com/img4.jpg'},
]
}
In my controller, i have something like this:
Evibe.MemberShowController = Ember.ObjectController.extend({
nb_pictures: function() {
return this.pictures.length;
}.property('pictures'),
addPictureObject: function(picture) {
this.get('pictures').addObject(picture);
}
});
And in my template, i have something like this:
{{ nb_pictures }} pictures
I don't understand why nb_pictures is not updated, as i'm adding an object into my "pictures" property with the addPictureObject function.
Also, when i try to do something like this:
this.get('pictures').setEach('is_main', false); // Works
this.get('pictures').findBy('id', pictureId).is_main = true; // Doesn't work
this.get('pictures').findBy('id', pictureId).set('is_main', true) // Doesn't work
The first line works as expected.
But... for the second line, i get the error: "Assertion failed: You must use Ember.set() to access this property (of [object Object])"
And for the third one, i get the error: "Uncaught TypeError: Object # has no method 'set' "
Any ideas that can help clarify this would be greatly appreciated.
In your nb_pictures computed property, you have set the dependent key with property('pictures'), the correct is property('pictures.length').
This is the updated code:
Evibe.MemberShowController = Ember.ObjectController.extend({
nb_pictures: function() {
return this.get('pictures.length');
}.property('pictures.length'),
addPictureObject: function(picture) {
this.get('pictures').addObject(picture);
}
});
Using just property('pictures') will make the framework observe just the array replacement, like set('pictures', [...]), not the changes in the array structure get('pictures').pushObject(...). this the reason that your ui don't update.
I'm trying to access the selectedObs property of this class:
qx.Class.define("edd.view.ObsToggleContainer", {
extend : qx.ui.container.Composite,
type: "singleton",
properties : {
selectedObs : { check: "Array"}
},
construct : function() {...
from another class like this:
var ObsToggle = edd.view.ObsToggleContainer.getInstance();
console.log(ObsToggle.getSelectedObs());
But it seems to not know about what the values are presently set to. Am I doing something wrong or is there some logic that I'm not aware of?
Thanks for any help you can give!
I ended up just making a separate class as just a qx.core.Object and used that instead of a singleton Composite class and it seemed to do what I wanted to accomplish.
qx.Class.define("edd.data.DataStore", {
properties : {
checkedObs: {
init: [false, true, true, false, false, false, false, false, true, false, false, false],
check: "Array"
}
},
extend : qx.core.Object,
type: "singleton",
construct : function() {
var thisClass = this;
},
members :{
}
});
So now, this code properly works:
var dataStore = edd.data.DataStore.getInstance();
var init_checked = dataStore.getCheckedObs();
Uhm. There's something else here. Your initial class definition and the one from your own answer are not far apart, as far as the property is concerned. Maybe in the rest of your initial class, or in the code using it, was something awry?!
Have a look at this Playground sample, which uses your initial class definition. You have to open the "Log" pane, in order to see the output. Works like a charm.
It would be very interesting if you could change this sample, and tweak it until it reproduces your problem?!