Add second model to a route - ember.js

My application is for swimming lessons. I need to add swimmers to a class. The relationship is has many in both directions. A Lesson can have many Swimmers and Swimmers can have many Lessons.
From the lesson route, I would like to select a swimmer in a drop down, from a list of all swimmers and have an action add that swimmer's ID to the lesson's "swimmers" array.
I can't get the swimmers to show up in the drop down field because I don't think I am loading the second model correctly.
I would also be open to suggestions of how to add a specific swimmer to a specific class. It's important to see all available swimmers
I am new to both ember and programming so please keep this in mind when making suggestions. Thank you!
App.Router.map(function() {
this.resource('lessons', { path: '/lessons' }, function() {
this.resource('lesson', { path: '/:lesson_id' })
this.route('new', { path: '/new' })
});
this.resource('swimmers', { path: '/swimmers' }, function() {
this.resource('swimmer', { path: '/:swimmer_id' })
this.route('new', { path: '/new' })
});
});
App.Lesson = DS.Model.extend({
type: DS.attr(),
name: DS.attr(),
/*level: DS.attr(), sometimes there are hybrid levels, likely leave it up to */
instructor:DS.belongsTo('instructor', {async: true}),
startDate: DS.attr(),
endDate: DS.attr(),
capacity: DS.attr('number'),
swimmers: DS.hasMany('swimmer',{async: true}),
});
App.Swimmer = DS.Model.extend({
nameFirst: DS.attr(),
nameLast: DS.attr(),
level: DS.attr(),
birthdate: DS.attr(),
gender: DS.attr(),
note:DS.attr(),
lessons: DS.hasMany('lesson', {async: true}),
});
App.LessonRoute = Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
lesson: this.store.find('lesson', params.lesson_id),
swimmers: this.store.findAll('swimmer')
})
},
setupController: function(controller, model) {
controller.set('model', model.lesson);
controller.set('swimmer', model.swimmer);
},
});
Drop down I am trying to use
<div class="form-group">
<label for="lesson_swimmers" class="col-sm-2 control- label">Swimmers</label>
<div class="col-sm-9">
{{view Ember.Select content=swimmers optionLabelPath="content.fullName" class="form-control" id="lesson_swimmers"}}
</div>
</div>

There are two answers to this question. The right one for you will depend on how you are providing the Model.
If you are arriving via transferTo or linkTo you will need to use a different approach than if you are arriving directly via URL.
#Kingpin2k answers the question and provides examples here,
Kingpin2k's elegant solution.

Related

Sorting data by timestamp on ember

