Emberjs display data after promise gets fulfilled? - ember.js

I am a bit conceptually confused about how this works.
The user enters data on the form, presses next. The controller 's nextStep action fires and I ask the model to get data from the server. When the data arrives, I transition to the results route. At that point, I have the route store the previous model on a variable on the controller. Then I want to iterate through the data on the template.
The problem is that nothing shows up.
This is what I have (in the appropriate files):
//Step 1 Controller (partial - nextStep action)
var businessmatch = this.store.find('businessmatch', {businessname: businessname, phonenumber: phonenumber, zipcode: zipcode})
.then(function (result) {
// The model has data at this point
controller.transitionToRoute('step2');
});
// step2 Router
import Ember from 'ember';
export default Ember.Route.extend({
setupController: function(controller, model) {
controller.set('businessmatches', this.store.find('businessmatch'));
}
});
// Step2 template (partial)
{{#each businessmatches}}
{{businessname}}
{{/each}}
// Model
import DS from 'ember-data';
var Businessmatch = DS.Model.extend({
businessname: DS.attr('string'),
phonenumber: DS.attr('string'),
address: DS.attr('string'),
pageurl: DS.attr('string'),
thubmurl: DS.attr('string')
});
export default Businessmatch;
// Sample response from server
{
"businessmatches":[
{
"businessname":"El Farolito",
"pageurl":"/biz/el-farolito-san-francisco-2",
"thumburl":"/bphoto/ohpxQWg-hB9Sb27HkVg-yQ/90s.jpg",
"address":"780 El Camino RealMillbrae, CA 94030",
"phonenumber":"(650) 583-0487",
"id":1
},
{
"businessname":"El Farolito",
"pageurl":"/biz/el-farolito-san-francisco-4",
"thumburl":"/photo/AW76YTovuu9YsO69_BcLKQ/30s.jpg",
"address":"2779 Mission StSan Francisco, CA 94110",
"phonenumber":"(415) 824-7877",
"id":2
},
{
"businessname":"El Farolito",
"pageurl":"/biz/el-farolito-san-francisco",
"thumburl":"/bphoto/LgTOTIicRY6XArigmPhBpw/90s.jpg",
"address":"2950 24th StSan Francisco, CA 94110",
"phonenumber":"(415) 641-0758",
"id":3
}
]
}

#tstirrat is correct, the store.all() method immediately returns an array of cached records, while the store.find() method returns a promise of fetched records.
Is there a specific reason you aren't using the model hooks in your route? They are promise aware, and will wait for your store.find() to resolve then pass the model on to your setupController. You can then reference the previous models with modelFor('step1') or just use store.all() again.

Found it, if I change
controller.set('businessmatches', this.store.find('businessmatch'));
to
controller.set('businessmatches', this.store.all('businessmatch'));
it works as expected.
Would be interested to know why though, if anyone can explain.

Related

Ember: model not being set by store in model hook route

So what I am doing is extremely basic: rendering model data to the template.
Upon setting the model hook, the {{model}} object doesn't show data in the corresponding template.
Here's my code:
contact (route):
user: Ember.inject.service('current-user'),
model: function()
{
// var that = this;
// console.log('whats being returned bitch: ', this.store.findRecord('contact', this.get('user').contactID));
//return this.store.findRecord('contact', this.get('user').contactID);
var records = this.store.findRecord('contact', this.get('user').contactID);
var promise = Ember.RSVP.defer();
// console.log('promise', promise.resolve());
// records.addObserver('isLoaded', function() {
// // console.log('records.getv', records);
promise.resolve(records);
//});
return promise;
},
setupController: function(controller)
{
// Get the parameters for the current route every time as they might change from one record to another
var params = this.paramsFor('dashboard.contact');
console.log('params', params);
// Set the data in the current instance of the object, this is required. Unless this is done the route will display the same data every time
this.module = Ember.String.capitalize(params.module);
this.id = params.id;
this.data = this.store.find(this.module,this.id);
// Set the data in the controller so that any data bound in the view can get re-rendered
controller.set('id',this.id);
controller.set('model',this.data);
controller.set('module',this.module);
}
});
First i was trying just this but it was not displaying data, then i tried deferring the promise and resolving it (like this) and finally i tried setting up the controller (setupController function) but that didn't work either since params is empty for some reason :/
contact(template):
<h1> Contact! </h1>
{{#each model as |contact|}}
<h3>{{contact.name}}</h3>
<h3>{{contact.password_c}}</h3>
{{/each}}
contact(model):
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
password_c: DS.attr('string'),
birthdate: DS.attr('string'),
assistant: DS.attr('string'),
account_name: DS.attr('string'),
email1: DS.attr('string'),
facebook: DS.attr('string'),
phone_home:DS.attr('string')
// address: Ember.computed('primary_address_street', 'primary_address_state',
// 'primary_address_city', 'primary_address_country', function() {
// return '${this.get('primary_address_street')} ${this.get('primary_address_state')} ${this.get('primary_address_city')} ${this.get('primary_address_country')}';
// })
});
Please help!
Let's assume this is your router
// app/router.js
import Ember from 'ember';
var Router = Ember.Router.extend({
});
Router.map(function() {
this.route('contacts', {path: '/contacts/:contact_id'});
});
export default Router;
and your model
// app/models/contact.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
password_c: DS.attr('string'),
});
then this is would be your contacts.js route it will have a very important role and We'll be using Ember Data's findRecord to retrieve an individual record from the data store.
// app/routes/contacts.js
import Ember from 'ember';
export default Ember.Route.extend({
model(param){
return this.store.findRecord('contact',param.contact_id);
}
});
note: this param is very important.The param is passed from the URL into the model. This posts model has an id that can be accessed via contact_id. It uses that id to look up the record so it can be returned. By default the template with the same name, contacts, will have access to this model.
Here we use Ember Data's findAll. This simply returns back all the records in the post data store.
// app/routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('contact');
}
});
now
// app/templates/application.hbs
{{#each model as |contact|}}
<h3>{{contact.name}}</h3>
<h3>{{contact.password_c}}</h3>
{{/each}}
As I don't have access to see your service and all your code I tried to simplify the way you can return all contact and get that and also how you can pass Param easily.
for more information : https://guides.emberjs.com/v2.7.0/tutorial/ember-data/
You can follow this codes and customize as you would like, I hope it will resolve your problem.
UPDATE:
If you have already your user data and it's ok, then remove {{#each}}
and let's have {{contact.name}}, that should work, you just need #each
while you have all contact like this.store.findAll('contact'); or if
you are in you must have this {{model.name}}, then model would be
contact !

ember data not saving foreign key, sent as null

My ember app is not sending my foreign key to the back-end.
I have a table called issues which is has a related table called categories
My model is:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
category_id: DS.belongsTo('category'),
description: DS.attr('string')
});
My route is:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
return this.store.findAll('issue');
},
actions: {
create: function(){
var issue = this.store.createRecord('issue');
issue.name = this.get('controller').get('newName');
issue.description = this.get('controller').get('newDescription');
issue.category_id = parseInt(this.get('controller').get('newCategory'));
//debugger;
console.log(issue);
issue.save();
},
...
other actions
...
}
}
});
the console.log from above looks like the category_id is getting set correctly:
category_id: 3
description: "foobar"
name: "test"
However my JSON payload that gets sent to the backend looks like:
{"issue":{"name":"test","description":"foobar","category_id":null}}
I tried stepping through by adding a custom serialiser in app/serializers/application.js
export default DS.RESTSerializer.extend({
...
serialize: function(snapshot,options){
console.debug('options='+options);
debugger;
var json = this._super(snapshot, options);;
return json;
}
...
});
But I got lost in all the super calling super indirection.
The snapshot.record has category_id: 3, but the json coming back from the this._super() call has category_id: null
options has includeID:true
Any clues will be much appreciated ...
Ember : 2.0.2
Ember Data : 2.0.0
Your model definition is wrong, when dealing with relationships you define them just as you would define any other attribute, there is no need to use _id.
export default DS.Model.extend({
name: DS.attr('string'),
category: DS.belongsTo('category'),
description: DS.attr('string')
});
As for the creation you should always use setters/getters when dealing with ember objects:
create: function() {
var issue = this.store.createRecord('issue', {
name: this.get('controller').get('newName'),
description: this.get('controller').get('newDescription'),
category: this.get('controller').get('newCategory') // assuming new category is a DS.Model instance of category
});
issue.save();
}
If you wish to stick to the syntax you have you would use issue.set('name', this.get('controller').get('newName')), from the looks of your code it seems you are going about this in the wrong way.
You should have a this.route('new') nested under your issues route, that way you wouldn't have to use the controller to store information.
You would simply set the model of the new route to:
model: function() {
return this.store.createRecord('issue');
}
Your template would make use of the input helpers like so:
{{input value=model.name}} and your action would just get the currentModel and call .save().

