I am having two table one is post and other is comment. I have a page with only post list, I need to display a icon for post if it contains comments.
App.Comment = DS.Model.extend({
user_name: DS.attr('string'),
user_id: DS.attr('number'),
comment: DS.attr('string')
});
App.Post = DS.Model.extend({
user_id: DS.attr('number'),
comments: DS.hasMany('App.Comment',{embedded:true}),
This is a sample handlebar
1. testpost <show icon only if comment not empty>
To achieve this you could create a computed property which listens to your comments property if it has at least the firstObject set, this would mean that there are comments, something like this:
App.Post = DS.Model.extend({
user_id: DS.attr('number'),
comments: DS.hasMany('App.Comment',{embedded:true}),
hasComments: function() {
var commentsLength = this.get('comments.length');
return (commentsLength > 0);
}.property('comments.firstObject')
});
And then in your template you add a if helper to listen to the hasComments computed property, this will show the content between the if helper if post.hasComments returns true, this would be when (commentsLength > 0) evaluates to true:
1. testpost {{#if post.hasComments}}<show icon>{{/if}}
EDIT
See here for a working DEMO.
Hope it helps.
Related
I'm trying to post data from my Ember app to my Rails API for a blog application I'm playing with.
I'm having a few issues, which I think each stem from my initial problem.
First of all, when I try to store the data from my inputs in Ember, I can't seem to store them in any variables. I've tried checking to see if I'm trying to access the values the wrong way but the documents I've been reading (Ember docs, and other stack overflow questions) lead me to believe I'm doing this correctly.
//new.js
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
actions:{
createPost(){
var created_at = new Date();
var user = this.store.findRecord('user', 1);
var title = this.get('title');
var body = this.get('body');
console.log(title, body);
// console.log(user);
var post = this.store.createRecord('post', {
title: title,
body: body,
created_at: created_at,
user: user
})
post.save();
}
}
});
and
//new.hbs
{{outlet}}
<form>
<strong>Title</strong><br/>
{{input name="title" value=title}}<br/>
<strong>Body</strong><br/>
{{input name="body" value=body}}<br/>
<button type="submit" {{action 'createPost'}}>Submit</button>
</form>
My output of title and body returns undefined.
Another issue I'm having is when I try post this data to my Rails API, I get a 422 error. It says that there is an unpermitted parameter (created_at) included, so I was wondering if there was a way to exclude parameters from my post request?
Thanks in advance.
I would move this action in controller, than define title and body as property.
import Ember from 'ember';
export default Ember.Controller.extend({
title: null,
body: null,
actions: {
createPost(){
var created_at = new Date();
var user = this.store.findRecord('user', 1);
var title = this.get('title');
var body = this.get('body');
console.log(title, body);
// console.log(user);
var post = this.store.createRecord('post', {
title: title,
body: body,
created_at: created_at,
user: user
})
post.save();
}
}
})
Error is showing because you didn't define title and body.
As for other part of problem with param created_at just remove it from model or from this.store.createRecord() block
The following snippet instantiates a new post record in the model hook which is available to the template.
//new.js
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
model() {
return this.store.createRecord('post');
},
actions:{
createPost(){
var user = this.store.findRecord('user', 1);
var created_at = new Date();
var post = this.currentModel;
post.setProperties({created_at: created_at, user: user});
post.save();
}
}
});
and your template becomes
//new.hbs
{{outlet}}
<form>
<strong>Title</strong><br/>
{{input name="title" value=model.title}}<br/>
<strong>Body</strong><br/>
{{input name="body" value=model.body}}<br/>
<button type="submit" {{action 'createPost'}}>Submit</button>
</form>
The var user = this.store.findRecord('user', 1); line in it's current form will cause issues since findRecord returns a promise.
To exclude the created_atfrom being sent to the API endpoint, create a post serializer
//serializers/post.js
import DS from 'ember-data';
// NOTE: Update the serializer to reflect your project's serializer choice
export default DS.JSONAPISerializer.extend({
attrs: {
created_at: { serialize: false }
}
});
This is how I have created my ember FIXTURE:
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr()
});
App.Category.FIXTURES = [
{
id: 1,
name: 'user1',
email: 'user1#gmail.com',
parent_id: 0
},
{
id: 2,
name: 'user2',
email: 'user2#gmail.com',
parent_id: 1
}
];
Here is a part of my ember view where 'parent-title' is a helper:
{{#each category in controller}}
<tr>
<td>{{category.name}}</td>
<td>{{parent-title category.parent_id}}</td>
<td>Edit/Delete</td>
</tr>
{{/each}}
What I want is that during listing if the parent_id is 0 it should print 'master' else the name of parent category. In my exapmle parent of user2 is id=1 show it should print 'user1'.
Below is the helper I have used:
Ember.Handlebars.helper('parent-title', function(parent_id){
if (parent_id > 0) {
var parent = category.findBy('id', parent_id);
return parent.name;
} else {
return 'master';
}
});
I know if I replace the line App.Category.FIXTURES = [ with var Category = [ I can get it done but I want ot do it with FIXTURES.
I can tell you that accessing data like that is a bad idea. If I were you, I would change parent_id to be a relationship, not an attribute (since that's really what it is). Then you can access the parent's name in templates with category.parent.name. Making it a relationship also gives you a few other luxuries.
But if you want to maintain backward compatibility, try using a computed property.
App.Category = DS.Model.extend({
name: DS.attr(),
parent_id: DS.attr(),
parent: function() {
return DS.PromiseObject.create({
promise: this.get('store').find('category', this.get('parent_id'))
});
}.property('parent_id'),
parent_name: function() {
return this.get('parent.name');
}.property('parent.name')
});
EDIT: If you want to change that into a relationship (which I think it should be), it's fairly simple. Instead of DS.attr, you use DS.belongsTo. For instances.
App.Categor = DS.Model.extend({
name: DS.attr(),
// I dropped the _id part because it's no longer an ID
parent: DS.belongsTo('category', { inverse: null })
});
This tells Ember-Data to interpret the ID you give in the parent field as another category object. So category.get('parent') will return another category object, not a number. But in your case, to make it work, you'll have to convert all of the 0 IDs to null IDs. I wasn't sure if that was possible, which is why I recommended the computed property.
EDIT: To display master in case of a null parent, use the Handlebars if expression.
{{#if parent}}
{{parent.name}}
{{else}}
master
{{/if}}
I have a simple model in Backbone.js with few configurations:
budget.Group = Backbone.Model.extend({
url: '/groups'
})
...now when I do something like this:
budget.Group.save({ id: 1, name: 'Food' });
... it attempts to PUT it to the following URL:
/groups
.. but my (Rails) app would post/put it to:
/groups/1
Do I need to configure my model to update to the correct url? Thanks
You should be doing
var budget = new budget.Group({ id: 1, name: 'Food' });
budget.save();
Backbone will do the rest for you (and use the correct URL).
As the two answers before mentioned you must instantiate your Model :
var budget = new budget.Group({ id: 1, name: 'Food' });
budget.save();
But you have also to change the url property to urlRoot
budget.Group = Backbone.Model.extend({
urlRoot: '/groups'
})
I'm working in my first web app with Ember.js backed with a Rails for API.
I have the following nested resources:
this.resource('selection_processes', function() {
this.resource('selection_process', { path: '/:selection_process_id' }, function() {
this.resource('candidate', { path: '/candidates/:candidate_id' });
});
})
So, when I access selection_processes/1 it's getting all of its candidates. Thats ok, but the problem is when I click on another selection process link Ember does not perform a new request, rendering no data in my templates. Btw, the API is returning the correct objects.
The only way I got this working was including all objects in my serializers, making Ember getting all the data of the whole nested resources in a single request. But this seems to be a lazy practice and "heavy".
By the way, here are my routes:
Safira.SelectionProcessesRoute = Ember.Route.extend({
model: function () {
return this.store.find('selection_process');
}
});
Safira.SelectionProcessRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('selection_process', params.selection_process_id);
}
});
Safira.CandidateRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('candidate', params.candidate_id);
}
});
UPDATE
Here are my models
Safira.SelectionProcess = DS.Model.extend({
beginDate : DS.attr('date'),
endDate : DS.attr('date'),
title : DS.attr('string'),
description : DS.attr('string'),
steps : DS.hasMany('Safira.Step', {async: true})
});
Safira.Step = DS.Model.extend({
realization: DS.attr('date'),
title: DS.attr('string'),
candidates: DS.hasMany('Safira.Candidate', {async: true}),
selection_process : DS.belongsTo('Safira.SelectionProcess')
});
Safira.Candidate = DS.Model.extend({
name : DS.attr('string'),
email : DS.attr('string'),
confirmation_token : DS.attr('string'),
step : DS.belongsTo('Safira.Step')
});
you have to pass same data in both cases.(when you get a collection or individual items).
I think you are returning partial data in case of collection.
When you go through a link it assumes it already has data for that model so it won't send a new request. IMO you should be able to access partial data of that model received in collection.
if the model is big, split it into multiple models. it require server side change also.
I have problems with ember-data. For example, I've created a project at http://localhost/~me/test
In my project I've created a store and a model as follows:
... init stuff here ...
var attr = DS.attr;
App.Person = DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
});
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter,
});
Now when I search (somewhere in my route) for a person like this
var person = App.Person.find(params);
The http://localhost/persons?post_id=10 is called. This one does not exist of course. I would've expected something like http://localhost/~me/test/persons?post_id=10. Even better would be http://localhost/~me/test/persons.php?post_id=10 How can I change this url ?
This is as of Ember Data Beta 3
To take care of the prefix, you can use the namespace property of DS.RESTAdapter. To take care of the suffix, you'll want to customize the buildURL method of DS.RESTAdapter, using _super() to get the original functionality and modifying that. It should look something like this:
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: '~me/test',
buildURL: function() {
var normalURL = this._super.apply(this, arguments);
return normalURL + '.php';
}
});
MilkyWayJoe is right, in your adapter you can define the namespace.
App.Adapter = DS.RESTAdapter.extend({
namespace: '~/me/test'
});
This would work too:
App.Person = DS.Model.extend({
url: '~me/test/persons',
firstName: attr('string'),
lastName: attr('string'),
});
Or if you want to use a namespace and .php path:
App.Adapter = DS.RESTAdapter.extend({
namespace: '~/me/test',
plurals: {
"persons.php": "persons.php",
}
});
App.Person = DS.Model.extend({
url: 'persons.php',
firstName: attr('string'),
lastName: attr('string'),
});
The plurals bit is to make sure Ember Data doesn't add an 's', e.g. person.phps