How to implement `loopback-ds-timestamp-mixin` in loopback 4? - loopbackjs

I found a problem when implementing it in loopback 4, because the structure of version 4 changed completely. there is no model-config.json file.
How do I add mixins property to server?
I want to add createdAt and updatedAt to each model on loopback 4

you can write a BaseClass Model and then inherit it in your models.
base-entity.model.ts
export abstract class BaseEntity extends Entity {
#property({
type: 'date',
default: () => new Date(),
name: 'created_on',
})
createdOn?: Date;
#property({
type: 'date',
default: () => new Date(),
name: 'modified_on',
})
modifiedOn?: Date;
}
yourmodel.model.ts
export class Yourmodel extends BaseEntity {
}

Related

Deep nested validation in loopback models

The loopback docs indicate how to validate models in the REST layer. But, is there a way you can validate deep/nested models inside your model?
If a User has both a name and a Contact, I want both to be validated, including all properties defined in Contact.
This comment from 2015 mentions deep validation is not supported. Is this still the case?
In this more recent answer it is suggested to set type: Contact directly, but I'm getting this error:
Property User.contact does not have "type" in its definition
import {Entity, model, property} from '#loopback/repository';
// User
#model()
export class User extends Entity {
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: Contact,
required: true,
})
contact: Contact;
}
// Contact
#model()
export class Contact extends Model {
#property({
type: 'string',
required: true,
default: 'phone',
})
type: string;
}

ember-cli-mirage loses relationship when models are sideloaded

I've got following models:
// venue.js
export default Model.extend({
name: DS.attr('string')
});
// web.js
export default Model.extend({
webname: DS.attr('string'),
venue: DS.belongsTo('venue', {async: false})
});
and I use the RESTAdapter. I defined my Mirage configuration as following:
// serializers/application.js
import { RestSerializer } from 'ember-cli-mirage';
export default RestSerializer.extend({
});
// config.js
export default function() {
this.get('/webs', schema => {
let venue = schema.venues.create({name: 'venue name'});
let web = schema.webs.create({
webname: 'web name',
venue: venue
});
return {
web: web,
venue: venue
}
})
}
This model sideloading is a part of our application, so I have to use it. Anyways as you can see the response is fine here, i.e. it correctly identifies the foreign keys etc:
But when I receive the response I can't access the venue from the web - it's null. The ember inspector confirms that:
Does anyone have any idea of how I can preserve the relationship when I obtain the data?
Glad you found include! The idea there is that whether associations are included or not can depend on the request. For example, with JSON:API a lot of apps will use query param includes to specify inclusion from the client-side. Other apps will have their servers send over default server-side includes, and that's what the include key is used for.
Also note that include can be a function, if you want more dynamic behavior. You can check out the docs for some examples.
Last point – your get handlers really should be returning what's already in Mirage's database, rather than creating new resources then returning them. This way your app will behave more similar to how it will in production.
So instead of
this.get('/webs', schema => {
let venue = schema.venues.create({name: 'venue name', id: 100});
let web = schema.webs.create({
webname: 'web name',
venueId: 100
});
return web;
})
try
this.get('/webs', schema => {
return schema.webs.all().models[0]
})
to return the first model (or schema.webs.all() if the endpoint should return a collection). Then, to seed Mirage with your starting data, put your data creation logic in scenarios/default.js using server.create:
// scenarios/default.js
export default function(server) {
let venue = server.create('venue', {name: 'venue name', id: 100});
server.create('web', {
venue,
webname: 'web name'
});
}
Ok, I did it a bit wrong apparently. Here is how one should do the sideloading correctly.
Since my web requires a venue to be sideloaded, I created a new model-specific serializer by calling ember g mirage-serializer web. Then I can specify the relationships which should be loaded alongside with the main entity via the include field:
// serializers/web.js
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
include: ['venue'],
});
Then the config.js can be changed to
// config.js
this.get('/webs', schema => {
let venue = schema.venues.create({name: 'venue name', id: 100});
let web = schema.webs.create({
webname: 'web name',
venueId: 100
});
return web;
})
// OR
this.get('/webs', schema => {
let venue = schema.venues.create({name: 'venue name', id: 100});
let web = schema.webs.create({
webname: 'web name',
venue: venue
});
return web;
})
or one can use fixture files etc.

