Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating a record with Ember.js & Ember-data & Rails and handling list of records

I'm building an app which has layout like below.

preview

I want to create a new post so I pressed 'new post button' and it took me to 'posts/new' route.

My PostsNewRoute is like below (I followed the method described here)

 App.PostsNewRoute = Ember.Route.extend({
     model: function() {

           // create a separate transaction, 

           var transaction = this.get('store').transaction();

           // create a record using that transaction

           var post = transaction.createRecord(App.Post, {
                title: 'default placeholder title',
                body: 'default placeholder body'
           });

          return post;
     } 
 });

It immediately create a new record, updates the list of the posts, and displays forms for new post.

preview
(source: sunshineunderground.kr)

Now I have two problems.

One is the order of post list.

I expected new post will be on top of the list, but It's on the bottom of the list.

I'm using rails as my backend and I set the Post model order to

 default_scope order('created_at DESC')

so old Post sits below within existing posts. but newly created one is not. (which isn't commited to backend yet)

Another is When I click created post in the list

I can click my newly created post in the posts list. and It took me to a post page with URL

 /posts/null

and It's very weird behavior that I must prevent.

I think there will be two solutions.

  1. When I click 'new post button' create a record and commit to server immediately and when server successfully saved my new record then refresh the posts list and enter the edit mode of newly created post.

  2. or initially set Route's model to null and create a record when I click 'submit' button in a PostsNewView.

  3. or show show only posts whose attribute is

        'isNew' = false, 'isDirty' = false, 
    

in the list..

But sadly, I don't know where to start either way...

for solution 1, I totally get lost.

for solution 2, I don't know how to bind datas in input forms with not yet existing model.

for solution 3, I totally get lost.

Please help me! which will be ember's intended way?

(I heared that Ember is intended to use same solution for every developer)

Update

now I'm using solution 3 and still having ordering issue. Here is my Posts template code.

    <div class="tools">
        {{#linkTo posts.new }}new post button{{/linkTo}}
    </div>
    <ul class="post-list">
        {{#each post in filteredContent}}
        <li>
            {{#linkTo post post }}
                <h3>{{ post.title }}</h3>
                <p class="date">2013/01/13</p>
                <div class="arrow"></div>
            {{/linkTo}}
        </li>
        {{/each}}
    </ul>
    {{outlet}}

Update

I solved this problem by filtering 'arrangedContent' ,not 'content'

 App.PostsController = Ember.ArrayController.extend({
   sortProperties: ['id'],
   sortAscending: false,
   filteredContent: (function() {

     var content = this.get('arrangedContent');

     return content.filter(function(item, index) {
       return !(item.get('isDirty'));
     });
   }).property('content.@each')

 });
like image 856
synthresin Avatar asked Feb 05 '13 10:02

synthresin


People also ask

How do you create an ember object?

To define a new Ember class, call the extend() method on EmberObject : import EmberObject from '@ember/object'; const Person = EmberObject. extend({ say(thing) { alert(thing); } }); This defines a new Person class with a say() method.

Is Ember a backend?

The most important thing to know is that Ember. js is completely backend-agnostic. You could write this part just as well in Ruby, PHP, Python or any other server language. In this lesson though we'll use Node.

What is store in Ember JS?

The store contains all of the data for records loaded from the server. It is also responsible for creating instances of Model that wrap the individual data for a record, so that they can be bound to in your Handlebars templates.

What are models in Ember JS?

In Ember Data, models are objects that represent the underlying data that your application presents to the user. Note that Ember Data models are a different concept than the model method on Routes, although they share the same name.


2 Answers

We use a variation of solution 3 in several places on our app. IMHO it's the cleanest of the 3 as you don't have to worry about setup/teardown on the server side This is how we implement it:

App.PostsController = Ember.ArrayController.extend({
  sortProperties: ['id'],
  sortAscending: true,
  filteredContent: (function() {
    return this.get('content').filter(function(item, index) {
      return !(item.get('isDirty'));
    });
  }).property('content.@each')
});

Then in your Posts template you loop through controller.filteredContent instead of controller.content.

For solution 1, there are many possibilities. You could define the following event:

  createPost: function() {
    var post,
      _this = this;
    post = App.Post.createRecord({});
    post.one('didCreate', function() {
      return Ember.run.next(_this, function() {
        return this.transitionTo("posts.edit", post);
      });
    });
    return post.get("store").commit();
  }

It creates the post then sets up a promise that will be executed once "didCreate" fires on the post. This promise transitions to the post's route only after it has come back from the server, so it will have the correct ID.

like image 124
Andre Malan Avatar answered Oct 04 '22 18:10

Andre Malan


Indeed, very nice write up. Thx for that.

Doesn't your filteredContent have to use the isNew state i.o. isDirty, otherwise the Post that is being edited will not be visible. In either case, the filteredContent property does not work in my case. I also noticed that, since I use an image as part of every element, all images will be refreshed when filteredContent is changed. This means that I see a request for every image.

I use a slightly different approach. I loop through the content and decide whether or not to display the Post in the template:

# posts.handlebars
<ul class='posts'>
  {{#each controller}}
    {{#unless isNew}}
      <li>
        <h3>{{#linkTo post this}}{{title}}{{/linkTo}}</h3>
        <img {{bindAttr src="imageUrl"}}/>
        <a {{action deletePost}} class="delete-post tiny button">Delete</a>
      </li>
    {{/unless}}
  {{/each}}
</ul>

This will only show the Post object after it is saved. The url in the H3-tag also contain the id of the newly created object i.o. posts/null.

One other thing I noticed in your question: instead of passing default values to createRecord, you can use the defaultValues property on the model itself:

So, instead of:

# App.PostsNewRoute
var post = transaction.createRecord(App.Post, {
            title: 'default placeholder title',
            body: 'default placeholder body'
       });

you can do this:

# App.Post
App.Post = DS.Model.extend({
  title: DS.attr('string', {
    defaultValue: "default placeholder title"
  }),
  body: DS.attr('string', {
    defaultValue: "default placeholder body"
  })
});

# App.PostsNewRoute
var post = transaction.createRecord(App.Post);
like image 31
bazzel Avatar answered Oct 04 '22 16:10

bazzel