Getting specific item with findRecord - ember.js

I'm new at ember and as first app I'm trying to build a little online shop.
I can receive "all products" as product overview but not one specific product by id.
I have following in the router.js:
Router.map(function() {
this.route('products');
this.route('product', {path: 'products/:product_id'});
});
My products.js (which works):
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return this.get('store').query('product', {});
}
});
And the product.js (which does generate the problem):
import Ember from 'ember';
export default Ember.Route.extend({
model(params){
return this.store.findRecord('product', params.product_id);
}
});
The project is available under https://github.com/hatchling-shop/hatchling/tree/master/EmberHatchling

After running the code seems that you have an issue in the API in Product.findById() and not in Ember.
In the following method:
Product.findById(id, function(id, err, product) {
if (err) {
res.send(err);
}
res.json({product: product});
});
the params in the callback are wrong, instead you need to remove id and change to:
Product.findById(id, function(err, product) {
if (err) {
res.send(err);
}
res.json({product: product});
});
Hope this helps.

Related

Ember: issue when refreshing the index page

When I refresh the index page of my ember app, it appears that I can't call the functions inside my index.js file located in app/routes.
I don't really know how I can solve this issue.
The source code of index.js:
import Ember from 'ember';
import ProjectRoute from 'web/project-route';
export default ProjectRoute.extend({
authSrv: Ember.inject.service('authentication'),
_title: 'index.title',
_requireAuth: true,
beforeModel()
{
"use strict";
this._super();
},
model()
{
"use strict";
console.log(this.get('authSrv').username);
return {user_id: this.get('authSrv').user_id,
username: this.get('authSrv').username};
}
});
In the code source above we can see that I try to display the username. When I first log onto this page, it display well, but when I refresh the page, it doesn't display anything.
Any thought about it is welcomed!
So I fixed it with the Ember module RSVP. Basically, the main problem was coming from a promise. I didn't waited to catch the promise.
The index.js look like this know.
import Ember from 'ember';
import ProjectRoute from 'web/project-route';
export default ProjectRoute.extend({
authSrv: Ember.inject.service('authentication'),
_title: 'index.title',
_requireAuth: true,
beforeModel()
{
"use strict";
this._super();
},
model()
{
"use strict";
let promise = new Ember.RSVP.Promise((resolve, reject) =>
{
this.get('authSrv').get('current_auth').promise.then((res) =>
{
resolve({user_id: this.get('authSrv').user_id,
username: this.get('authSrv').username});
});
});
return promise;
}
});

Ember nested global routes (Wildcard)

I'm trying to do something like this in my routes:
this.route('products', { path: "/products/*choises"}, function() {
this.route('promotion', {path: "/promotion/*offers"});
});
product route:
offerPath: function(params){
this.transitionTo('product.promotion', params);
}
The problem is that it doesn't matter the promotion that I visit, the app thinks is part of the products route.
How can I do this? I need them to be nested.
Update:
You can use beforeModel(transition) hook in router to check what's in the url.
http://example.com/products/manufacturer-209/series-881/tag-17143/none/494822/f‌​lawless
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel(transition) {
console.log(transition.params.products.choises)
// if you use this url: http://example.com/products/manufacturer-209/series-881/tag-17143/none/494822/f‌​lawless
// console log would be: "manufacturer-209/series-881/tag-17143/none/494822/f‌​lawless"
}
});
At least you have the rest of the url so, you can filter out the important information and redirect with this.transitionTo() to the exact place.
You could have the following route:
http://example.com/products/123/promotions/456
or
http://example.com/products/awesome_souce/promotions/monday_deal
In the first case, your route would look like this:
this.route('product', { path: "/products/:product_id"}, function() {
this.route('promotion', {path: "/promotions/:promotion_id"});
});
In the second case, maybe like this:
this.route('product', { path: "/products/:product_name"}, function() {
this.route('promotion', {path: "/promotions/:promotion_name"});
});
Finally, your route handlers can download the proper models (example for the first case):
// app/routes/product.js
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('product', params.product_id);
}
});
---
// app/routes/product/promotion.js
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
// you can get access to the parent route model if you need for the api query
const product = this.modelFor('product');
return this.store.findRecord('promotion', params.promotion_id);
}
});
If you need only the param from the product route, instead of returning a whole record, for example you can just return params.product_name, so you will have access to a string with this.modelFor('product') in a subroute level.

this.get('content') (inside controller) is undefined after migrating to ember-cli

After migrating from global-namespace-version to ember-cli (0.1.4), my code doesn't work as before. I'm watching the content property in my controller to handle the data, fetched in my route. But nothing happens, the groupedResults function isn't called.
The data is fetched successfully (Ember Inspector shows all projects), so the content property shouldn't be empty.
Router
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
controllerName: 'organization-projects',
model: function() {
return this.store.find('project');
},
renderTemplate: function() {
// render all projects
this.render('organization/projects-list', {
into: 'application'
});
// render toolbar
this.render('organization/toolbar', {
into: 'application',
outlet: 'toolbar'
});
}
});
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
groupedResults: function () {
console.log(this.get('content'));
}.property('content.[]')
});
Are there some breaking changes that I've missed?
Got it: changed controllerName: 'organization-projects' to controllerName: 'organization.projects'.
But I wonder why this worked in my old global-namespace-version.

ember js - Uncaught Error: Assertion Failed: Cannot delegate set

