简体   繁体   中英

Ember JS - Having trouble loading multiple models into a route with belongsTo relationship

I'm stuck in this situation where I cant set up my data correctly. I want to be able to load all my posts, and have the author data load with each post as well. These are my models.

App.Post = DS.Model.extend({
    authorId:   DS.attr('number'),
    author:     DS.belongsTo('author'),
    title:      DS.attr('string'),
    body:       DS.attr('string'),
    snippet:    DS.attr('string'),
    postDate:   DS.attr('date'),
    imageName:  DS.attr('string'),

    postSnippit: Ember.computed();
    imageUrl: Ember.computed()})
});


App.Author = DS.Model.extend({
    name:        DS.attr('string'),
    imageName:   DS.attr('string'),
    posts:       DS.hasMany('post')
});

and heres the relevant part of my router...

App.Router.map(function() {
    this.route('blog', {path: '/blog'}, function() {
        this.resource('posts', { path: '/' });
        this.resource('post', {path: '/:id'});
    });
});

I have it setup correctly for a single post like so ...

App.PostRoute = Ember.Route.extend({
    setupController: function(controller, model){
        this._super(controller, model);
        controller.set('post', model);
        controller.set('author', this.get('author'));
    },
    afterModel: function(){
        Ember.run.scheduleOnce('afterRender', this, scrollToNav);
        var self = this;
        var id = this.get('model.authorId');
        return this.store.find('author', id).then(function(result){
            self.set('author', result);
        });
    },
    model: function(params){
        return this.store.find('post', params.id);
    }
});

So what happens now, is I am loading a list of all posts as the model for 'blog'. The route has a sidebar on the left with a list of all the blog posts, and an outlet on the right side that defaults to loading the 'posts' route. I need the same info for both routes, a list of all posts, as well as the author information. The outlet loads the current post and replaces the 'posts' template, but retains the list of posts on the left sidebar. Currently I don't have the JSON data sideloaded because I couldn't figure it out either, so right now the data comes like this...

{
    "posts": [
        {
            "id": "1",
            "authorId": "1",
            "title": "Title 1",
            "body": "This is the body 1",
            "uploadDate": "2015-06-03 19:26:15",
            "imageName": "image1.jpg"
        },
        {
            "id": "2",
            "authorId": "2",
            "title": "Title 2",
            "body": "This is the body 2",
            "uploadDate": "2015-06-03 19:26:15",
            "imageName": "image2.jpg"
        }
    ]
}

and then there is a separate call for authors...

{
    "authors": [
        {
            "id": "1",
            "name": "John Smith",
            "email": "jsmith@gmail.com",
            "imageName": "image1.jpg",
            "gender": "M",
            "bio": "John Smith is an awesome dude who went to awesome school!",
            "facebookUrl": "null",
            "twitterUrl": null,
            "linkedinUrl": null
        }
    ]
}

And now here are my current route objects (after going back and forth through 1million things).

App.BlogRoute = Ember.Route.extend({
    model: function() {
        var store = this.store;
        var posts = store.findAll('post');
        return posts;

        /* *********************************
        **Tried this, keep getting an error saying 'content.slice' is not a function**
        *
        var authors = store.findAll('author');

         return Ember.RSVP.hash({
            posts: posts,
            authors: authors
        }).then(function(hash){
            return hash;
        }, function(reason){
            console.log(reason);
        });
        ********************************* */

    },
    setupController: function(controller, model) {
        this._super(controller, model);
        controller.set('model', model);
        controller.set('authors', this.get('authors')); //This doesnt do much, and I dont think its necessary.

        /* *********************************
        **Also tried this with the RSVP.hash function, would not render because of 'content.slice' error**

        controller.set('model', model.posts);
        controller.set('authors', model.authors)
        ********************************* */

    },
    afterModel: function(model) {
        Ember.run.scheduleOnce('afterRender', this, scrollToNav);
        var self = this;
        return this.store.findAll('author').then(function(result){
            self.set('authors', result);

        });
    }
});

App.PostsRoute = Ember.Route.extend({
    afterModel: function() {
        Ember.run.scheduleOnce('afterRender', this, scrollToNav);
    },
    model: function() {
        return this.modelFor('blog');
    }
});

