where is the best place to adjust how ember-data talks to sails - ember-data

I am trying to implement an ember-cli (ember-data) to sails.js connection. I have installed ember-data-sails and have a simple model and am using the socketAdapter...
adapters/application.js
import SailsSocketAdapter from 'ember-data-sails/adapters/sails-socket';
export default SailsSocketAdapter.extend({
useCSRF: true,
coalesceFindRequests: true,
namespace: 'api/v1',
//defaultSerializer: '-rest',
});
Example Model:
RequestType.js
module.exports = {
attributes: {
name : { type: 'string' },
desc : { type: 'string' },
requestSubType : { type: 'hasMany' }
}
};
So far it is mostly working, I can get a list of RequestTypes ok. However if i try and nest a route to display/create the hasMany relationship, then i have an issue.
request-sub-types/new/routes.js
This file, requests the Parent model requestType first, and then creates a new record, inserting the Parent in the belongsTo attribute.
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var requestType = this.modelFor('requestTypes/view');
return requestType.get('requestSubTypes').createRecord({
requestType: requestType
});
}
});
request-types/view/route.js
This file is the model I think that is being requested above. The issue here is the params object that get fed through is:
{ requestType_id: 6 }
but if this is passed to the store.query call then nothing gets returned. If however I change the params object to:
{ id: 6 }
then I do see a record come back from the sails api.
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
console.log('RT: View Route MODEL', params);
var query = {};
if(params.requestType_id) {
query.id= params.requestType_id;
}
return this.store.query('requestType', query);
}
});
So My question is what do i need to edit to make the Primary Key definitions match up. so that Ember-data and Sails can talk to each other correctly? Is it in a serializer somewhere, the Sails API or in each model call?
thanks... and sorry if the above doesn't make any sense! :)

Related

Apollo query that depends on another query's result

I’m trying to create an Apollo query (in Vue/Nuxt) that depends on the result of another query.
sessions needs person in order to use this.person.id. How can I ensure that person exists before getting data for sessions?
My script and queries are below. Both queries work fine in GraphiQL. Thank you!
JS
import gql from 'graphql-tag'
import Photo from '~/components/Photo.vue'
import Session from '~/components/Session.vue'
import {Route} from 'vue-router'
import { currentPerson, currentPersonSessions } from '~/apollo/queries.js'
export default {
name: 'Speaker',
props: ['slug', 'id', 'person'],
data() {
return {
title: 'Speaker',
routeParam: this.$route.params.slug
}
},
apollo: {
person: {
query: currentPerson,
loadingKey: 'loading',
variables() {
return {
slug: this.routeParam
}
}
},
sessions: {
query: currentPersonSessions,
loadingKey: 'loading',
variables() {
return {
itemId: this.person.id
}
}
}
},
components: {
Photo,
Session
}
}
Queries
export const currentPerson = gql `query ($slug: String!) {
person(filter: { slug: { eq: $slug }}) {
name
bio
affiliation
id
photo {
url
}
}
}`
export const currentPersonSessions = gql `query($itemId: ItemId!) {
allSessions (filter: { speakers: { anyIn: [$itemId] }}) {
title
slug
start
end
}
}`
I managed to solve this by breaking out the part the sessions into a separate component.
Is that the appropriate way to handle a case like this, or is there a better way by chaining the requests here? If there's a way to do this, I still wouldn't mind hearing other answers.
I think the best way is to have a proper graph of your data model like :
type Person {
name: String!
bio: String
affiliation: String
id: ID!
photo: Photo
sessions(filter: SessionFilter): SessionList!
}
With GraphQL, you model your business domain as a graph
Graphs are powerful tools for modeling many real-world phenomena because they resemble our natural mental models and verbal descriptions of the underlying process. With GraphQL, you model your business domain as a graph by defining a schema; within your schema, you define different types of nodes and how they connect/relate to one another. On the client, this creates a pattern similar to Object-Oriented Programming: types that reference other types
You can read more in the official docucmentation

ember multi level has many serialization issue

