Can one add a complex type item to ListModel? - json

I would like to have a ListModel-like structure to display inputs of a simple state machine. Each input might consist of several strings/ints. So I need each item of the ListModel to be able to store a list of data (strings with names of the input's parameters, or dictionaries with strings etc). At the moment I cannot append an item with a list property to a ListModel.
So the ListModel looks like this:
ListView {
anchors.fill: parent
model: ListModel {
id: listModel
}
delegate: Text {
text: inputs[0]['name']
}
}
And when the state changes I want to update the model and append elements like this:
var state = {
name: "abcd",
inputs: [{name: 'a'}, {name: 'b'}, {name: 'c'}]
}
listModel.append(state);
Current version of the code returns error TypeError: Cannot read property 'name' of undefined. It does not see the list.
According to this question there might be issues with using lists in the items of ListModel. But it seems irrelevant to my case. Maybe I need to use lists and dicts differently in QML, maybe I had to write text: inputs[0].name in delegate (which I tried) or something else (suggestions?).
Could someone suggest how to make a more or less complex item (basically, it is standard JSON) in a ListModel? It is not clear, since documentation and blogs/questions deal with strings all the time. Is there some helpful documentation which I missed? What are good practices to do it in QML? Should I use some custom objects?

List data can be added to a ListElement, according to the documentation and as you correctly did in your imperative code. However, nested roles are not really arrays. They are ListModels themselves. That's because, by design, QML does not produce a notification if an element of an array changes, which would be a show-stopper in a model-view-delegate setting.
Since the nested role is a model, you can use model's functions. For instance, this example works fine:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
id: window
width: 600
height: 400
visible: true
ListView {
anchors.fill: parent
model: ListModel {
id: listModel
}
delegate: Text {
text: name + inputs.get(index % inputs.count).name // accessing the inner model
}
}
MouseArea {
anchors.fill: parent
onClicked: {
var state = {
name: "abcd",
inputs: [{name: 'a'}, {name: 'b'}, {name: 'c'}]
}
listModel.append(state);
}
}
}
According to your question, the input is plain JSON. In that case, consider the usage of JSONListModel in place of ListModel. It exposes a set of API which matches XMLListModel, via JSONPath, and could possibly represent the perfect solution for your scenario.

Related

JSON Schema / Formly dependent sub-schemas

This issue is a bit tricky to describe so bear with me and please ask questions if I am missing anything...
Say you have a json object that defines a list of features, each feature has a the same three properties but has a property that has an entirely different structure. For example:
{
features: [
{
id: "feature-a",
enabled: true,
configurationData: {
featureAConfigPropertyA: {
somePrperty: "whatever",
anotherProperty: true
},
featureAConfigPropertyB: "some string"
}
},
{
id: "feature-b",
enabled: true,
configurationData: {
featureBConfigArrayPropertyA: ["some string"],
featureBConfigPropertyB: [
{
"id": "some string",
"name": "some string",
"description": "some string",
"enabled": true
}
]
}
}
]
}
The actual structure of each feature is irrelevant. I am just trying to express this via json schema whereby the structure of configurationData for each feature is dependent on or dictated by the feature id value of its parent.
EDIT: I guess technically it doesnt need to be dependent on so long as either structure of configurationData is valid schema for that property on the feature schema itself. Also, the types in configurationData arent arbitrary, they would always be one of the two types for a given feature in this example.
This however needs to be structured in a way that can be expressed via Formly as I am using this to generate forms. In this case it would be an array of ObjectFieldTypes, one for feature a and one for feature b, which would enumerate the three properties and provide Input field types, until it got to configurationData at which point it would use an ObjectFieldType again, which would now be different for each field type.
The issue here is that 1) I'm not sure how to express this in json schema and 2) I can't use things like patternProperties with formly because the properties have to be explicitly defined in the json schema in order for formly to render the field types for each property. Although patternProperties would technically be valid schema in this case, if the schema doesn't define those properties, then the model in the valueChanges observable on the FormGroup just excludes them entirely. So I would end up with:
{
features:[
{
id: "feature-a",
enabled: true,
configurationData: { }
},
{
id: "feature-b",
enabled: true,
configurationData: { }
}
]
}
I have tried the if then else construct, but I cant tell if the schema is wrong or if formly just doesn't support this. I made a stack blitz for this below:
https://stackblitz.com/edit/angular-g45ydm?file=src%2Fassets%2Fjson-schema%2Fif_then.json

Populate array in react-redux-firebase

react-redux-firebase supports replacing ids with the corresponding value in a lookup object – like a join – through a config object called populates (and later using a convenient mapping function inside mapStateToProps).
Unfortunately, the documentation of react-redux-firebase is rather rudimentary in this area (and in others)...
Can someone tell me, whether it's possible to populate a list of ids?
For example:
// from:
{ friends: [1, 27, 42] }
// to:
{ friends: [
{ id: 1, name: 'Charlie' },
{ id: 27, name: 'Parker' },
{ id: 42, name: 'Brown' },
] }
Testing version 3.0.0, it seems that populating a list works exactly the same as replacing a single field. Instead of receiving back an object you will receive an array of replaced objects.

Polymer index property of type object with another property