The current behavior that happens, is that everything loads in the template, except for the author information. It only loads AFTER the actual post has been visited (ex: 'posts/1'), and then from then on, if I visit the posts or blog route, the author name will stay there.

I can't figure out how to make the information display all the time. From what I can see in the ember inspector, the data for both is being loaded! Ik there is probably something simple I'm missing. Please let me know if you guys have a solution.

There is also a secondary problem that i'm sure is somehow linked. No matter what method I choose to set data to the controller, (ex: controller.set('posts', posts) I cannot reference that data from the template in an each loop by calling it the given name. (ex: {{#each posts as |post|}} should be the correct format}} Please correct me if that's not the correct way, or even what is the intended way to do it.

And just for reference, here is the relevant parts of the templates for blogs, posts, and post.

<script type="text/x-handlebars" id="blog">
    <div class="container-fluid">
        <h3>Posts</h3>
        <ul>
            {{#each model as |post|}}
            <li>
                {{#link-to 'post' post}}
                <h3>{{post.title}}</h3>
                <h4>{{post.author.name}}</h4>
                <h5>{{format-date post.date}}</h5>
                {{/link-to}}
            </li>
            {{else}}
                No Models Loaded!
            {{/each}}
        </ul>
    </div>
    <div class="container-fluid">
        {{outlet}}
    </div>
</script>

<script type="text/x-handlebars" id="posts">
    <h1>Recent Blog Posts</h1>
    {{#each model as |post|}}
    <h1>{{post.title}}</h1>
    <h4>By: {{post.author.name}} </h4>
    <h4>Posted: {{format-date post.date}} </h4>

    <img src={{post.imageUrl}} class="img-responsive img-thumbnail">

    <p class="indent">
        {{post.body}}
    </p>

    {{#link-to 'post' post}}Read More{{/link-to}}
    {{/each}}
</script>

<script type="text/x-handlebars" id="post">
    {{#link-to 'posts' class="pull-right"}}<span class="glyphicon glyphicon-chevron-left"></span>Back{{/link-to}}

    <h1>{{post.title}}</h1>
    <h4>Written By: {{author.name}} </h4>
    <h4>Posted: {{format-date post.date}} </h4>
    <hr/>
    <div class="img-container center">
        <img src={{post.imageUrl}} class="img-responsive img-thumbnail">
    </div>
    <br/>
    <p class="indent">
        {{post.body}}
    </p>
</script>

Note: I tried a few different versions of Ember, and no matter what version I use I get the "content.slice is not a function" error, so I'm sure something I' m doing is wrong, but here is some extra info just incase :D!

VERSION INFO:

Ember Inspector:      1.8.1
Ember:                1.13.0-beta.2+76473dd3
Ember Data:           1.0.0-beta.18
jQuery:               2.1.4

1) I recommend you should not use store.findAll , since this is private api method. api link . The right way is to use store.find('modelName') instead. For example,

model: function() {
  return this.store.find('post'); // promise to find all posts
}

2) If you want to load all posts and all authors in a route (for example in blog route)

App.BlogRoute = Ember.Route.extend({

  model: function() {
    return Ember.RSVP.hash({
      posts: this.store.find('post'),
      authors: this.store.find('author')
    });
  },

  setupController: function(controller, modelHash) {
    controller.setProperties(modelHash);
    // or directly 
    // controller.set('posts', modelHash.posts);
    // controller.set('authors', modelHash.authors);
    // even
    // this.controllerFor('posts').set('authors', modelHash.authors);
  }
}); 

3) I'd simplify post route code:

App.PostRoute = Ember.Route.extend({
  model: function(params){
    return this.store.find('post', params.id);
  },

  afterModel: function(model){
    var self = this;
    return model.get('author').then(function(author) {
        self.controllerFor('post').set('author', author);
    });
  }
  // setupController not needed here
});

4) You may need more steps. Jsbin would be helpful to give accurate answer.

UPD 08/jun/15: Looks like findAll will become public soon https://github.com/emberjs/data/pull/3234 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM