I created model Consultation:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
records: DS.hasMany('record', { async: true }),
currentUser: DS.belongsTo('user'),
remoteUser: DS.belongsTo('user')
});
And also I created model Record:
import DS from 'ember-data';
export default DS.Model.extend({
record_text: DS.attr('string'),
record_poster_id: DS.attr('string'),
record_consultation_id : DS.attr('number'),
consultation: DS.belongsTo('consultation'),
isMine: DS.attr('boolean')
});
At first all consultations load during opening page. And I don't want to load all records of each consultation during opening page. To do this I added async: true but all records loaded simultaneously sending many requests like /consultations/:id/records. After that consultation and records still non-joined. I have next json response for consultation:
{
"consultations":[
{
"id":140721,
"title":"ExpertId: 41217, clientId: 0",
"links":{
"records":"records"
},
"currentUser":41217,
"remoteUser":159984
},
......
]
}
And for records:
{
"records":[
{
"record_id":681952,
"record_consultation_id":140721,
"record_poster_id":0,
"record_text":"1",
},
........
]
}
I think I need to override default serializer. I tried to create serializer for Record:
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: 'record_id',
keyForRelationship: function(key, kind) {
return 'record_consultation_id';
}
});
But it still doesn't work.
Please advise how to join models using links?
UPDATE:
Template
{{#each item in model.records}}
<div class="table message">
{{item.record_text}}
</div>
{{/each}}
I am doing Async (lazy loading) of data using RESTAdaptor and Ember-Data too.
For my links area, i put in the full request URL as follows:
"links":{
"records":"/consultations/140721/records"
},
And using firebug to look at when the request gets sent off, its only when I request for the async content that the AJAX gets fired off.
model.get('records');
Can you provide your Controllers & Template code so I can see how your accessing the Records?
Related
I have four models
//models/exam.js
name: attr('string'),
owner: belongsTo('user'),
//models/question.js
content: attr('string'),
exam: belongsTo('exam')
//models/answer.js
owner: belongsTo('user'),
question: belongsTo('question'),
answer: attr('string'),
remarks: attr('string'),
exam: belongsTo('exam')
//models/user.js
owner : attr('string'),
email : attr('string'),
password : attr('string'),
I load the models into a route. Then, when I run the following template code,
{{#each model.answers as |ans|}}
<p>{{ans.question.content}}</p>
{{/each}}
//route.js
import Route from '#ember/routing/route';
import { hash } from 'rsvp';
export default Route.extend({
model: function(params){
return hash({
student: this.store.findRecord('student',params.id),
answers: this.store.query('answer',{
owner: params.id
}),
});
}
});
it shows the output as follows
<frontend#model:question::ember276:5>
<frontend#model:question::ember281:6>
<frontend#model:question::ember286:4>
why is it showing such an code, why not showning the original content?
I think you encountered a very special and rare case. The content has a special meaning for ember relationships. That's internal stuff and an end-user should not deal with it. But that's the reason, why you get
<frontend#model:question::ember276:5>
for
{{ans.question.content}} {{!-- .content doesn't return the content attribute --}}
I would work around it by changing the attribute's name at server and ember-model. If I server's attribute-name is not mutable, I would customize the ember-serializer. I.e.:
//app/serializers/person.js (ember g serializer question)
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
attrs: {
questionContent: 'content' //map server's attribute content to ember-model's questionContent
}
});
I'm attempting to set the belongsTo relationship using a dropdown.
So I have my Books model:
import DS from 'ember-data';
export default DS.Model.extend({
// Relationships
author: DS.belongsTo('author'),
name: DS.attr()
});
And my Author model:
import DS from 'ember-data';
export default DS.Model.extend({
// Relationships
author: DS.hasMany('books'),
name: DS.attr()
});
My Books/new route:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
book: this.store.createRecord('book'),
authors: this.store.findAll('author')
})
},
actions: {
saveBook(newBook) {
newBook.book.save().then(() => this.transitionTo('book'));
},
willTransition() {
this.controller.get('book').rollbackAttributes();
}
}
});
And my Books/new template:
<label >Book Name</label>
{{input type="text" value=model.name placeholder="Book Name"}}
<label>Author</label>
<select>
{{#each model.authors as |author|}}
<option value="{{author.id}}">
{{author.name}}
</option>
{{/each}}
</select>
<button type="submit"{{action 'saveBook' model}}>Add Book</button>
If I remove the select element and just save the name of the book it works fine, but with it I get this: (where id is an auto-generated ID)
Error: Some errors were encountered while saving app#model:book id
at reportError (firebase.js:425)
at firebase.js:445
at tryCatch (ember.debug.js:58165)
at invokeCallback (ember.debug.js:58177)
at publish (ember.debug.js:58148)
at publishRejection (ember.debug.js:58091)
at ember.debug.js:37633
at invoke (ember.debug.js:339)
at Queue.flush (ember.debug.js:407)
at DeferredActionQueues.flush (ember.debug.js:531)
I think I need to do something like getting the author object and setting book.author to that, but I can't find a clear explanation of how. Especially as I can't even work out how to get the data from that select menu in the route!
I feel like I'm missing something pretty simple here, anyone have any insight?
I would suggest moving this functionality to your controller.js where it belongs. Why is your relation to books in AuthorModel called author instead of books?
I would suggest rewriting your action (in the controller) to something like this:
saveBook(newBook) {
newBook.set('author', this.get('selectedAuthor') // or just the call below if you go with the alternative below
newBook.save().then(() => this.transitionTo('book'));
},
Now the problem persists, that you don't have a binding to your selected author. I would suggest using something like ember-power-select to bind your selected author to a controller property.
Then you would do this in your template:
{{#power-select
placeholder="Please select Author"
onchange=(action "authorSelectionChanged")
options=model.authors
as |author|}}
{{author.name}}
{{/power-select}}
And in your actions within your controller:
authorSelectionChanged(author) {
this.get('model.book').set('author', author);
// or the following if you go with the alternative above
this.set('selectedAuthor', author);
}
I've two models:
ticket:
export default DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
messages: DS.hasMany('message'),
})
messages:
export default DS.Model.extend({
message: DS.attr('string'),
date: DS.attr('date'),
ticket: DS.belongsTo('ticket', { async: true }),
});
And a router with this model method:
model(params) {
return this.get('store').findRecord('ticket', params.id, {reload: true, include: 'messages'});
},
At the page reload all ok, only a call to tickets/:id is made but sometimes ember make a bunch of calls to messages/:id. Why don't use the included data and try to retrieve from the server?
I've tried async: false on hasMany relation but I've this error:
Error: Assertion Failed: You looked up the 'messages' relationship on a 'ticket' with id 15 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)
Any idea?
This is the GET tickets/:id response:
{"data":
{
"id":"15",
"type":"tickets",
"attributes":{...},
"relationships":{"messages":{"data":[{"id":"1478482584658","type":"messages"},{"id":"1478482588516","type":"messages"},{"id":"1478517720","type":"messages"},{"id":"1478517813","type":"messages"},{"id":"1478517893","type":"messages"},{"id":"1478530030","type":"messages"},{"id":"1478530032","type":"messages"},{"id":"1478533446","type":"messages"}]}}},
"included":[
{"id":"1478482584658","type":"messages","attributes":{...}},{"id":"1478482588516","type":"messages","attributes":{...}},
...
}
}
]
}
Why don't take data from included and make other calls to the server?
What about promise?
While working with relationships it is important to remember that they return promises.
https://guides.emberjs.com/v2.1.0/models/working-with-relationships/#toc_relationships-as-promises
I have the route /bets with a component to display bets filtered by future dates. The route template also has a link to route /bets/new where the user can add new bets. Each bet has a belongsTo relationship to User.
My problem is that my bets doesn't show up in the component even though I can see in Ember Inspector that the data is loaded. It does show up when I add a new bet through the form in /bets/new though.
My reasoning was that since the data is async, it doesn't load until I request it by pushing a new bet, but I can't get my head around it.
bets.hbs
{{outlet}}
{{#link-to 'bets.new' class="btn btn-default"}}
{{fa-icon "plus"}} New bet
{{/link-to}}
{{#upcoming-bets bets=model.bets}}
{{/upcoming-bets}}
bets.js
import Ember from 'ember';
export default Ember.Route.extend({
model () {
return this.store.findRecord('user', this.get('session.currentUser.uid'));
}
});
upcoming-bets.hbs
<h2>Upcoming bets:</h2>
{{#each upcomingBets as |bet|}}
<div class="bet">
<h3>{{bet.homeTeam}} vs {{bet.awayTeam}}</h3>
<p>
<small>{{moment-format bet.eventDate 'MMMM Do'}}</small>
</p>
<p>
Bet type: {{bet.type}}
</p>
<p>
Bet: {{bet.bet}}
</p>
<p>
Stake: {{bet.stake}}
</p>
<p>
User: {{bet.user}}
</p>
<button class="btn btn-danger" {{action "deleteBet" bet}}>{{fa-icon "trash"}}</button>
</div>
{{else}}
<p>
You don't have any upcoming bets. Maybe {{#link-to 'bets.new'}}add one?{{/link-to}}
</p>
{{/each}}
upcoming-bets.js
import Ember from 'ember';
export default Ember.Component.extend({
upcomingBets: function() {
var today = new Date();
today.setHours(0,0,0,0);
return this.get('bets').filter(function(bet){
return bet.get('eventDate') > today;
});
}.property('bets.#each'),
actions: {
deleteBet: function(bet){
bet.destroyRecord();
}
}
});
new.js
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
addBet() {
var newBet = this.store.createRecord('bet', {
created: new Date(),
sport: this.get('selectedSport.name'),
league: this.get('league'),
homeTeam: this.get('homeTeam'),
awayTeam: this.get('awayTeam'),
type: this.get('type'),
bet: this.get('bet'),
stake: this.get('stake'),
odds: this.get('odds'),
eventDate: this.get('eventDate'),
});
// Save user as well as bet
var user = this.get('user');
user.get('bets').addObject(newBet);
newBet.save().then(function(){
return user.save();
});
}
}
});
user.js
import DS from 'ember-data';
export default DS.Model.extend({
email: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
bets: DS.hasMany('bet', { async: true })
});
bet.js
import DS from 'ember-data';
export default DS.Model.extend({
created: DS.attr('date'),
sport: DS.attr('string'),
league: DS.attr('string'),
homeTeam: DS.attr('string'),
awayTeam: DS.attr('string'),
type: DS.attr('string'),
bet: DS.attr('string'),
stake: DS.attr('number'),
odds: DS.attr('number'),
eventDate: DS.attr('date'),
win: DS.attr('boolean', {defaultValue: false}),
closed: DS.attr('boolean', {defaultValue: false}),
user: DS.belongsTo('user', { async: true })
});
I appreciate any pointers. Thank you!
Update 22 nov 2016
I've tried making things simpler and more clean by moving the filtering part to a controller as well as making the filter itself simpler by just matching a string. I still have the exact same issue - nothing gets rendered until I add a new bet at which point the filter works as intended and the correct bets show up. I've also had a hard time researching the issue as most examples with filtering out there waits for some sort of action before the filtering is done, for example typing something in an input field. I need the filtering to be done on load.
Here are my updated files:
bets.hbs
{{outlet}}
{{#link-to 'bets.new' class="btn btn-default"}}
{{fa-icon "plus"}} New bet
{{/link-to}}
{{#each upcomingTest as |bet|}}
<p>
{{bet.homeTeam}}
</p>
{{/each}}
controllers/bets.js
import Ember from 'ember';
export default Ember.Controller.extend({
upcomingTest: function() {
var team = 'EXAMPLE';
return this.get('model.bets').filterBy('homeTeam', team);
}.property('model.bets.#each')
});
Update 23 nov 2016
So I think I've proved that the issue has to do with me wanting to filter data that has a relationship to the model and isn't being loaded directly in the route. I tried using the exact same filter on data that I loaded directly in the route and that works great, even directly on load.
Using Ember-data and Ember.js, I'm trying to load two models with one JSON request. The models have a relationship analogous to this:
App.Person = DS.Model.extend({
name: DS.attr('string'),
dogs: DS.hasMany('App.Dog'),
});
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person'),
});
My server is sending JSON like this:
{
"dog": {
"id": 1,
"name": "Fido",
"owner": {
"id": 1,
"name": "John Smith",
"dogs": [1]
}
}
}
…And yet, Ember-data still sends a request (using findQuery) to my server trying to get the owner JSON.
I have a jsFiddle set up that demonstrates it here. To watch the problem happen, you'll need to go to this link to activate the route/template:
http://fiddle.jshell.net/6kQ8s/2/show/#/dog/1
I haven't defined findQuery() in my adapter on purpose because I shouldn't need that to get data that I have already sent… Right?
Does anyone know what I'm doing wrong here?
I'm doing the following (using ember-data revision 8)
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});
Additionally, I have to tell the serializer to load a mapping for this relation.
Though it's not required, I'm using my own DS.Serializer subclass. At initialisation
time the serializer loads a mapping for the Person class, which specifies that
embedded relationships should be loaded.
App.WOSerializer = DS.Serializer.extend({
init: function(){
this._super();
this.map(App.Dog, {
person: {
embedded: 'load'
}
});
});
Edit by question asker:
The serializer needed to be initialized in the adapter.
App.adapter = DS.Adapter.create({
// ...
serializer: App.WOSerializer.create()
});
Try use embedded property.
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});