I have a custom element, x-custom, that has a property, object, that is an Object.
I have another property, name, that I want to use to index object, to put data from other custom elements under a specific property of object.
Something like the following is the end goal:
<other-custom-element data="{{object.{{name}}}}"></other-custom-element>
...
name: {
type: String,
},
object: {
type: Object,
value: {}
}
So, in the example, if the value of name is "prop1", then the data from other-custom-element would be placed into object.prop1.
EDIT:
So, I've updated my code and have gotten a bit closer, but still not quite all the way. The following example code is much closer to my actual code, so maybe it'll help in answering the question:
<template is="dom-repeat" items="[[locations]]" as="location">
<get-data-at-location
location="[[location]]"
data="[[_resolveRef(location)]]">
</get-data-at-location>
<h5>Data at location: [[location]]</h5>
<template is="dom-repeat" items="[[_resolveRef(location)]]" as="data">
<p>Data: {{data.value}}</p>
<p>Time: {{data.time}}</p>
</template>
</template>
...
alldata: {
type: Object
value: {}
},
_resolveRef: function(location) {
return this.alldata[location];
}
I'm trying to basically pass a reference to a specific element of alldata with _resolveRef(), but I think there may be a scoping issue. I get the following error (say, if location is equal to "some_location"):
Uncaught TypeError: Cannot read property 'some_location' of undefined
So, for some reason it doesn't seem to know about "alldata", even though it is a property of the custom element. Hopefully that's enough for a Polymer whiz to help :)
EDIT 2:
Some sample data to make the final goal more clear.
Say that locations is an array consisting of the following strings:
locations: {
type: Array,
value: ['location1', 'location2']
}
And say that the <get-data-at-location> element returns the following data for each individual location:
<get-data-at-location
location=[[location]] //'location1'
data={{}}> //[{value: 4, time: 0}, {value: 5, time: 1}]
</get-data-at-location>
<get-data-at-location
location=[[location]] //'location2'
data={{}}> //[{value: 10, time: 1}, {value: 11, time: 2}]
</get-data-at-location>
After all is said and done, I would like alldata to have the following information:
alldata: {
location1: [{value: 4, time: 0}, {value: 5, time: 1}],
location2: [{value: 10, time: 1}, {value: 11, time: 2}]
}
So, I would like the object alldata shared across all instances of the dom-repeat so that they can all place data into their respective property (which is just the string value of location) of the overall object.
EDIT 3:
Tried something new. Rather than _resolveRef(location) as a way to return a pointer to the location where <get-data-from-location> should store the data that it gets, I just used the following syntax: {{alldata[location]}} and things worked better, but still not perfect.
<template is="dom-repeat" items="[[locations]]" as="location">
<get-data-at-location
location="[[location]]"
data="{{alldata[location]}}">
</get-data-at-location>
<h5>Data at location: [[location]]</h5>
<template is="dom-repeat" items="{{alldata[location]}}" as="data">
<p>Data: {{data.value}}</p>
<p>Time: {{data.time}}</p>
</template>
</template>
...
alldata: {
type: Object
value: {}
}
So, this appears to work! Until I realized that the alldata that the data is being bound to is not the alldata of the custom element - it appears to be bound to the scope of the first dom-repeat, which the second dom-repeat has access to.
So basically, if I was to use this custom element within another page and bind to alldata, it doesn't work:
<x-custom alldata={{allData}}></x-custom>
<x-pretty-json object={{allData}}></x-pretty-json>
<x-pretty-json> just pretty-prints the provided object, and in this case it just prints empty brackets {}. So, closer (kind of), but still not quite there.

How to set the `type` property of the serialized JSON object in Ember

I have a model named line-item in my ember application. When I try to create a line-item record, I observe in the network tab that ember serialises a JSON object that looks like so:
{
data: {
quantity: 1,
type: "lineItems", // note this
links: {
cart: {linkage: {id: "7", type: "carts"}}
product: {linkage: {id: "a65874aa87b", type: "products"}}
}
}
}
What I would like to do is change the value of the type property to line-items instead (since the backend expects that). I have tried overriding the serializeIntoHash and typeForRoot methods of the RESTSerializer class, but no luck there. I'm probably mucking around with the wrong methods, but I'm just not sure where I should be looking at instead.
I would really appreciate some pointers in the right direction.Thanks!

How to avoid "Unable to get property 'X' of undefined or null reference" when creating $scope properties from JSON data

I want to populate some charting data from some JSON data pulled down using $http.get. However, the issue I have is the $scope property I am binding to doesn't exist until the JSON is returned, so the page is throwing an error when it loads.
How do I avoid the error? It feels like I have a chicken and egg scenario.
Example:
$scope.Model.Charts.Electricity = {
series: [
name: "2014 Target",
data: $scope.Model.Data.Json.Charts.Electricity.CurrentYearTarget
]
};
The Electricity.CurrentYearTarget property is the one that doesn't exist until the promise is completed:
promise.then(
function(payload) {
$scope.Model.Data.Json.Charts = payload.data;
});
The JSON is what then defines the objects that sit under .Charts. Example:
{"Electricity":
{"CurrentYearTarget":
[10000.0,
// snip
10000.0]
}
}
OK, so what can I do to work with this? I suppose I could wrap all of my properties like $scope.Model.Charts.Electricity and so forth into a simple JavaScript if statement, but that doesn't feel right.
I suggest you consider simplifying your approach somewhat, as $scope.Model.Data.Json.Charts.Electricity.CurrentYearTarget is somewhat verbose, and Model, Data, Json all mean the same thing really, you can probably cut some of these out.
However, this is beside the point, you can still acheive what you want to, just populate the data when the request has returned:
$scope.Model = { Data: { Json: { Charts: {} } } }
promise.then(
function(payload) {
$scope.Model.Data.Json.Charts = payload.data;
$scope.Model.Charts.Electricity = {
series: {
name: "2014 Target",
data: $scope.Model.Data.Json.Charts.Electricity.CurrentYearTarget
}
};
});