i face this error when saving data to api
Uncaught Error: Assertion Failed: Cannot delegate set('firstName', a) to the 'content' property of object proxy <>: its 'content' is undefined
below is my code
import Ember from 'ember';
export default Ember.ObjectController.extend({
isValid: Ember.computed(
'email',
'firstName',
'lastName',
'twitter',
function() {
return !Ember.isEmpty(this.get('email')) &&
!Ember.isEmpty(this.get('firstName')) &&
!Ember.isEmpty(this.get('lastName')) &&
!Ember.isEmpty(this.get('twitter'));
}
),
actions:{
save: function() {
if (this.get('isValid')) {
var _this = this;
this.get('model').save().then(function(friend) {
_this.transitionToRoute('friends.show', friend);
});
} else {
this.set('errorMessage', 'You have to fill all the fields');
}
},
cancel: function() {
this.transitionToRoute('friends');
}
}
});
Don't use ObjectController. Use simply Ember.Controller.extend.
I see this on the ember-cli-101 book. I encountered the same issue myself. It's likely that you are not properly setting the model attribute in your route. Based on the book, the error either occurs in the edit or new route.
if your router.js looks like this:
...
Router.map(function() {
this.resource('friends', function() {
this.route('new');
this.route('show', { path: ':friend_id' });
this.route('edit', { path: ':friend_id/edit' });
});
});
...
the friends/index route needs to set the model attribute:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('friend');
},
});
and the friends/new route needs to set the model in a different way:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('friend');
},
});
For anyone not familiar with the book (mentioned above) the question is from code that is in the book, which is why I referenced it. In most cases, if you get this issue it is likely because you forgot to or did not set up the model attribute correctly in the appropriate route.

How to load belongsTo/hasMany relationships in route with EmberJS

In my EmberJS application I am displaying a list of Appointments. In an action in the AppointmentController I need to get the appointments owner, but the owner always returns "undefined".
My files:
models/appointment.js
import DS from 'ember-data';
export default DS.Model.extend({
appointmentStatus: DS.attr('number'),
owner: DS.hasMany('person'),
date: DS.attr('Date')
});
models/person.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string')
});
templates/appointmentlist.js
{{#each appointment in controller}}
<div>
{{appointment.date}} <button type="button" {{action 'doIt'}}>Do something!</button>
</div>
{{/each }}
controllers/appointmentlist.js
export default Ember.ArrayController.extend({
itemController: 'appointment'
});
controllers/appointment.js
export default Ember.ObjectController.extend({
actions:{
doIt: function(){
var appointment = this.get('model');
var owner = appointment.get('owner'); //returns undefined
//Do something with owner
}
}
});
Now, I know I can change the owner-property to owner: DS.hasMany('person', {async: true}), and then handle the promise returned from appointment.get('owner');, but that is not what I want.
I have discovered that if I do this {{appointment.owner}} or this {{appointment.owner.name}} in the appointmentlist template, the owner record is fetched from the server. So I guess Ember does not load relationships unless they are used in the template.
I think that the solution to my problem is to use the appointmentlists route to fetch the record in the belongsTo relationship. But I can't figure out how.
Maybe something like this?
routes/appointmentlist.js
export default Ember.Route.extend({
model: function() {
return this.store.find('appointment');
},
afterModel: function(appointments){
//what to do
}
});
EDIT
I did this:
routes/appointmentlist.js
export default Ember.Route.extend({
model: function() {
return this.store.find('appointment');
},
afterModel: function(appointments){
$.each(appointments.content, function(i, appointment){
var owner= appointment.get('owner')
});
}
});
and it works, but I do not like the solution...
You are still asynchronously loading those records, so if you are fast enough you could still get undefined. It'd be better to return a promise from the afterModel hook, or just modify the model hook to do it all.
model: function() {
return this.store.find('appointment').then(function(appointments){
return Ember.RSVP.all(appointments.getEach('owner')).then(function(){
return appointments;
});
});
}
or
model: function() {
return this.store.find('appointment');
},
afterModel: function(model, transition){
return Ember.RSVP.all(model.getEach('owner'));
}
Another way to go is:
export default Ember.Controller.extend({
modelChanged: function(){
this.set('loadingRelations',true);
Ember.RSVP.all(this.get('model').getEach('owner')).then(()=>{
this.set('loadingRelations',false);
});
}.observes('model')
});
This way the transition finishes faster and the relations are loaded afterwards. The loading-state can be observed through loadingRelations.
When there are a lot of relations to load I think this gives a better UX.
You want to load all the assocations in the route, because you want to use Fastboot for search engines and better first time site opened experience.
Holding your assocation loading after primary models are loaded, might not be the best decision.
I am using a syntax to load all assocations in the route:
let store = this.store;
let pagePromise = store.findRecord('page', params.page_id);
let pageItemsPromise = pagePromise.then(function(page) {
return page.get('pageItems');
});
return this.hashPromises({
page: pagePromise,
pageItems: pageItemsPromise
});
And for this.hashPromises I got a mixin:
import Ember from 'ember';
export default Ember.Mixin.create({
hashPromises: function(hash) {
let keys = Object.keys(hash);
return Ember.RSVP.hashSettled(hash).then(function(vals) {
let returnedHash = {};
keys.forEach(function(key) {
returnedHash[key] = vals[key].value;
});
return returnedHash;
});
}
});