Ember not requesting models by id after first request

SO,
I am working on an Ember app and experiencing a confusing problem. At the index route the app performs a find() and returns an array of dataset and links to a template to show further details about each dataset which are sideloaded when a resquest is made to find by id. (i.e. find(1), where 1 is the id.)
The first request with an id works fine, returning the dataset object and it's sideloaded data, however subsequent requests do not seem to do anything. The server does not see any request if I try to navigate to any other dataset after the first one's details have been loaded. However if I navigate from a specific dataset back to index and then back to any dataset it will send the request again (twice even, am not sure if this a related problem) and work. In other words:
/# works
/#/1 also works (or any other id as long as it is the first one visited)
/#/1 then /#/2 does not work, no request is sent
/#/1 followed by /# then /#/2 does work, maintaining the data at /#/1 & getting the new data for /#/2.
How do I get all of the specific dataset objects to return upon visiting them, without the hacky pitstop at index? Any advice would be greatly appreciated, thank you in advance!
The code:
-app.js
/**************************
* Application
**************************/
var App = Em.Application.create();
App.Router.map(function() {
this.resource('application', {path:'/'}, function() {
this.resource('dataset', {path: '/:dataset_id'}, function() {
});
});
});
App.ApplicationRoute = Em.Route.extend({
model: function() {
return App.Dataset.find();
}
});
App.DatasetRoute = Em.Route.extend({
activate: function() {
this.modelFor('dataset').reload();
}
});
/**************************
* Models
**************************/
App.Store = DS.Store.extend({
adapter: DS.RESTAdapter.create({
url: 'http://***.***.***.***:5000',
namespace: 'api',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
return '_id';
}
})
})
});
App.Dataset = DS.Model.extend({
dataset: DS.attr('string'),
title: DS.attr('string'),
points: DS.hasMany('App.Point')
});
App.Point = DS.Model.extend({
dataset: DS.attr('string'),
dataset_id: DS.attr('string'),
date: DS.attr('date'),
value: DS.attr('string')
});
A route's activate hook is only called when the route is first transitioned to. It is not called again if the route's model changes. So when you transition into App.DatasetRoute either by entering the url directly or by clicking link on index page, the activate hook runs and your dataset is reloaded. When you switch from #/1 to #/2, the route remains active and no hook is called.
If I am understanding your question correctly, you want to reload the dataset whenever a user visits its url. In that case instead of the route's activate hook what you probably want to do is observe changes to the dataset controller's content. Something like this should work:
App.DatasetController = Ember.ObjectController.extend({
refreshOnChange: function() {
var dataset = this.get('content');
if (dataset) {
console.log('reloading dataset ', dataset.toString());
dataset.reload();
}
}.observes('content')
}

Serialize is reached before server response is complete

In my ember app, I have a router with nested resources, like so:
App.Router.map(function () {
this.resource('explore', function() {
this.resource('building', { path: 'building/:slug' });
this.resource('country', { path: ':slug' }, function() {
this.resource('state', {path: ':slug' });
});
});
});
App.CountryRoute = Ember.Route.extend(App.SlugRouter, {
setupController: function(controller, country) {
controller.set('title', 'country detail');
controller.set('model', country);
}
});
App.SlugRouter = Ember.Mixin.create({
serialize: function(model, params) {
var name, object;
object = {};
name = params[0];
object[name] = model.get('slug');
return object;
}
});
App.Building = DS.Model.extend({
country: DS.belongsTo('App.Country'),
name: DS.attr('string'),
slug: DS.attr('string')
});
App.Country = DS.Model.extend({
name: DS.attr('string'),
slug: DS.attr('string'),
buildings: DS.hasMany('App.Building'),
states: DS.hasMany('App.State')
});
Loading the explore route shows a list of buildings received from the server (a django-rest-framework app), each building has a relationship to a country with a belongsTo attribute.
In the explore.index route, I display the list of the buildings, with links to the country route for each building, using {{linkTo this.country}}. The href, however, is loaded as #/explore/undefined, instead of #/explore/<country-name>.
The part that is confusing me is that this only happens the first time that I load the list. If I go to another route, then come back to #/explore, the links render correctly.
In the debugger, putting a breakpoint in the serialize method, I see that the first time that I load the page, the model object is empty (_data.attributes is an empty object). Going to the network tab in the debugger, I see that the a request has been made to the server to get the country data, but the response has not been received yet:
The response is eventually received, since {{this.country.name}} renders correctly, but after it's too late.
Thanks in advance for any responses/tips.
I am using:
Ember: 1.0.0-rc.5,
Handlebars: 1.0.0-rc.4,
jQuery: 1.8.3,
ember-data: 0.13,
ember-data-django-rest-adapter: 0.13
Firstly you are mixing in App.SlugRouter before it's definition. You should be seeing an error like Assertion failed: Expected hash or Mixin instance, got [object Undefined] in the console.
After that you need to load the model for a country by the slug. I don't see this in your Route either. You need something like this in CountryRoute depending on your persistence library.
model: function(params) {
return App.Country.find({slug: params.slug});
}
I suspect the part that is working right now is because your index route is loading the model and passing it in to setupController with the linkTo. Direct loading of the nested page requires configuring that route's model hook.

Ember Data sideloaded properties being dropped on a model

I'm working with
Ember RC3
Ember Data Revision 12
Handlebars RC3
I have Ember Data sideloading relationships on many of my models, so that I can template the sideloaded relationships like so:
// Models
App.Client = DS.Model.extend({
company: DS.attr('string'),
accountNumber: DS.attr('string'),
startDate: DS.attr('mysqlDate'),
// Relationships
campaigns: DS.hasMany('App.Campaign'),
users: DS.hasMany('App.User'),
phones: DS.hasMany('App.Phone'),
addresses: DS.hasMany('App.Address')
});
App.User = DS.Model.extend({
email: DS.attr('string'),
password: DS.attr('string'),
// Relationships
userType: DS.belongsTo('App.UserType'),
role: DS.belongsTo('App.Role'),
clients: DS.hasMany('App.Client'),
phones: DS.hasMany('App.Phone'),
addresses: DS.hasMany('App.Address')
});
<!-- template -->
<script type="text/x-handlebars" data-template-name="user/index">
<h2>{{email}}</h2>
<h5>Clients</h5>
<ul>
{{#each client in model.clients}}
<li>{{client.company}}</li>
{{/each}}
</ul>
</script>
This works wonderfully...except for every 1 in 10 reloads or so. Every once in a while the sideloaded relationship (in this case the hasMany relationship model.clients) DOES NOT render to the template while all other model properties (not relationships) DO render to the template. What's weird is that it only does this every once in a while.
I'm not quite sure yet how I can set up a js fiddle for this problem, so I wanted to ask:
Where in the call stack could I set a break point to see what properties will actually get rendered?
I'm using {{debugger}} in the template in question, I'm just not sure where the best place would be to inspect the application state in the call stack.
So, my problem was two-fold. First Problem: Here's my router map and routes:
App.Router.map(function() {
this.resource('users', function() {
this.route('create');
this.resource('user', { path: ':user_id' }, function() {
this.route('edit');
this.route('delete');
});
});
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
// Default for this route
App.UserRoute = Ember.Route.extend({
model: function(params) {
return App.User.find(params.user_id);
}
});
Therefore, when navigating to the route 'clients/3' the DS.JSONSerializer would do an extract() for the UserRoute and an extractMany() for the UsersRoute. However, interestingly enough, most of the time extractMany() (for getting a JSON return of all of the users) would occur before extract() for the single user and its sideloaded properties. When this happened the sideloaded properties would indeed render to the template. However, every once in a while extract() would come before extractMany() (it asynchronosly "beat" the extract many), the sideloaded properties would not render. I think this is because if the extract() occured first that model would then be reset when the extractMany() then occurred for all of the models, which when extracting many do not have sideloaded properties.
I fixed this first problem by doing the following:
App.Router.map(function() {
this.resource('users', function() {
this.route('create');
});
this.resource('user', { path: 'user/:user_id' }, function() {
this.route('edit');
this.route('delete');
});
});
This prevented both models from being extracted in the same route, but the following might have solved both problems.
Second Problem: When navigating away from client/3 to clients and then back to client/3 again, the model would get reset just like the first problem—-sideloaded properties would get dropped.
The way to fix this was to use the UserRoute's activate hook to reload the model.
App.UserRoute = Ember.Route.extend({
activate: function() {
this.modelFor('user').reload();
}
});
This will force the model to be reloaded with the sideloaded properties every time this route 'activates', which is needed of this particular app we're building anyway.
Hope this helps somebody!
You may want to have a custom property that observes your association and print its content in the console.
printRelationship: function() {
console.log(model.clients.get('length'), model.clients);
}.computed(model.clients.#each);