How to update model joined with belongsTo?

I have a Consultation model
export default DS.Model.extend({
records: DS.hasMany('record', { async: true }),
currentUser: DS.belongsTo('user'),
remoteUser: DS.belongsTo('user'),
created_time: DS.attr('number'),
freeMsgCount: function () {
return (this.get('remoteUser.msg_block')-this.get('user_msg_cnt'));
}.property('user_msg_cnt'),
.....
});
And User model
export default DS.Model.extend({
name: DS.attr('string'),
.....
});
And I try to update User model. I get json data via WebSocket
socket.on('message', function (jsonObj) {
if (jsonObj.action && jsonObj.action == 'userReload') {
self.store.push('user',jsonObj.userData );
return;
}
}
But Consultation model doesn't know about this update, because I have property freeMsgCount in Consultation model which is using data from User model this.get('remoteUser.msg_block'). User data was updated I saw it in Ember inspector. How can I tell Consultation model to update relation remoteUser?
It seems that you're not watching remoteUser changes in your computed property, so it won't be triggered if remoteUser property's changed. Please add remoteUser.msg_block to computed property declaration:
export default DS.Model.extend({
# ...
freeMsgCount: function () {
return (this.get('remoteUser.msg_block')-this.get('user_msg_cnt'));
}.property('user_msg_cnt', 'remoteUser.msg_block'),

Waterline associations, change foreign key?

The latest waterline now supports associations. Here is an example of a one-to-many
// A user may have many pets
var User = Waterline.Collection.extend({
identity: 'user',
connection: 'local-postgresql',
attributes: {
firstName: 'string',
lastName: 'string',
// Add a reference to Pets
pets: {
collection: 'pet',
via: 'owner'
}
}
});
var Pet = Waterline.Collection.extend({
identity: 'pet',
connection: 'local-postgresql',
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
owner: {
model: 'user'
}
}
});
This creates a field called owner on the pet collection. This would be fine except for working with an existing DB. which calls it's foreign key owner_id.
Is there anyway to override the field name used in the database?
You can change the column name used for any model attribute by setting the columnName property:
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
owner: {
columnName: 'owner_id',
model: 'user'
}
}
Note also that when defining a model in Sails, you shouldn't extend from Waterline directly, but simply save the model file in /api/models with the appropriate name, e.g. User.js:
module.exports = {
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
owner: {
model: 'user',
columnName: 'owner_id'
}
}
}
and let Sails / Waterline handle the identity and connection for you unless you really want to override the defaults.

Ember-data mapping embedded object from JSON

I am struggling with a strange problem. I have a model called Activity with a property defined like this:
owner: DS.belongsTo('App.User', embedded: true)
The User is also a defined model when I'm getting the JSON response like this:
some single properties and
user: { id: etc. }
My all properties map well but the user embedded object from JSON doesn't map to the owner property. However, when I change
owner
to
user
It maps well. But I want to leave the owner because it's a better representation of what I mean. I tried this action:
owner: DS.belongsTo('App.User', key: 'user', embedded: true)
but it didn't help.
First, I recommend using the latest Ember / EmberData, but you will need to handle embedded records manually by enhancing extractSingle in a custom serializer (see example below). Also, you should define relationships like this:
App.Activity = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('user')
});
App.User = DS.Model.extend({
name: DS.attr('string'),
activities: DS.hasMany('activity')
});
Next, I recommend using the ActiveModelAdapter if you are using underscores when communicating with the server (i.e. like in EmberData 0.13):
App.ApplicationAdapter = DS.ActiveModelAdapter;
Finally, to use owner for a User, override typeForRoot in a custom serializer.
For example:
App.ApplicationSerializer = DS.ActiveModelSerializer.extend({
typeForRoot: function(root) {
if (root == 'owner' || root == 'owners') { root = 'user'; }
return this._super(root);
},
// based on: https://github.com/emberjs/data/blob/master/TRANSITION.md#embedded-records
extractSingle: function(store, type, payload, id, requestType) {
var owner = payload.activity.owner,
ownerId = owner.id;
payload.owners = [owner];
payload.activity.owner_id = ownerId;
return this._super.apply(this, arguments);
}
});
Example JSBin