Passing a relationship model through link-to helper? ember 2 - ember.js

Heyy!!
I'm having trouble passing a model to a route in ember cli. I'm making a simple app where posts have and author and a title. When you click the title you go to the post details and when you click the author you go to the author's profile. My problem is that I go to the respective user but when I refresh the page I get a n error in the author route. I have no idea why, I'm guessing it has to do with the model not being fetched again when I refresh since it passes the model using link-to helper
My code (client):
app/models/author.js
import DS from 'ember-data';
export default DS.Model.extend({
posts: DS.hasMany('post', {async: true}),
name: DS.attr('string'),
url: DS.attr('string')
});
app/models/post.js
import DS from 'ember-data';
var attr= DS.attr;
export default DS.Model.extend({
author: DS.belongsTo('author'),
title: attr('string'),
description: attr('string'),
date: attr('date'),
url:attr('string'),
});
app/routes/author.js
import Ember from 'ember';
export default Ember.Route.extend({
setupController: function(controller, model) {
model.reload();
controller.set('model', model);}
});
app/templates/posts.hbs
<div class="container" style="width:70%">
{{#each model as |post|}}
<div class="well">
<div class="media">
<a class="pull-left" >
<img class="media-object" src={{post.url}} style="width:200px;height:200px">
</a>
<div class="media-body">
<h1>{{#link-to 'post' post}}{{post.title}}{{/link-to}}</h1>
<h4>Posted by: {{#link-to 'author' post.author.id}} {{post.author.name}}{{/link-to}} </h4>
<p>{{post.description}}</p>
</div>
</div>
</div>
{{/each}}
</div>
My Code (server):
var authors=[];//list of authors
var profileRouter= express.Router();
profileRouter.get('/', function(req, res) {
res.send({
'authors':authors
});
});
profileRouter.get('/:id', function(req, res) {
res.send({
'author': authors.find(function(user){
return author.id==req.params.id
// id: req.params.id,
})
});
});
app.use('/api/author', profileRouter);

You are correct that link-to is passing the model, which is not happening when the page is refreshed. You need to define the model hook on the author route (which is not called when the model is passed) -
model: function(params) {
return this.store.find('author', params.id);
}

Related

Model hook throwing error. Ember

In my application users have many meals. meals belong to a user. I have it so that a user can currently post a meal by creating a record and saving it. Immediately after completing this my console threw the error:
Error: Assertion Failed: You need to pass a model name to the store's modelFor method at new Error (native)
I then wrote my model hook for getting meals, when I started to get the error:
TypeError: Cannot read property 'some' of undefined
I am not certain what is causing this. Here is my code:
User Model:
import DS from 'ember-data';
import { hasMany } from 'ember-data/relationships';
export default DS.Model.extend({
email: DS.attr('string'),
height: DS.attr('number'),
weight: DS.attr('number'),
age: DS.attr('number'),
tdee: DS.attr('number'),
gender: DS.attr('string'),
activity_level: DS.attr('number'),
meals: hasMany('meal')
});
Meal model:
import DS from 'ember-data';
import { belongsTo, hasMany } from 'ember-data/relationships';
export default DS.Model.extend({
meal_type: DS.attr('string'),
created_at: DS.attr('date'),
user: belongsTo('user'),
// meal_items: hasMany('meal_item')
});
Meal route:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('meal');
},
actions: {
createMeal(data) {
let meal = this.get('store').createRecord('meal', data);
meal.save();
}
}
});
I am new to ember, but have a hunch it may be related to explicit inverses? An advice is appreciated. I can post more code if something needs clarification.
EDIT:
The GET request to the server successful. The meal data is in the DS.
So I pass the createMeal action down to my create-meal component:
{{create-meal meals=model createMeal="createMeal"}}
The component sends this action back up on submit:
import Ember from 'ember';
export default Ember.Component.extend({
form: {},
actions: {
submit (){
this.sendAction('createMeal', this.get('form'));
}
}
});
Here is my template for that component:
<label class="col-md-3">Create A Meal</label>
<div class="input-group col-md-8 col-centered create-meal-div">
{{input value=form.meal_type type="text" class="form-control" placeholder="Enter Meal Name" aria-describedby="basic-addon2"}}
<span class="input-group-addon" id="basic-addon2"><button type="submit" class="btn-primary" {{action "submit" form on "submit"}}> Start Adding Food</button></span>
</div>
<div class="col-md-6 food-search-div">
{{food-search}}
</div>
EDIT 2:
rails controller meal create action:
def create
# binding.pry
#meal = current_user.meals.build(meal_params)
if #meal.save
render json: #meal, status: :created, location: #meal
else
render json: #meal.errors, status: :unprocessable_entity
end
end
SOLVED:
REMOVING the user: belongsTo('user'), in my meal model go rid of the error.
I think the problem is here in this line - router
return this.get('store').findAll('meal');
Try this but it is weird it should work
model() {
return this.store.findAll('meal');
}
Inside controllers you need to use this.get('store') when you are inside route just this.store. Try that and let me know if it works. Also inside your route set the action to accept the model as param, like this
actions: {
update(model){
model.save().then( ....
This can be triggered from template like this
<form {{action 'update' model on='submit'}} autocomplete="off">
In this way you do not need to get this.store in your route - you will have a model passed and you can just go to save
Hope it helps
EDIT: also this would prevent ember cli to precompile
user: belongsTo('user'),
// meal_items: hasMany('meal_item')
so it should be without ,
user: belongsTo('user')
// meal_items: hasMany('meal_item')

Uncaught Error: No model was found for 'model'

I'm building an Ember-CLI app using the following:
DEBUG: Ember : 1.10.0
DEBUG: Ember Data : 1.0.0-beta.15
DEBUG: jQuery : 2.1.3
Using a form, I'm trying to save changes on 2 separate models.
One of the models (the user model) saves successfully, while the other (profile model) throws this error:
Uncaught Error: No model was found for 'userProfile'
Models
The two models in question are:
models/user.js
models/user/profile.js
user model:
import DS from "ember-data";
export default DS.Model.extend({
email: DS.attr('string'),
username: DS.attr('string'),
firstname: DS.attr('string'),
lastname: DS.attr('string'),
comments: DS.hasMany('comments'),
});
profile model:
import DS from "ember-data";
export default DS.Model.extend({
avatar: DS.attr('string'),
educationDegree: DS.attr('string'),
educationUniversity: DS.attr('string'),
workRole: DS.attr('string'),
workOrganisation: DS.attr('string'),
interests: DS.attr('string'),
});
Controller
import Ember from "ember";
export default Ember.Controller.extend({
saved:false,
actions: {
save:function(){
this.get('model.user').save();
this.get('model.profile').save();
this.set('saved',true);
},
},
});
Route
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function(){
var _this = this;
var currentUser = this.get('session.user');
return new Ember.RSVP.all([
_this.store.find('user', currentUser.id),
_this.store.find('user.profile', {UserId: currentUser.id}),
]).then(function(values){
return {
user: values[0],
profile: values[1].get('firstObject'),
}
});
},
});
Template
<form {{action "save" on="submit"}}>
{{input type="text" placeholder="First Name" value=model.user.firstname}}
{{input type="text" placeholder="Last Name" value=model.user.lastname}}
{{input type="email" placeholder="Email" value=model.user.email}}
{{input type="text" placeholder="Affiliation" value=model.profile.workOrganisation}}
<button type="submit" class="btn teal white-text">Save</button>
{{#if saved}}
<p class="text-valid">Save Successful.</p>
{{/if}}
</form>
This error occurs because Ember Data cannot find a model into which to insert the data coming back from the PUT ensuing from the save, which I assume looks like
{ userProfile: { ... } }
I don't know the exact rules by which Ember looks up models based on these "root keys" such as userProfile, but I doubt if it can find the profile model hiding down underneath models/user/.
In the past the following has worked for me, if you have control over the server:
{ "user/profile": { ... } }
If you can't change the server response, or this fails to work for some other reason, the simplest thing to do is to move the profile model up to the top level of the models directory and name it user-profile.js.
Another alternative is to play with modelNameFromPayloadKey:
// serializers/application.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
modelNameFromPayloadKey: function(payloadKey) {
if (payloadKey === 'userProfile') payloadKey = 'user/profile';
return this._super(payloadKey);
}
});

Ember select not binding to a property in the model

If I understand the ember.js documentation correctly then I should see the models systemStatus value get populated, but I'm not:
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Description</label>
<div class="col-sm-10">
{{view "select" content=statuses value=model.systemStatus }}
</div>
</div>
This is the controller:
import Ember from "ember";
export default Ember.Controller.extend({
statuses: ["Being Built", "Active","Inactive"],
selectedSystemStatus: 'Active',
actions: {
save: function() {
// this.model.set('systemStatus', this.selectedStatus);
var s = this.get('selectedSystemStatus');
this.model.save();
},
cancel: function() {
}
}
});
The model:
import DS from "ember-data";
export default DS.Model.extend({
name: DS.attr('string', {defaultValue: 'Hello'}),
systemStatus: DS.attr('string', {defaultValue: 'Active'}),
description: DS.attr('string', {defaultValue: 'Describe me'})
});
The router:
import Ember from "ember";
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('software-system');
}
});
Everything works up until you try to select an option from the UI. I'm not sure what I'm doing wrong here, and would like some help.
do you have any place where you call this.store.find('my-model')? This is where the model gets populated.
The default way would be to put it into a corresponding route in the model hook:
// routes/my-model.js
export default Ember.Route.extend({
model: function() {
return this.store.find('my-model');
}
});
With this approach, your controller will wait until the model is loaded.

Computed property on a model in a different controller

I'm building the mandatory TODO app to learn ember.
I have tasks and tags in a belongsTo/hasMany relationship (each tag hasMany tasks). When showing tasks, I want to show a computed property on each available tag.
Models:
App.Tag = DS.Model.extend({
tasks: DS.hasMany('task', {async: true}),
..
});
App.Task = DS.Model.extend({
tag: DS.belongsTo('tag', {async: true}),
..
});
Route:
App.TasksRoute = Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
tasks: this.store.find('task'),
tags: this.store.find('tag')
});
},
setupController: function(controller, model) {
this.controllerFor('tasks').set('content', model.tasks);
this.controllerFor('tags').set('content', model.tags);
}
});
Tags controller:
App.TagsController = Ember.ArrayController.extend({
needs: ["tag"]
})
Tag controller:
App.TagController = Ember.ObjectController.extend({
taskCount: function() {
// FOLLOWING DOES NOT WORK
return this.get('tasks.length')
}.property('tasks')
});
Tag partial:
<ul>
{{#each tag in model}}
<li>
{{tag.name}} ({{controllers.tag.taskCount}} tasks)
</li>
{{/each}}
</ul>
The computed property 'taskCount' does not work. There is something wrong with 'this'.
Is this canonical way of doing it? And if so, what is wrong? Thanks
EDIT: FIXED
I had missed out
App.ApplicationSerializer = DS.ActiveModelSerializer.extend();
And I've used render to get the controller decoration:
{{render 'tag' tag}}
which calls the controller before rendering

Link route to single object, without id

I have prepared this jsfiddle. This is the code:
App = Ember.Application.create();
App.Store = DS.Store.extend({
revision: 13,
adapter: 'DS.FixtureAdapter'
});
App.Router.map(function() {
// put your routes here
});
App.User = DS.Model.extend({
name : DS.attr('string'),
email : DS.attr('string'),
});
App.User.FIXTURES = [{ id: 'me', name: 'Max Smith', email: 'max.smith#email.com' }];
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
And this is the template:
<script type="text/x-handlebars" data-template-name="index">
{{#each model}}
<div class="form_labels_wrapper">
<dl class="dl-horizontal">
<dt>Id:</dt> <dd>{{id}}</dd>
<dt>Name:</dt> <dd>{{name}}</dd>
<dt>Email:</dt> <dd>{{email}}</dd>
</dl>
{{/each}}
</div>
</script>
What I want to do is to display the data of a single user. The opbject has no id (it represents the logged in user, which is session related, and has no id visible for ember). Instead, I am forced to create a list of users (with one user), and give it a fake id (me). This makes no sense in my application.
I would like to use this template, but I have no idea how to configure ember for this:
<script type="text/x-handlebars" data-template-name="index">
<div class="form_labels_wrapper">
<dl class="dl-horizontal">
<dt>Name:</dt> <dd>{{name}}</dd>
<dt>Email:</dt> <dd>{{email}}</dd>
</dl>
</div>
</script>
With this fixture:
App.User.FIXTURES = { name: 'Max Smith', email: 'max.smith#email.com' };
Note that this is a single element, and that I am not looping through the models with #each, because this should not be a list: there is only a single element.
Ember refuses to accept this. What can I do to get this to work?
You can returning an array of records from the model hook, as a result ember is generating an ArrayController for it, which expects it's content to be an array.
Change the model hook to return the single record. For instance using me as the id.
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.User.find('me');
}
});
Then your template works. See the updated jsfiddle.