I've looked at a few examples, but haven't been able to reverse-sort, so that newly generated objects are on top.
My sortable items are in components, and I don't think I'm passing sortProperties & sortAscending correctly.
lavender.js:
export default Ember.Controller.extend({
needs: ['application'],
sortProperties: ['timestamp'],
sortAscending: false
});
lavender.hbs
{{#each model.comment as |comment|}}
{{comment-thread message=comment.message user=comment.user timestamp=comment.timestamp sortProperties=sortProperties sortAscending=sortAscending}}
{{/each}}
comment.js
export default DS.Model.extend({
message: DS.attr('string'),
timestamp: DS.attr('date'),
user: DS.belongsTo('user', {async: true}),
todo: DS.belongsTo('todo', {async: true}),
});
todo.js (model for lavender.js)
export default DS.Model.extend({
title: DS.attr('string'),
isCompleted: DS.attr('boolean', {defaultValue: false}),
detail: DS.attr('string', {defaultValue: "add details.."}),
comment: DS.hasMany('comment', {async: true}),
});
There must be something I'm not seeing.. thank you!
You have to use deprecated Ember.ArrayController instead of Ember.Controller if you want your approach to work or you can choose other approach.
The best approach is to use Ember.computed macro:
export default Ember.Controller.extend({
needs: ['application'],
commentsSorting: ['timestamp:desc'],
comments: Ember.computed.sort('model.comment', 'commentsSorting')
});
Then, instead of model, iterate over comments in your template.
You can also use computed property and private(discouraged) Ember.ArrayProxy, like this:
export default Ember.Controller.extend({
needs: ['application'],
comments: Ember.computed('model', 'model.comment', function() {
return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['timestamp'],
sortAscending: false,
content: this.get('model.comment')
});
})
});
Then you can iterate over comments in your template:
{{#each model.comment as |comment|}}
{{comment-thread message=comment.message user=comment.user timestamp=comment.timestamp}}
{{/each}}
I don't think you need to pass sort properties to comment-thread, I don't you've misunderstood how this works. It gets sorted in controller, where are all records, not in component, where you have only 1 record per 1 component and no reference to other records.

Nested resources in ember.js, adding comments to a post with fixture data

I'm trying to write a simple blog with Ember. I can't figure out how to nest comments resource inside the post resource. (I used ember-cli to generate the app)
/app/router.js
var Router = Ember.Router.extend({
location: ENV.locationType
});
Router.map(function() {
this.resource('posts', { path: '/' }, function() {
this.resource('post', { path: 'posts/:post_id' }, function() {
this.resource('comments');
});
});
});
export default Router;
/app/templates/posts.hbs
<div class="col-xs-3">
<h2>Posts</h3>
{{#each}}
<h4>{{#link-to 'post' this}}{{title}}{{/link-to}}</h4>
{{/each}}
</div>
<div class="col-xs-9">
{{outlet}}
</div>
/app/templates/post.hbs
<h2>
{{#if isEditing}}
{{input value=title}}
{{else}}
{{title}}
{{/if}}
</h2>
<p>
{{#if isEditing}}
{{textarea value=body}}
{{else}}
{{body}}
{{/if}}
</p>
<p>
{{publishDate}}
</p>
{{#if isEditing}}
<button {{action 'doneEditing'}}>Save</button>
{{else}}
<button {{action 'edit'}}>Edit</button>
{{/if}}
{{!-- Should be outlet to Comments? --}}
{{outlet}}
/app/templates/comments.hbs
<h1>Comments</h1>
/app/model/post.js
var Post = DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
magic: DS.attr('string'),
publishDate: DS.attr('date'),
comments: DS.hasMany('comment')
});
Post.reopenClass({
FIXTURES: [
{
id: 1,
title: "Writing a blog in Ember",
body: "I am writting a blog",
magic: "heloo212",
publishDate: "05/22/2104",
comments: [1, 2]
},
{
id: 2,
title: "I'm shouting Ember",
body: "I am shouting about Ember",
publishDate: "05/22/2104",
comments: 3
}
]
});
export default Post;
/app/models/comment.js
var Comment = DS.Model.extend({
body: DS.attr('string'),
author: DS.attr('string'),
createdAt: DS.attr('date'),
post: DS.belongsTo('post')
})
Comment.reopenClass({
FIXTURES: [
{
id: 1,
body: "Woohoo",
author: "Matthew",
createdAt: "01/01/2015",
post: 1
},
{
id: 2,
body: "Great Stuff",
author: "Mark",
createdAt: "01/02/2015",
post: 1
},
{
id: 3,
body: "A comment",
author: "Luke",
createdAt: "01/04/2015",
post: 2
}
]
});
export default Comment;
app/routes/comments.js
var CommentsRoute = Ember.Route.extend({
model: function() {
return this.store.find('comment');
}
});
export default CommentsRoute;
I expected to see my comments.hbs template appearing at the bottom of the post.hbs (currently just trying to put out Comments but I don't see anything and ember inspector is telling me:
Could not find "post.index" template or view. Nothing will be rendered
Object {fullName: "template:post.index"}
Nested routes/resources aren't automatically rendered. Think of this example
Router.map(function() {
this.resource('posts', { path: '/' }, function() {
this.resource('post', { path: 'posts/:post_id' }, function() {
this.resource('comments');
this.resource('somethings');
});
});
});
There are multiple resources/routes under the post, either or could be rendered into the outlet.
For your particular case, there are a couple of things we'll need to fix.
First you'll need to mark comments as async, due to your json returning ids.
App.Post = DS.Model.extend({
title: DS.attr('string'),
body: DS.attr('string'),
magic: DS.attr('string'),
publishDate: DS.attr('date'),
comments: DS.hasMany('comment', {async:true})
});
Second we'll want to set up routes for each resource/route that needs a model
App.PostsRoute = Em.Route.extend({
model: function() {
return this.store.find('post');
}
});
App.PostRoute = Em.Route.extend({
model: function(params){
return this.store.find('post', params.post_id);
},
serialize: function(model){
return {post_id:model.get('id')};
}
})
App.CommentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('post').get('comments');
}
});
And then instead of linking to a single post, we link to comments. And ember will apply the passed in model to the route that has a dynamic model (IE :post_id).
{{#each}}
<h4>{{#link-to 'comments' this}}{{title}}{{/link-to}}</h4>
{{/each}}
http://emberjs.jsbin.com/wekoxapi/1/edit

Ember.js - setupController model no output

Ey guys,
I have a strange problem with one of my models:
The following code is responsible for getting the page model and all the new projects.
Bouwbedrijf.ProjectsOldRoute = Ember.Route.extend({
model: function() {
return App.Page.find(3);
},
setupController: function(controller, model)
{
controller.set('content', model);
controller.set('projecten', App.OldProjects.find());
},
renderTemplate: function (){
this.render();
this.render('pagetitle', {into: 'projects', outlet: 'page-title'});
this.render('oldprojectshowcase', {
into: 'projects/old',
outlet: 'projecten'
});
}
});
However if I loop through the controller.projecten within the template there is no propery output. I do see 3 li elements rendered but I can't seem to display the name of a project...
Here is the template code
<script type="text/x-handlebars" data-template-name="oldprojectshowcase">
<h3>Projects showcase</h3>
<ul>
{{#each project in projecten}}
<li>{{project.name}}</li>
{{/each}}
</ul>
</script>
To make things more clear for you guys I prepared a jsbin -> http://jsbin.com/Uqop/2/
Within this example there is a page Old projects which is displayin a showcase (sort of ;) )
There is no relation between the problem and setupController.
The problem is you are trying to access a property which you have not defined in the model.
The property name is not defined in the App.OldProjects model.
Defining it does the trick.
App.OldProjects = DS.Model.extend({
title: DS.attr('string'),
project: DS.attr('string'),
name: DS.attr('string'),
img: DS.attr('string')
});
Corrections to your JSBin.

duplicated data when navigating without reloading page using ember-data

I'm using ember.js 1.0.0-pre4, ember-data revision 11.
I have the following model:
App.DbProcess = DS.Model.extend({
pid: DS.attr('number'),
backendStart: DS.attr('string'),
transactionStart: DS.attr('string'),
queryStart: DS.attr('string'),
stateChange: DS.attr('string'),
waiting: DS.attr('boolean'),
state: DS.attr('string'),
query: DS.attr('string')
})
With the following route:
App.HomeDbProcessesRoute = Ember.Route.extend({
model: function() {
return App.DbProcess.find();
}
})
I then have a template which uses {{#each controller}}{{/each}} to render all the processes retrieved. However if I navigate to other pages (without reloading the page) and returning back to the processes page, the processes will be retrieved again and the duplicates are rendered on page.
EDIT: I also tried this, but it didn't work:
DS.RESTAdapter.map('App.DbProcess', {
primaryKey: 'pid'
})
I had the same issue now and here is my little hot-fix:
{{#if id}}
<div>
{{title}}
</div>
{{/if}}
In the template I render item from store only if it has id set (only those are coming from databse). But You propably solved it already!
(using revision 12)
Turns out you can do something like this to customize the primary key globally
App.Adapter = DS.RESTAdapter.extend({
url: document.location.protocol+'//url-api.com',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
// If the type is `BlogPost`, this will return
// `blog_post_id`.
var typeString = (''+type).split(".")[1].underscore();
return typeString + "_id";
}
})
})

Ember.js Router v2 dynamic slug

In the following example, using the new Router v2 API, the ember application behaves as expected with one exception. When hovering over the dynamically created links, using a registered #linkTo Handlebars the url shows undefined.
How do I have a slug field in the URL?
Here is the model
App.Todo = DS.Model.extend({
slug: DS.attr('string'),
date: DS.attr('date'),
updated: DS.attr('date'),
task: DS.attr('string'),
description: DS.attr('string')
});
My Router
App.Router.map(function(match){
this.route('index', {path: '/'});
this.resource('todos', {path: '/todos'}, function(){
this.resource('create', {path: '/create'});
this.resource('todo', {path: '/:slug'}, function(){
this.resource('edit', {path: 'edit'});
});
});
});
I know that this does show 'undefined', but this would be a nice (Handlebars)
{{#each todo in tasks}}
<div class="user">
{{#linkTo todo todo.slug}}<h4><i class="icon-list"></i>{{todo.task}}</h4>{{/linkTo}}
<p>{{todo.description}}</p>
</div>
{{/each}}
Thanks for any pointers! I'm using Ember-data also
Here is a example fiddle
http://jsfiddle.net/R2SPs/6/
This works for ember routing v2.1 (01.16.13)
Thanks to rakl on #emberjs on IRC here is a mixin that solves the problem
App.SlugRouter = Ember.Mixin.create({
serialize: function(model, params) {
var name, object;
object = {};
name = params[0];
object[name] = model.get('slug');
return object;
}
});
Now just place that in your Router and your golden
App.TodoRoute = Ember.Route.extend(App.SlugRouter,{
//insert your code
});
The path of the route is "todo.index" with the resource definition:
this.resource('todo', {path: '/:slug'}, ...
So create Route and Controller for it.