IT story

EmberJS / Ember Data에서 단일 경로로 여러 모델을 사용하는 방법은 무엇입니까?

hot-time 2020. 9. 15. 19:24
반응형

EmberJS / Ember Data에서 단일 경로로 여러 모델을 사용하는 방법은 무엇입니까?


문서를 읽어 보면 다음과 같이 경로에 모델을 할당해야하거나 할당해야하는 것처럼 보입니다.

App.PostRoute = Ember.Route.extend({
    model: function() {
        return App.Post.find();
    }
});

특정 경로에서 여러 개체를 사용해야하는 경우 어떻게합니까? 즉, 게시물, 댓글 및 사용자? 로드 할 경로를 어떻게 지정합니까?


영원히 마지막 업데이트 : 계속 업데이트 할 수 없습니다. 따라서 이것은 더 이상 사용되지 않으며 이런 식으로 될 것입니다. 여기 스레드 더 최신 더 나은, 그리고 어떻게 같은 노선에 여러 모델을로드 : EmberJS은?

업데이트 : 원래 답변 embedded: true에서 모델 정의에서 사용한다고 말했습니다 . 틀 렸습니다. 개정 12에서 Ember-Data는 단일 레코드 또는 수집을 위해 접미사 ( link ) _id사용하여 외래 키를 정의 할 것으로 예상 _ids합니다. 다음과 유사한 것 :

{
    id: 1,
    title: 'string',
    body: 'string string string string...',
    author_id: 1,
    comment_ids: [1, 2, 3, 6],
    tag_ids: [3,4]
}

나는 바이올린을 업데이트했으며 변경 사항이 있거나이 답변에 제공된 코드에 더 많은 문제가있는 경우 다시 업데이트 할 것입니다.


관련 모델에 대한 답변 :

당신이 설명하는 시나리오의 경우, 내가 의지 할 협회 모델 사이 (설정 embedded: true) 만로드 Post나는 정의 할 수 있습니다 고려하고, 그 길에서 모델을 DS.hasMany협회 Comment모델과 DS.belongsTo에 대한 연결을 User모두에 CommentPost모델. 이 같은:

App.User = DS.Model.extend({
    firstName: DS.attr('string'),
    lastName: DS.attr('string'),
    email: DS.attr('string'),
    posts: DS.hasMany('App.Post'),
    comments: DS.hasMany('App.Comment')
});

App.Post = DS.Model.extend({
    title: DS.attr('string'),
    body: DS.attr('string'),
    author: DS.belongsTo('App.User'),
    comments: DS.hasMany('App.Comment')
});

App.Comment = DS.Model.extend({
    body: DS.attr('string'),
    post: DS.belongsTo('App.Post'),
    author: DS.belongsTo('App.User')
});

이 정의는 다음과 같이 생성됩니다.

모델 간의 연결

이 정의를 사용하면 find게시물 이있을 때마다 해당 게시물과 관련된 댓글 모음, 댓글 작성자 및 게시물 작성자 인 사용자에 액세스 할 수 있습니다 . 경로는 간단합니다.

App.PostsPostRoute = Em.Route.extend({
    model: function(params) {
        return App.Post.find(params.post_id);
    }
});

그래서에서 PostRoute(또는 PostsPostRoute당신이 사용하는 경우 resource), 내 템플릿에 액세스 할 수 있습니다 컨트롤러의 content는 IS, Post나는 단순히 저자 참조 할 수 있도록 모델,author

<script type="text/x-handlebars" data-template-name="posts/post">
    <h3>{{title}}</h3>
    <div>by {{author.fullName}}</div><hr />
    <div>
        {{body}}
    </div>
    {{partial comments}}
</script>

