I'm trying to determine how best to define my models. I'm trying to represent an asset. Ex: car, tv, house, etc.
Here's the payload coming from my server:
{
"asset": {
"id": "1b0b77a2-28d7-488e-9a6f-8a202c297593",
"created_at": "2015-03-19T09:16:43+0500",
"updated_at": "2015-03-19T09:16:43+0500",
"name": "Flat Screen Tv",
"properties": [
{
"name": "brand",
"value": "Sony",
"public": false,
"family": false,
"friends": false
},
{
"name": "price",
"value": 1000,
"public": false,
"family": false,
"friends": false
}
]
}
}
It needs to get POSTed back in the same format. How can I define this model with ember data? The properties are hanging me up. I thought I could just define a transformer but then noticed that changing values in the transformed data doesn't update the asset's "dirty" flag. I need that dirty flag to be updated when the properties get changed. Any ideas?
// app/transforms/array.js
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Transform.extend({
serialize: function(value) {
return value.toArray();
},
deserialize: function(value) {
return Ember.A(value);
}
});
Here's my asset model definition.
import DS from 'ember-data';
// app/models/asset.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
properties: DS.attr('array')
});
Your properties are going to have to be a separate model, and related to your asset models with hasMany. If that doesn't work for you, you might have to rethink how you are storing your properties.
Related
I set up a basic model and controller connected to mysql database. Everything looks good and is working.
My Definition Model looks like this.
import { Entity, model, property } from '#loopback/repository';
#model({
settings: {
mysql: {
table: 'definition'
}
}
})
export class Definition extends Entity {
#property({
type: 'number',
id: true,
generated: true,
forceId: true,
required: false,
description: 'The unique identifier for a definition',
})
id: number;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'string',
required: true,
})
process_id: string;
constructor(data?: Partial<Definition>) {
super(data);
}
}
export interface DefinitionRelations {
// describe navigational properties here
}
export type DefinitionWithRelations = Definition & DefinitionRelations;
When I have it up and running and click on the explorer to test it out.
I click on "Try it out" for get /definitions and the only editable field that gets enabled is the filter field.
It is repopulated with a value like this...
{
"where": {},
"fields": {
"id": true,
"name": true,
"process_id": true
},
"offset": 0,
"limit": 0,
"skip": 0,
"order": [
"string"
]
}
I have to clear that to get it to work which is fine. When I run it with no filter it returns these results.
[
{
"id": 10,
"name": "Place Manual Payoff Order Process Config",
"process_id": "Process_PlaceManualPayoffOrderProcessConfig"
},
{
"id": 11,
"name": "test",
"process_id": "Test"
},
{
"id": 12,
"name": "test2",
"process_id": "test2"
}
]
I am trying to use the filter expression to only return ones with a specific process_id field. So I change the filter to look like this.
{
"where": {"process_id": "test2"}
}
And it still returns the same results.
[
{
"id": 10,
"name": "Place Manual Payoff Order Process Config",
"process_id": "Process_PlaceManualPayoffOrderProcessConfig"
},
{
"id": 11,
"name": "test",
"process_id": "Test"
},
{
"id": 12,
"name": "test2",
"process_id": "test2"
}
]
Do filters currently work in Loopback 4 or am I using them incorrectly?
EDIT: if I post the filters in the URL string they work. It seems as though the openapi ui isn't generating that part of the filter Into the URL string.
It seems as though the openapi ui isn't generating that part of the filter Into the URL string.
Yes, this a precise description.
OpenAPI Spec version 3.x does not specify how to serialize deeply-nested values to URL queries and swagger-js, the library powering swagger-ui (which powers LoopBack's REST API Explorer), silently ignores such values.
The issue has been reported and is being discussed here:
swagger-api/swagger-js#1385
OAI/OpenAPI-Specification#1706
I have two models in Ember:
Collection
export default DS.Model.extend({
name: DS.attr(),
description: DS.attr(),
items: DS.hasMany('collection-item')
});
Collection Item
export default DS.Model.extend({
date: DS.attr(),
volume: DS.attr(),
sequenceNumber: DS.attr()
});
I want to save the collection items inside the 'items' attribute of the collection, like MongoDB:
[{
"name": "First Collection",
"description": "This is my first collection",
"items": [
{
"date": "2017-07-26",
"volume": "1",
"sequenceNumber": "1"
},
{
"date": "2017-07-27",
"volume": "1",
"sequenceNumber": "2"
}
]
},
{
"name": "Second Collection",
"description": "This is my second collection",
"items": [
{
"date": "2017-07-26",
"volume": "1",
"sequenceNumber": "1"
},
{
"date": "2017-07-27",
"volume": "1",
"sequenceNumber": "2"
}
]
}]
I have read something about serializers, but I don't get the point ;) Can someone give me a hint?
BTW, I'm using Firebase (emberfire) for now, but I'm going to build my own API in future.
What you're describing is known as an embedded record in Ember. On the serializers page, beneath the discussion of the JSONAPISerializer is a discussion of the embedded record mixin: https://guides.emberjs.com/v2.14.0/models/customizing-serializers/
You can use a RESTSerializer with an embedded mixin to achieve what you're after.
That said, unless your backend needs are fairly simple, I'd suggest beginning to build a backend (and using JSON-API for it) before you get too far. JSON-API is a spec based off of pain points the entire Ember community has felt over the years. If you build a simpler backend right now, you may find yourself hitting headaches in the future that JSON-API is specifically designed to address.
Good luck!
If you are using the defaults from Ember Data, you need to have a JSON API compatible backend service where to retrieve/send data from. You can take a look at the projects implementing JSON API standards if you don't have a backend yet.
After you have a working API, the rest is fairly straightforward and well documented.
Possibly I'm misunderstanding how Ember wants to lazy load hasMany sides of relationships but I'll specify what I want and someone can tell me if it is possible and how to configure things.
I'm using the RESTAdapter and I have a parent object with a 1-to-many relationship with several other objects. When I view the parent, I have links for the children objects that I want to show as child outlets.
Examples might be:
// brand.js
export default Model.extend({
name: attr('string'),
description: attr('string'),
dateCreated: attr('date'),
lastUpdated: attr('date'),
regions: hasMany('region', {async: true})
});
// region.js
export default Model.extend({
name: attr('string'),
dateCreated: attr('date'),
lastUpdated: attr('date'),
brand: belongsTo()
});
When I access /api/brands/1 I'm returning the following JSON:
{
"brand": {
"id": 1,
"dateCreated": 1466456255539,
"lastUpdated": 1466456255936,
"name": "Some Brand",
"description": "Voluptates odio nemo corrupti",
}
}
I have my route defined like so:
this.route('brand', {path: '/brands/:brand_id'}, function() {
this.route('regions');
});
So, in the brand detail screen, I have an {{outlet}} and when I click on the link for regions, I need to fetch the regions for that brand, and the URL would be /api/brands/1/regions.
Everything works if I return all the data in one big result, and my regions router looks like this:
export default Ember.Route.extend({
model() {
return this.modelFor('brand').get('regions');
}
});
But I don't know what to do so that regions gets lazily fetched correctly from my API. Or if it is even possible. I read up on side loading and currently, my API won't return ID's only for children.
With your example, ember.js does not know, what regions to load. Your JSON has to be either (non-embedded):
{
"brand": {
"id": 1,
"dateCreated": 1466456255539,
"lastUpdated": 1466456255936,
"name": "Some Brand",
"description": "Voluptates odio nemo corrupti",
"regions": ["5", "9"]
}
}
Which then loads regions with the id 5 and 9. Or you could embed them directly:
{
"brand": {
"id": 1,
"dateCreated": 1466456255539,
"lastUpdated": 1466456255936,
"name": "Some Brand",
"description": "Voluptates odio nemo corrupti",
"regions": [{
"id": "5",
"name": "Hanover",
"dateCreated": "2016-09-25 18:39:18",
"lastUpdated": "2016-09-25 18:39:18",
},{
"id": "9",
"name": "Cologne",
"dateCreated": "2016-01-19 11:59:18",
"lastUpdated": "2016-02-22 12:09:58",
}]
}
}
See here for the latter.
My server returns JSON like below and I cannot change it into sideloading style.
{
"owner": {
"id": 293,
"servers": [{
"id": 32,
"hostname": "host1",
"networks": [ {
"id": 1234,
"ip": 10.10.10.10
}, {
"id": 5678,
"ip": 10.10.20.10
}]
}, {
"id": 33,
"hostname": "host2",
"networks": [ {
"id": 1234,
"ip": 10.10.10.11
}, {
"id": 5678,
"ip": 10.10.20.11
}]
}]
}
}
There is one and only API endpoint for serving above single JSON, and for it, I written three models - owner, server, network - setup like below:
network:
import DS from 'ember-data';
export default DS.Model.extend({
servers: DS.hasMany('server'),
});
server:
import DS from 'ember-data';
export default DS.Model.extend({
hostname: DS.attr('string'),
owner: DS.belongsTo('owner', {async: true}),
networks: DS.hasMany('network', {async: true}),
});
owner:
import DS from 'ember-data';
export default DS.Model.extend({
servers: DS.hasMany('server', {async: true}),
});
Serializer for owner and server like this:
import TestSerializer from './test';
import DS from 'ember-data';
export default TestSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
servers: { embedded: 'always' },
}
});
import TestSerializer from './test';
import DS from 'ember-data';
export default TestSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
networks: { embedded: 'always' },
}
});
when I run it without network-server relation, server-owner relation solved by serializer and it is find. but with 3 level relation, it fails with very long error:
EmberError#http://localhost:4200/assets/vendor.js:27348:15
_emberMetalCore.default.assert#http://localhost:4200/assets/vendor.js:16067:13
ember$data$lib$system$store$serializer$response$$_normalizeSerializerPayloadItem/http://localhost:4200/assets/vendor.js:76803:17
ember$data$lib$system$store$serializer$response$$_normalizeSerializerPayloadItem/http://localhost:4200/assets/vendor.js:76815:35
ember$data$lib$system$store$serializer$response$$_normalizeSerializerPayloadItem/<#http://localhost:4200/assets/vendor.js:76791:12
.eachRelationship/<#http://localhost:4200/assets/vendor.js:87742:11
Map.prototype.forEach/cb#http://localhost:4200/assets/vendor.js:29106:11
OrderedSet.prototype.forEach#http://localhost:4200/assets/vendor.js:28889:11
Map.prototype.forEach#http://localhost:4200/assets/vendor.js:29110:7
.eachRelationship#http://localhost:4200/assets/vendor.js:87741:9
ember$data$lib$system$store$serializer$response$$_normalizeSerializerPayloadItem#http://localhost:4200/assets/vendor.js:76785:7
ember$data$lib$system$store$serializer$response$$_normalizeSerializerPayload#http://localhost:4200/assets/vendor.js:76753:18
ember$data$lib$system$store$$Store<.push#http://localhost:4200/assets/vendor.js:84416:18
ember$data$lib$serializers$embedded$records$mixin$$EmbeddedRecordsMixin<._extractEmbeddedHasMany/<#http://localhost:4200/assets/vendor.js:86708:11
ember$data$lib$serializers$embedded$records$mixin$$EmbeddedRecordsMixin<._extractEmbeddedHasMany#http://localhost:4200/assets/vendor.js:86706:9 ember$data$lib$serializers$embedded$records$mixin$$EmbeddedRecordsMixin<._extractEmbeddedRecords/<#http://localhost:4200/assets/vendor.js:86671:17
.eachRelationship/<#http://localhost:4200/assets/vendor.js:87742:11
Map.prototype.forEach/cb#http://localhost:4200/assets/vendor.js:29106:11
OrderedSet.prototype.forEach#http://localhost:4200/assets/vendor.js:28889:11
Map.prototype.forEach#http://localhost:4200/assets/vendor.js:29110:7
.eachRelationship#http://localhost:4200/assets/vendor.js:87741:9
ember$data$lib$serializers$embedded$records$mixin$$EmbeddedRecordsMixin<._extractEmbeddedRecords#http://localhost:4200/assets/vendor.js:86664:1 ember$data$lib$serializers$embedded$records$mixin$$EmbeddedRecordsMixin<.normalize#http://localhost:4200/assets/vendor.js:86399:16
apply#http://localhost:4200/assets/vendor.js:34198:1
superWrapper#http://localhost:4200/assets/vendor.js:33836:15
ember$data$lib$serializers$rest$serializer$$RESTSerializer<.extractArray/normalizedArray<#http://localhost:4200/assets/vendor.js:77580:20
ember$data$lib$serializers$rest$serializer$$RESTSerializer<.extractArray#http://localhost:4200/assets/vendor.js:77579:33
ember$data$lib$serializers$json$serializer$$JSONSerializer<.extractFindAll#http://localhost:4200/assets/vendor.js:74872:16
ember$data$lib$serializers$json$serializer$$JSONSerializer<.extract#http://localhost:4200/assets/vendor.js:74856:16
ember$data$lib$system$store$serializer$response$$normalizeResponseHelper#http://localhost:4200/assets/vendor.js:76729:33
ember$data$lib$system$store$finders$$_findAll/http://localhost:4200/assets/vendor.js:79011:25
Backburner.prototype.run#http://localhost:4200/assets/vendor.js:10843:18
ember$data$lib$system$store$$Store<._adapterRun#http://localhost:4200/assets/vendor.js:84741:16
ember$data$lib$system$store$finders$$_findAll/<#http://localhost:4200/assets/vendor.js:79010:9
tryCatch#http://localhost:4200/assets/vendor.js:67710:14
invokeCallback#http://localhost:4200/assets/vendor.js:67725:15
publish#http://localhost:4200/assets/vendor.js:67693:9
#http://localhost:4200/assets/vendor.js:44051:7
Queue.prototype.invoke#http://localhost:4200/assets/vendor.js:11571:9
Queue.prototype.flush#http://localhost:4200/assets/vendor.js:11635:11
DeferredActionQueues.prototype.flush#http://localhost:4200/assets/vendor.js:11436:11
Backburner.prototype.end#http://localhost:4200/assets/vendor.js:10725:9
Backburner.prototype.run#http://localhost:4200/assets/vendor.js:10847:13
run#http://localhost:4200/assets/vendor.js:31688:12
ember$data$lib$adapters$rest$adapter$$RestAdapter<.ajax/http://localhost:4200/assets/vendor.js:72892:15
jQuery.Callbacks/fire#http://localhost:4200/assets/vendor.js:3346:10
jQuery.Callbacks/self.fireWith#http://localhost:4200/assets/vendor.js:3458:7
done#http://localhost:4200/assets/vendor.js:9512:5
.send/callback#http://localhost:4200/assets/vendor.js:9916:8
vendor.js:44090:9
So, Can I use EmbeddedRecordsMixin for it? or how can I get right result on this condition?
I am trying to display some CI information via an ember application backed by Firebase. If I do a this.store.find('plan'); it fetches and displays the plans for the desired project, but it doesn't automatically async fetch the plans like I want. I am not quite sure what I am doing wrong.
DEBUG: -------------------------------
DEBUG: Ember : 1.9.0-beta.1+canary.8105a1bb
DEBUG: Ember Data : 1.0.0-beta.11+canary.d96c747da5
DEBUG: EmberFire : 1.2.7
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 1.10.2
DEBUG: -------------------------------
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('project');
}
});
App.ApplicationAdapter = DS.FirebaseAdapter.extend({
firebase: new Firebase('https://my.firebaseio.com/api-data/')
});
App.Project = DS.Model.extend({
name: DS.attr('string'),
plans: DS.hasMany('plan', { async: true })
});
App.Plan = DS.Model.extend({
project: DS.belongsTo('project', { async: true }),
shortName: DS.attr('string'),
shortKey: DS.attr('string'),
type: DS.attr('string'),
enabled: DS.attr('boolean'),
name: DS.attr('string'),
description: DS.attr('string'),
isBuilding: DS.attr('boolean'),
averageBuildTimeInSeconds: DS.attr('number')
});
My template
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each project in model}}
<li>
<h3>{{project.name}}</h3>
<ul>
{{#each plan in project.plans}}
<li>{{plan.name}}</li>
{{else}}
<li>no plans</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
How can I get the ember-data async relationship to automatically fetch when I try to access the project.plans property?
Edit:
I have tried mocking with Ember-CLI's http-mock and am sending back the following for /projects
{"plans":[{id: '10', project: '1', name: 'test', plans: ['10']}]}
Which is now working with adding the plans array. I now just need to figure out how this works on firebase.
When using Firebase after my initial seed data, I fetched each model type and called save on all instances. This caused Firebase to be properly populated for the async data array. This was persisted as follows:
/projects
{
"AS": {
"name": "AS",
"plans": {
"AS-AS": true
}
},
"F": {
"name": "F",
"plans": {
"F-INT": true,
"F-QA": true,
"F-STAG": true
}
}
}
/plans
{
"AS-AS": {
"averageBuildTimeInSeconds": 23,
"description": "",
"enabled": true,
"isBuilding": false,
"name": "AS - AS",
"project": "AS",
"shortKey": "AS",
"shortName": "AS",
"type": "chain"
},
"F-INT": {
"averageBuildTimeInSeconds": 18,
"description": "Integration build",
"enabled": true,
"isBuilding": false,
"name": "F - Integration",
"project": "F",
"shortKey": "INT",
"shortName": "Integration",
"type": "chain"
},
"F-QA": {
"averageBuildTimeInSeconds": 38,
"description": "Release from Stage to QA",
"enabled": true,
"isBuilding": false,
"name": "F - QA",
"project": "F",
"shortKey": "QA",
"shortName": "QA",
"type": "chain"
},
"F-STAG": {
"averageBuildTimeInSeconds": 16,
"description": "Stage Build and Deploy",
"enabled": true,
"isBuilding": false,
"name": "F - Stage",
"project": "F",
"shortKey": "STAG",
"shortName": "Stage",
"type": "chain"
}
}