I am facing some issues with my serialization of model. Below is a detailed code which will show how my current model and corresponding serializers look.
User model
export default Model.extend({
name: attr('string'),
accounts: hasMany('account', {async: false})
});
Account model
export default Model.extend({
type: attr('string'),
transactions: hasMany('transaction')
})
Transaction model
export default Model.extend({
date: attr('string'),
amount: attr('number')
})
So basically its a hasMany within another hasMany.
Serializers looks like this:
base serializer:
export default BaseSerializer.extend({
keyForAttribute(key) {
return key.underscore();
},
keyForRelationship(key, relationship) {
return key.underscore();
}
});
User serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
account: { embedded: 'always' }
}
});
Account serializer:
export default BaseSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
transaction: { embedded: 'always' }
}
});
Now when I am invoking api call which gives me a json response where user model has a property named as accounts: which internally have another property called as transactions, I am expecting them to serialize but somehow its not working. have I done anything wrong here? Please advise as I am new to ember and still learning it.
Base serializer is:
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
keyForRelationship(key, relationship) {
return key.underscore();
}
})
Serialized json
I dont have it but from the console logs, seems like only user model is getting serialized as when I tried to print console.log(user.get('accounts').get('firstObject').get('type') then i saw undefined there.
What I want is:
{
name: "bhavya"
accounts: [
{
type : 'savings',
transactions: [
{
amount: 500
}
]
}
]
}

How can I retrieve a route's model() when writing a test?

I have a route that has its own model, which does not come from the Ember store (let's say it can come from "anywhere" for the sake of this question).
model() {
return RSVP.hash({
value: someCall()
});
}
this.owner.lookup('route:routeName').model() does not seem to work, neither does this.owner.lookup('controller:controllerName').get('model.X') or any of the other things I've tried.
Nor does it seem to be mentioned at https://guides.emberjs.com/v3.1.0/testing/testing-routes/
How would you retrieve a route's model in a test?
The Ember router doesn't appear to have any kind of public interface to get the model according to the official docs (https://emberjs.com/api/ember/3.1/classes/EmberRouter). It can access the model function internally though. This feels a bit hackish, but I was able to get it to work:
Router:
import Route from '#ember/routing/route';
import { hash } from 'rsvp';
export default Route.extend({
model() {
return hash({
value: 'wibble'
});
},
getMyModel: function() {
return this.get('model')();
}
});
Router test:
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Route | test', function(hooks) {
setupTest(hooks);
test('do something with the router model...', function(assert) {
let route = this.owner.lookup('route:test');
assert.ok(route);
route.getMyModel().then(function(model) {
console.log(model);
assert.equal(model.value, 'wibble');
});
});
});
You can go with:
route.get('model')()

Ember, Ember Data - Updating hasMany relation

I'm trying to update a hasMany relation in Ember but I'm having a bit of trouble getting the data to send correctly.
I have the following Ember models:
// models/post.js
export default DS.Model.extend({
title: DS.attr('string'),
tags: DS.hasMany('tag', { async: true })
});
// models/tag.js
export default DS.Model.extend({
title: DS.attr('string'),
post: DS.belongsTo('post', { async: true })
});
And then I have the following action in my route to create a new tag and update the post:
addTag: function() {
var post = this.get('currentModel');
var newTag = this.store.createRecord('tag', {
title: 'Lorem Ipsum',
post: post
});
newTag.save().then(function() {
post.get('tags').pushObject(newTag);
post.save();
});
}
The new tag is created successfully and saved by my Express api but the post doesn't get saved correctly. It's received by the api but the request payload made by Ember never contains the tag IDs (it does send the tag title though). What am I doing wrong? Would really appreciate any help!
Edit
It turns out the RESTSerializer by default doesn't serialize and include the related IDs for a hasMany relationship. It only includes them for the belongsTo side as it expects the API to take care of saving it where needed. I will probably change my API to fit this behaviour as it's more efficient but in case any one else comes across this, it is possible to make the serializer include the IDs by extending the serializer and using the DS.EmbeddedRecordsMixin mixin - http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html - Which would look something like this:
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
tags: { serialize: 'ids' }
}
});
You don't need to call .save() on post. When you call createRecord to create a tag, your backend receives id of post and should persist dependencies accordingly.
addTag: function() {
var post = this.get('currentModel');
this.store.createRecord('tag', {
title: 'Lorem Ipsum',
post: post})
.save()
.then(function(tag) {
post.get('tags').pushObject(tag);
});
Meet the same problem.
Currently I solve it by serializeHasMany hook.
// app/serializers/application
import {ActiveModelSerializer} from 'active-model-adapter';
export default ActiveModelSerializer.extend({
serializeHasMany: function(snapshot, json, relationship){
this._super.apply this, arguments
if (!json[relationship.type + '_ids']){
var ids = []
snapshot.record.get(relationship.key).forEach(function(item){
ids.push item.get 'id'
});
json[relationship.type + '_ids'] = ids
}
}
})

Ember.js update model after save

I have a small Ember app and adding in authentication at the moment with a simple API in the background:
POST /login
//returns
{
"token": "Much53cr4t"
}
Ember model for login (route setup correctly and calls the endpoint as expected)
App.Login = DS.Model.extend({
username: DS.attr(),
password: DS.attr(),
token: DS.attr()
});
Controller
App.LoginController = Ember.ObjectController.extend({
// Implement your controller here.
actions: {
submit: function() {
var self = this;
var login = self.get('model');
login.set('username', self.get('username'));
login.set('password', self.get('password'));
login.save().then(function (result) {
//do something here?
});
}
}
});
I would like to get the returned token value to be added either to the created model before save, or new one. Whichever is easier. Can't seem to find any other advice other than 'return an id' but that I would consider not the best when it comes to an AUTH API endpoint like this.
So I ended up adding a custom REST adapter for this:
App.LoginAdapter = DS.RESTAdapter.extend({
host: 'http://127.0.0.1',
createRecord: function(store, type, record) {
var p = this._super(store, type, record);
return p.then(function(data){
record.set('token', data['token']);
});
},
});
Still wonder is there an easier, or more 'Ember-way' for doing this?