<script type="text/x-handlebars" data-template-name="_comments">
    <h5>Comments</h5>
    {{#each content.comments}}
    <hr />
    <span>
        {{this.body}}<br />
        <small>by {{this.author.fullName}}</small>
    </span>
    {{/each}}
</script>

( 바이올린 참조 )


관련없는 모델에 대한 답변 :

그러나 시나리오가 설명했던 것보다 조금 더 복잡 하거나 특정 경로에 대해 다른 모델을 사용 (또는 쿼리) 해야하는 경우 Route#setupController. 예를 들면 :

App.PostsPostRoute = Em.Route.extend({
    model: function(params) {
        return App.Post.find(params.post_id);
    },
    // in this sample, "model" is an instance of "Post"
    // coming from the model hook above
    setupController: function(controller, model) {
        controller.set('content', model);
        // the "user_id" parameter can come from a global variable for example
        // or you can implement in another way. This is generally where you
        // setup your controller properties and models, or even other models
        // that can be used in your route's template
        controller.set('user', App.User.find(window.user_id));
    }
});

이제 Post 경로에있을 때 템플릿이 후크 user에 설정된대로 컨트롤러 속성에 액세스 할 수 있습니다 setupController.

<script type="text/x-handlebars" data-template-name="posts/post">
    <h3>{{title}}</h3>
    <div>by {{controller.user.fullName}}</div><hr />
    <div>
        {{body}}
    </div>
    {{partial comments}}
</script>

<script type="text/x-handlebars" data-template-name="_comments">
    <h5>Comments</h5>
    {{#each content.comments}}
    <hr />
    <span>
        {{this.body}}<br />
        <small>by {{this.author.fullName}}</small>
    </span>
    {{/each}}
</script>

( 바이올린 참조 )


Em.Object여러 모델을 캡슐화하는 데 사용 하는 것은 모든 데이터를 model후크 로 가져 오는 좋은 방법 입니다. 그러나 뷰 렌더링 후 모든 데이터가 준비되었는지 확인할 수는 없습니다.

또 다른 선택은 Em.RSVP.hash. 여러 약속을 함께 결합하고 새로운 약속을 반환합니다. 모든 약속이 해결 된 후 해결 된 경우 새 약속입니다. 그리고 setupController약속이 해결되거나 거부 될 때까지 호출되지 않습니다.

App.PostRoute = Em.Route.extend({
  model: function(params) {
    return Em.RSVP.hash({
      post:     // promise to get post
      comments: // promise to get comments,
      user:     // promise to get user
    });
  },

  setupController: function(controller, model) {
    // You can use model.post to get post, etc
    // Since the model is a plain object you can just use setProperties
    controller.setProperties(model);
  }
});

이런 식으로 뷰 렌더링 전에 모든 모델을 가져옵니다. 그리고 사용하는 Em.Object것은 이러한 이점이 없습니다.

또 다른 장점은 약속과 비 약속을 결합 할 수 있다는 것입니다. 이렇게 :

Em.RSVP.hash({
  post: // non-promise object
  user: // promise object
});

자세한 내용은 https://github.com/tildeio/rsvp.js 를 확인하십시오 Em.RSVP.


그러나 경로에 동적 세그먼트가있는 경우 Em.Object또는 Em.RSVP솔루션을 사용하지 마십시오.

주요 문제는 link-to. link-to모델로 생성 된 링크를 클릭하여 URL을 변경 하면 모델이 해당 경로로 직접 전달됩니다. 이 경우 model후크가 호출되지 않고 setupController모델이 link-to제공됩니다.

예는 다음과 같습니다.

경로 코드 :

App.Router.map(function() {
  this.route('/post/:post_id');
});

App.PostRoute = Em.Route.extend({
  model: function(params) {
    return Em.RSVP.hash({
      post: App.Post.find(params.post_id),
      user: // use whatever to get user object
    });
  },

  setupController: function(controller, model) {
    // Guess what the model is in this case?
    console.log(model);
  }
});

그리고 link-to코드, 게시물은 모델입니다.

{{#link-to "post" post}}Some post{{/link-to}}

여기서 상황이 흥미로워집니다. URL /post/1사용 하여 페이지를 방문하면 model후크가 호출되고 setupControllerpromise가 해결되면 일반 객체를 가져옵니다.

그러나 link-to링크 를 클릭하여 페이지를 방문하면 post모델을 전달 PostRoute하고 경로는 model후크 를 무시합니다 . 이 경우 setupController얻을 것이다 post물론 사용자를 얻을 수의 모델을.

따라서 동적 세그먼트가있는 경로에서 사용하지 마십시오.


잠시 동안을 사용 Em.RSVP.hash하고 있었지만 문제는 렌더링 전에 모든 모델이로드 될 때까지 뷰를 기다리지 않기를 원한다는 것입니다. 그러나 Ember.PromiseProxyMixin 사용과 관련된 Novelys 의 사람들 덕분에 훌륭한 (그러나 상대적으로 알려지지 않은) 솔루션을 찾았습니다 .

세 가지 시각적 섹션이있는 뷰가 있다고 가정 해 보겠습니다. 이러한 각 섹션은 자체 모델에 의해 뒷받침되어야합니다. 보기 상단의 "splash"콘텐츠를 지원하는 모델은 작고 빠르게로드되므로 정상적으로로드 할 수 있습니다.

경로 만들기 main-page.js:

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        return this.store.find('main-stuff');
    }
});

그런 다음 해당 Handlebars 템플릿을 만들 수 있습니다 main-page.hbs.

<h1>My awesome page!</h1>
<ul>
{{#each thing in model}}
    <li>{{thing.name}} is really cool.</li>
{{/each}}
</ul>
<section>
    <h1>Reasons I Love Cheese</h1>
</section>
<section>
    <h1>Reasons I Hate Cheese</h1>
</section>

따라서 템플릿에 치즈와의 사랑 / 증오 관계에 대한 별도의 섹션을 갖고 싶다고 가정 해 보겠습니다. 각 섹션은 어떤 이유로 든 자체 모델에 의해 뒷받침됩니다. 각 모델에는 각 이유와 관련된 광범위한 세부 정보가 포함 된 많은 레코드가 있지만 맨 위에있는 콘텐츠가 빠르게 렌더링되기를 원합니다. 여기에 {{render}}도우미가 있습니다. 다음과 같이 템플릿을 업데이트 할 수 있습니다.

<h1>My awesome page!</h1>
<ul>
{{#each thing in model}}
    <li>{{thing.name}} is really cool.</li>
{{/each}}
</ul>
<section>
    <h1>Reasons I Love Cheese</h1>
    {{render 'love-cheese'}}
</section>
<section>
    <h1>Reasons I Hate Cheese</h1>
    {{render 'hate-cheese'}}
</section>

이제 각각에 대한 컨트롤러와 템플릿을 만들어야합니다. 이 예제에서는 사실상 동일하므로 하나만 사용하겠습니다.

라는 컨트롤러를 만듭니다 love-cheese.js.

import Ember from 'ember';

export default Ember.ObjectController.extend(Ember.PromiseProxyMixin, {
    init: function() {
        this._super();
        var promise = this.store.find('love-cheese');
        if (promise) {
            return this.set('promise', promise);
        }
    }
});

PromiseProxyMixin여기서는 컨트롤러가 약속을 인식하도록 만드는 여기를 사용하고 있음을 알 수 있습니다. 컨트롤러가 초기화되면 Promise가 love-cheeseEmber 데이터를 통해 모델을 로드해야 함을 나타냅니다 . 컨트롤러의 promise속성 에서이 속성을 설정해야 합니다.

이제 다음과 같은 템플릿을 만듭니다 love-cheese.hbs.

{{#if isPending}}
  <p>Loading...</p>
{{else}}
  {{#each item in promise._result }}
    <p>{{item.reason}}</p>
  {{/each}}
{{/if}}

템플릿에서 약속 상태에 따라 다른 콘텐츠를 렌더링 할 수 있습니다. 페이지가 처음로드되면 '내가 치즈를 좋아하는 이유'섹션이 표시됩니다 Loading.... Promise가로드되면 모델의 각 레코드와 관련된 모든 이유를 렌더링합니다.

각 섹션은 독립적으로로드되며 주요 콘텐츠가 즉시 렌더링되는 것을 차단하지 않습니다.

이것은 단순한 예이지만 다른 모든 사람들이 저처럼 유용하다고 생각하기를 바랍니다.

여러 행의 콘텐츠에 대해 유사한 작업을 수행하려는 경우 위의 Novelys 예제가 훨씬 더 관련성이 있음을 찾을 수 있습니다. 그렇지 않은 경우 위의 내용이 잘 작동합니다.


이것은 모범 사례와 순진한 접근 방식이 아닐 수 있지만 하나의 중앙 경로에서 여러 모델을 사용할 수있는 방법을 개념적으로 보여줍니다.

App.PostRoute = Ember.Route.extend({
  model: function() {
    var multimodel = Ember.Object.create(
      {
        posts: App.Post.find(),
        comments: App.Comments.find(),
        whatever: App.WhatEver.find()
      });
    return multiModel;
  },
  setupController: function(controller, model) {
    // now you have here model.posts, model.comments, etc.
    // as promises, so you can do stuff like
    controller.set('contentA', model.posts);
    controller.set('contentB', model.comments);
    // or ...
    this.controllerFor('whatEver').set('content', model.whatever);
  }
});

도움이 되길 바랍니다


다른 모든 훌륭한 답변 덕분에 여기 최고의 솔루션을 간단하고 재사용 가능한 인터페이스로 결합하는 믹스 인을 만들었습니다. 지정한 모델 Ember.RSVP.hashafterModel대해 in 실행 한 다음 속성을의 컨트롤러에 삽입합니다 setupController. 표준 model후크를 방해하지 않으므로 여전히 정상으로 정의합니다.

사용 예 :

App.PostRoute = Ember.Route.extend(App.AdditionalRouteModelsMixin, {

  // define your model hook normally
  model: function(params) {
    return this.store.find('post', params.post_id);
  },

  // now define your other models as a hash of property names to inject onto the controller
  additionalModels: function() {
    return {
      users: this.store.find('user'), 
      comments: this.store.find('comment')
    }
  }
});

다음은 믹스 인입니다.

App.AdditionalRouteModelsMixin = Ember.Mixin.create({

  // the main hook: override to return a hash of models to set on the controller
  additionalModels: function(model, transition, queryParams) {},

  // returns a promise that will resolve once all additional models have resolved
  initializeAdditionalModels: function(model, transition, queryParams) {
    var models, promise;
    models = this.additionalModels(model, transition, queryParams);
    if (models) {
      promise = Ember.RSVP.hash(models);
      this.set('_additionalModelsPromise', promise);
      return promise;
    }
  },

  // copies the resolved properties onto the controller
  setupControllerAdditionalModels: function(controller) {
    var modelsPromise;
    modelsPromise = this.get('_additionalModelsPromise');
    if (modelsPromise) {
      modelsPromise.then(function(hash) {
        controller.setProperties(hash);
      });
    }
  },

  // hook to resolve the additional models -- blocks until resolved
  afterModel: function(model, transition, queryParams) {
    return this.initializeAdditionalModels(model, transition, queryParams);
  },

  // hook to copy the models onto the controller
  setupController: function(controller, model) {
    this._super(controller, model);
    this.setupControllerAdditionalModels(controller);
  }
});

https://stackoverflow.com/a/16466427/2637573 is fine for related models. However, with recent version of Ember CLI and Ember Data, there is a simpler approach for unrelated models:

import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Route.extend({
  setupController: function(controller, model) {
    this._super(controller,model);
    var model2 = DS.PromiseArray.create({
      promise: this.store.find('model2')
    });
    model2.then(function() {
      controller.set('model2', model2)
    });
  }
});

If you only want to retrieve an object's property for model2, use DS.PromiseObject instead of DS.PromiseArray:

import Ember from 'ember';
import DS from 'ember-data';

export default Ember.Route.extend({
  setupController: function(controller, model) {
    this._super(controller,model);
    var model2 = DS.PromiseObject.create({
      promise: this.store.find('model2')
    });
    model2.then(function() {
      controller.set('model2', model2.get('value'))
    });
  }
});

Adding to MilkyWayJoe's answer, thanks btw:

this.store.find('post',1) 

returns

{
    id: 1,
    title: 'string',
    body: 'string string string string...',
    author_id: 1,
    comment_ids: [1, 2, 3, 6],
    tag_ids: [3,4]
};

author would be

{
    id: 1,
    firstName: 'Joe',
    lastName: 'Way',
    email: 'MilkyWayJoe@example.com',
    points: 6181,
    post_ids: [1,2,3,...,n],
    comment_ids: [1,2,3,...,n],
}

comments

{
    id:1,
    author_id:1,
    body:'some words and stuff...',
    post_id:1,
}

... I believe the link backs are important so that the full relationship is established. Hope that helps someone.


You could use the beforeModel or afterModel hooks as these are always called, even if model is not called because you're using dynamic segments.

As per the asynchronous routing docs:

The model hook covers many use cases for pause-on-promise transitions, but sometimes you'll need the help of the related hooks beforeModel and afterModel. The most common reason for this is that if you're transitioning into a route with a dynamic URL segment via {{link-to}} or transitionTo (as opposed to a transition caused by a URL change), the model for the route you're transitioning into will have already been specified (e.g. {{#link-to 'article' article}} or this.transitionTo('article', article)), in which case the model hook won't get called. In these cases, you'll need to make use of either the beforeModel or afterModel hook to house any logic while the router is still gathering all of the route's models to perform a transition.

따라서에 themes속성 이 있다고 가정하면 SiteController다음과 같은 것을 가질 수 있습니다.

themes: null,
afterModel: function(site, transition) {
  return this.store.find('themes').then(function(result) {
    this.set('themes', result.content);
  }.bind(this));
},
setupController: function(controller, model) {
  controller.set('model', model);
  controller.set('themes', this.get('themes'));
}

참고 URL : https://stackoverflow.com/questions/16463958/how-to-use-multiple-models-with-a-single-route-in-emberjs-ember-data

반응형