我应该如何在Ember组件之间共享数据? [英] How should I share data between Ember components?

查看:78
本文介绍了我应该如何在Ember组件之间共享数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Ember应用程序的路由包含2个不同的组件和一个带有index.hbs模板的控制器。

My Ember app has a route that contains 2 different components and one controller with an index.hbs template.

这是它的样子:

1)用户可以选择多个过滤器组件下拉菜单中的过滤器

1) A user can select multiple filters from the dropdowns of the Filter Component

2) DataGrid是与过滤器分离的单独组件

2) The DataGrid is a separate component from the filter

3)用户可以通过选中复选框从DataGrid中选择多行

3) A user can select multiple rows from the DataGrid by checking boxes

4)创建自定义报告按钮会向路由的控制器触发 sendAction

4) Create Custom Report button fires "sendAction" to the route's controller

此数据不是特定于模型的……只是临时数据,我可以创建一个自定义报告。

This data is not model-specific... it's just temporary data that is required before I can make a custom report.

Ember的最佳做法是减少数据/增加操作,从我的阅读中,您不应该尝试访问

Ember best practices are "Data Down / Actions Up", and from what I read, you shouldn't be trying to access a component from a controller.

但是,问题是控制器中的 createCustomReport方法需要访问所有选定的过滤器在过滤器组件中以及在网格组件中检查的所有行。

The problem, though, is that the createCustomReport method in the controller needs to have access to all of the filters that were selected in the filter-component along with all of the rows that were checked in the grid-component.

我的第一个直觉是在组件上设置属性本身-让它保持自己的状态-然后从控制器获取对该组件的引用,以便在将其状态传递给报告功能之前获取其状态。

My first instinct is to set the properties on the component itself - have it maintain its own state - then get a reference to the component from the controller in order to get its state before passing it off to the report function.

这是我当前的解决方法:

每次我选择一个过滤器时,都会有一个sendAction从组件冒泡到控制器并在控制器上设置自定义属性。

Each time I select a filter, there is a sendAction that bubbles up to the controller from the component and sets a custom property on the controller.

此外,每次我从网格中选择一个复选框时,另一个sendAction会转到该组件,然后冒泡到控制器并进行设置

Also, each time I select a checkbox from the grid, another sendAction goes to the component, then bubbles up to the controller and sets a custom property on the controller for selected grid rows.

然后,当我单击 createCustomReport时,控制器中激发的方法可以访问我之前设置的属性。 -因为它们现在都在控制器上。

Then, when I click "createCustomReport" the method that fires in the controller has access to the properties that I set earlier - because they are all on the controller now.

所以看起来像这样:

import Ember from 'ember';

export default Ember.Controller.extend({

    myFirstFilter: undefined,
    mySecondFilter: undefined,

    actions: {
        createCustomReport() {
            // do something with all those component properties you've been setting
        },

        // These are triggered by the sendAction on the respective component
        firstFilterMethod(myProperty1) {                
            this.set('myFirstFilter', myProperty1.name);
        },

        secondFilterMethod(myProperty2) {               
            this.set('mySecondFilter', myProperty2.name);
        },

        ... etc...


    }
});






这是我的问题

我不是直接从控制器访问组件,而是使用向上动作原理,在控制器上设置视图的属性

I'm not directly accessing the components from the controller, but by using the "Actions Up" principle, I'm setting properties on the controller that are view specific.

来自Sencha ExtJS背景,控制器引用了他们的视图,我觉得这很奇怪。

Coming from a Sencha ExtJS background where controllers have references to their views, I find this very weird.

由于没有获得对组件的引用,所以我应该将其控制器从其视图中解耦出来……但是由于我要设置的所有属性通常都在视图中,因此该控制器最终甚至是 耦合到视图,就比我仅获得对该组件的引用要好。

By not getting references to components, I'm supposed to be decoupling my controller from its views... but since all the properties I'm setting would normally be on the view, the controller ends up being even more coupled to the view than it would be if I were to just get a reference to the component.

这是否是Ember中的最佳实践,还是我有更好的方法来获取所有这些单独组件的数据,从而触发createCustomReport方法?

推荐答案

好吧,我想我已经解决了自己的问题,并且解决了灰烬的做事方式。

Alright, I think I've managed to solve my own problem and come around to the Ember way of doing things.

我发现了2种不同的解决方案,每种解决方案各有千秋。另外,我还创建了2个有关如何解决状态传播和共享组件数据的小型Ember Twiddle迷你教程。

I found 2 different solutions, each of which have their merits. Plus I've created 2 small Ember Twiddle mini tutorials on how to solve state propagation and sharing component data.

这两个解决方案都完全符合Ember 2.6的处理方式:不需要控制器

Both solutions are fully compliant with the Ember 2.6 way of doing things: No Controllers Needed.

我建立了一个简单的电影列表,可以在这里查看:
https://ember-twiddle.com/c91e98cd255a556311417ac603ab0315

I built a simple movie list that can be viewed here: https://ember-twiddle.com/c91e98cd255a556311417ac603ab0315

通过遵循文件中的注释并查看上方的Ember Twiddle,您应该回答所有问题

By following the comments inside the files and looking over the Ember Twiddle above, all your questions should be answered on how to implement this.

由于服务是单身人士,我可以将其注入到我的组件和路线中,其唯一目的是保持

Since a Service is a singleton, I can inject it into my components and into my route and its sole purpose will be to maintain the data of its associated component.

以下是该组件的外观:

import Ember from 'ember';

