【问题标题】:Access store from component从组件访问存储
【发布时间】:2013-09-07 20:29:21
【问题描述】:

我有一个组件,当用户单击组件时,它会添加一些值来存储,我尝试使用这种方式,但出现错误:

OlapApp.MeasureListItemComponent = Ember.Component.extend({
  tagName: 'li',
  isDisabled: false,
  attributeBindings: ['isDisabled:disabled'],
  classBindings: ['isDisabled:MeasureListItemDisabled'],

  actions: {
    add: function(measure) {
      var store = this.get('store');
      store.push('OlapApp.AxisModel', {
            uniqueName: measure.uniqueName,
            name: measure.name,
            hierarchyUniqueName: measure.hierarchyUniqueName,
            type: 'row',
            isMeasure: true,
            orderId: 1
      });
    }
  }
});

这是错误:

Uncaught TypeError: Cannot call method 'push' of undefined  MeasureListItemComponent.js:18

是否可以将记录从组件推送到存储?为什么我无法访问商店? 我的模型名称是“AxisModel”,应用程序命名空间是“OlapApp”

【问题讨论】:

    标签: ember.js ember-data


    【解决方案1】:

    自 Ember 2.1.0 起

    export default Ember.Component.extend({
      store: Ember.inject.service('store'),
    });
    

    Ember 2.1.0 之前 - 依赖注入方式

    App.MyComponent = Ember.Component.extend({
    
      store: Ember.computed(function() {
         return this.get('container').lookup('store:main');
      })
    
    });
    

    在 Ember 2.1.0 之前 - 控制器方式

    您可以将 store 作为属性从控制器传递:

    App.MyComponent = Ember.Component.extend({
    
      value: null,
      store: null,
      tagName: "input",
    
      didInsertElement: function () {
    
         if (!this.get('store')) {
            throw 'MyComponent requires store for autocomplete feature. Inject as store=store'
         }
      }
    
    });
    

    商店在每个控制器上都可用。所以在父视图中你可以包含如下组件:

    {{view App.MyComponent
        store=store
        class="some-class"
        elementId="some-id"
        valueBinding="someValue"
    }}
    

    将属性传递给组件已记录在案 here

    【讨论】:

    • 这对我来说似乎是最佳实践,诚然是 Ember 新手。 +1
    • 控制器已被弃用。
    • 但是在我们获得可路由组件后,控制器将被弃用;大概可路由组件将注入存储?
    【解决方案2】:

    可以借助依赖注入来注入 store。

    示例

    import Ember from 'ember';
    
    export default Ember.Component.extend({
    
      /**
       *
       */
      store: Ember.inject.service(),
    
      /**
       * Initialize the component.
       */
      init() {
        this.initialize();
    
        this._super();
      },
    
      /**
       * Initialize the properties and prerequisites.
       */
      initialize() {
        // Set the component properties
        this.todos().then((data) => {
          this.set('todoEntries', data);
        });
      },
    
      /**
       * Returns the todo entries.
       *
       * @returns {*|Promise|Promise.<T>}
       */
      todos() {
        const store = this.get('store');
    
        return store.findAll('todo');
      },
    
    });
    

    【讨论】:

    • 谢谢!找这样的东西很久了! :)
    【解决方案3】:

    从 Ember v1.10 开始,可以使用初始化器将 store 注入到组件中,参见:http://emberjs.com/blog/2015/02/07/ember-1-10-0-released.html#toc_injected-properties:

    export default Ember.Component.extend({
        store: Ember.inject.service()
    });
    

    【讨论】:

    • @MBehtemam 您可以考虑将此标记为已确认的解决方案
    • 为什么叫storage而不是store
    • 这只是一个名字。您可以在组件中随意调用它(所以store 也可以使用)。
    • 文档说明“使用 Ember.inject.service() 注入与注入的属性同名的服务”。所以我认为你需要做 "store: Ember.inject.service('storage')" @RonniEgeriis
    • @J.Barca,你是对的。我认为在 Ember 1.10 发布时 storage 是 store 的别名。现在我只会使用store: Ember.inject.service()
    【解决方案4】:

    component 中,应用程序启动时,商店不会像routecontroller 中那样自动注入。这是因为组件被认为更加孤立。

    以下内容不被视为最佳做法。组件应该使用传递给它的数据而不知道它的环境。处理这种情况的最佳方法是使用sendAction 冒泡您想要执行的操作,并在控制器本身中使用store 处理操作。

    @sly7_7 建议是一个很好的建议,如果您有很多组件需要访问商店,那么这可能是一个好方法。

    获取store 的另一种方法是获取store 你的component 周围的controller 引用。在这种情况下,哪个controller 无关紧要,因为每个controller 都已经引用了注入其中的store。因此,现在可以通过获取componenttargetObject 来获取您的store,这将是围绕componentcontroller,然后获取store

    示例:

    OlapApp.MeasureListItemComponent = Ember.Component.extend({
      ...
      actions: {
        add: function(measure) {
          var store = this.get('targetObject.store');
          ...
        }
      }
    });
    

    有关工作示例,请参阅 here

    希望对你有帮助。

    更新以响应您具有嵌套组件的评论

    例如,如果您的子组件仅嵌套一层,那么您仍然可以使用 parentView 引用父组件的 targetObject:

    App.ChildCompComponent = Ember.Component.extend({
      storeName: '',
      didInsertElement: function() {
        console.log(this.get('parentView.targetObject.store'));
        this.set('storeName', this.get('parentView.targetObject.store'));
      }
    });
    

    更新example

    【讨论】:

    • 很好,我不知道组件的目标对象。我绝对必须更新我对组件的了解。我很确定它真的是孤立的,控制器根本无法访问
    • 组件中的 sendAction 怎么样?使用 sendAction 并使用事件冒泡将操作发送到具有存储的 Route 或控制器?
    • 我有嵌套组件,它对我不起作用。当我使用console.log(this.get('targetObject.store')) 登录时,它说未定义
    • 我试图通过这种方式访问​​组件,我可以,但从视图中我不能。
    • @MBehtemam 似乎sendAction 是解决此问题的更好方法,因为理想情况下组件不应直接摆弄store。我已经编辑了这个答案来解决这个问题。
    【解决方案5】:

    当前 ember-cli 执行此操作的方法似乎是使用初始化程序。与@Sly7_7 的答案非常相似。

    要获得基本模型,请使用:

      ember g initializer component-store-injector
    

    然后将其编辑为:

    // app/initializers/component-store-injector.js
    
    export function initialize(container, application) {
      application.inject('component', 'store', 'store:main');
    }
    
    export default {
      name: 'component-store-injector',
      initialize: initialize
    };
    

    我相信这会将商店添加到所有组件中。

    盗自https://github.com/ember-cli/ember-cli-todos

    【讨论】:

    【解决方案6】:

    我不知道组件是否打算以这种方式使用。但如果你愿意,我认为你可以声明一个初始化器并将 store 注入到所有组件中。

    Ember.onLoad('OlaApp', function(OlaApp) {
      OlapApp.initializer({
        name: 'injectStoreIntoComponents',
        before: 'registerComponents',
        initialize: function(container, application){
          container.register('store:main', App.Store);
          container.injection('component', 'store', 'store:main');
        }
      })
    });
    

    这是一个人为但有效的示例:http://jsbin.com/AlIyUDo/6/edit

    【讨论】:

    • 好一个,如果他只需要访问 some 组件中的商店,就加了我的两分钱,但无论如何这两种解决方案都会创建依赖项
    • 我觉得你的更好:)
    • 此解决方案是否适合您?我已经尝试过了,但我的组件中没有显示“商店”属性。
    • 其实我没有测试过。所以要么它完全错误,我应该删除,要么初始化程序有问题。也许它应该在 Ember.onLoad() 钩子中声明。你能试一试并告诉我它是否更好用吗?
    • registerComponents 已重命名为 registerComponentLookupstore:main 不必注册,将在 App.ApplicationStore 是一个商店类之后设置。所有这些东西现在都会抛出错误:)
    【解决方案7】:

    另一种还没有人提到的方法是简单地将 controller.store 传递给组件,例如

    {{my-awesome-component store=controller.store}}
    

    【讨论】:

    • @Marcello:哎呀错过了 - 感谢您的更正
    猜你喜欢
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    • 2013-04-30
    • 2018-05-14
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 2017-02-27
    相关资源
    最近更新 更多