export default Ember.Component.extend({
  movieService: Ember.inject.service('movie-displayer-service'),
  currentSelectedMovie: '',

  didInsertElement: function() {
    // When the component is inserted into the DOM tree, use the model to set
    // the 'currentSelectedMovie' property.
    this.set('currentSelectedMovie', this.get('model').currentSelectedMovie);   
  },

  actions: {
    selectMovie: function(movie) {
      // Instead of saving state in the component itself, let's
      // save it in a service that can be consumed anywhere
      // in the application.

     this.get('movieService').setupCurrentSelectedMovie(movie);

     // When the movie changes, we can override the 'currentSelectedMovie' property
     // that is being populated with the 
     this.set('currentSelectedMovie', movie);   

    }
  }
});

以下是服务的外观:

import Ember from 'ember';

export default Ember.Service.extend({
  currentSelectedMovie: undefined,

  setupCurrentSelectedMovie: function(movie) {
   this.set('currentSelectedMovie', movie); 
  },

  showSelectedMovie: function() {
    if (this.get('currentSelectedMovie')) {
        alert("The current selected movie of the movie-displayer component is:  \n" + this.get('currentSelectedMovie'));
    } else {
        alert('Please Select a Movie First');
    }
  }
});

以下是组件的把手文件:

<div class="movie-list-container">
    <h4 class="movie-list-title">Movie List</h4>

  <ul>
    {{#each model.movies as |movie|}}

        {{!--   'eq' is a helper function that I made
                    to compare two values.  You can check it out in
              the 'helpers' folder.
      --}}
        <li class="{{if (eq movie currentSelectedMovie) "selected" "not-selected"}}" {{action 'selectMovie' movie}}>{{movie}}</li>
    {{/each}}
  </ul>

</div>

路线如下:

import Ember from 'ember';

export default Ember.Route.extend({
  movieService: Ember.inject.service('movie-displayer-service'),

  model: function() {
    return {
        currentSelectedMovie: this.get('movieService').currentSelectedMovie,

      movies: ['Captain America: Civil War', 'Guardians of the Galaxy', 'Ant Man']
    }
  },

  actions: {
    showServiceState: function() {
        this.get('movieService').showSelectedMovie();
    }
  }
});






服务质量解决方案:

作为一个单例,我可以在应用程序中的任何位置访问此组件的数据。

Being a singleton, I can access the data of this component anywhere in the application.

服务缺点解决方案:

我必须将其注入到每个我想在其中使用的文件中-这样就可以创建依赖项。另一种解决方案是使用Ember Initializer类,该类将在应用启动时自动将其注入到Routes,Controllers或Components中。当然,这意味着它会进入注入的每个实例,而这可能会导致过大杀伤力。

I have to inject this into every file that I want to use it in - thereby creating dependencies as I go. The other solution is to use an Ember Initializer class that will automatically inject it into Routes, Controllers, or Components upon app startup. Of course, this means that it would go into every single instance of what it is injected into which could be overkill.

第二个Ember Twiddle是一个简单的餐厅列表,显示了无需服务即可传播状态的方法:

The second Ember Twiddle is a simple restaurant list that shows how to propagate state without the need of a service:

https://ember-twiddle.com/dffc679fb96434ba6698161ba7617d15

以下是组件的把手文件:

<div class="restaurant-list-container">
  <ul>
    {{#each model as |restaurant|}}
      <li class="{{if (eq currentlySelectedRestaurant restaurant ) 'selected' 'not-selected' }}" {{action 'selectRestaurant' restaurant}}>{{restaurant}}</li>
    {{/each}}
  </ul>

</div>

以下是路线文件:

import Ember from 'ember';

export default Ember.Route.extend({  
  // Properties Here
    currentlySelectedRestaurant: 'Please Select a Restaurant',

  model: function() {
    return ['Taco Bell', 'McDonalds', 'Dennys']
  },

  actions: {
    setupRestaurantState : function(restaurant) {
        this.set('currentlySelectedRestaurant', restaurant);
    },

    getComponentState: function() {
     alert(this.get('currentlySelectedRestaurant'));
    }
  }
});

这是组件文件:

import Ember from 'ember';

export default Ember.Component.extend({

  currentlySelectedRestaurant: undefined,

  actions: {
    selectRestaurant: function(restaurant) {

      // The 'sendAction' method is where the magic happens.
      // A method called 'stateSetter' references a function
      // that lives either on the controller or the route.
      // This was setup when the component was instantiated in the
      // fancy-restaurants.hbs file.
      this.sendAction('stateSetter', restaurant);
      this.set('currentlySelectedRestaurant', restaurant);
    }
  }
});






请注意,路由包含未定义的状态属性: currentlySelectedRestaurant。


Notice that the Route contains an undefined state property: 'currentlySelectedRestaurant'.

这很容易是具有多个属性或数组的对象。

This could easily be an object with multiple properties or an array.

您还可以具有通用名称,例如 componentState,并存储从任何组件发送的任何内容:在过滤列表中选中的选项或从网格中选择的项目。例如

You could also have a generic name like "componentState" and store whatever you choose to send up from any component: options checked on a filtered list or selected items from a grid for instance.

不使用服务的专业人员:

这很容易做到。只需在组件中使用sendAction()即可将其冒泡到路由器。而且没有创建额外的文件或任何依赖项。

It's easier to do. Just use sendAction() in your component to bubble up to the router. And there are no extra files created or any dependencies.

不使用服务的缺点

由于模型数据从路径级别向下流动,因此如果更改路径,将无法访问状态。

Since the model data flows down from the route level, you won't be able to access the state if you change routes.

每种解决方案都是可行的,因此我将由您自己决定哪种方法最有效。

Each solution is viable so I will leave it up to you to find what works best.

此外,我没有将其标记为答案只是因为其他人可能有更好的解决方案,因此很高兴收到一些反馈。

Also, I'm not marking this as the answer just yet because someone else may have a better solution and it would be nice to get some feedback on this.

这篇关于我应该如何在Ember组件之间共享数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