从组件中访问存储

49

我有一个组件,当用户点击组件时,它会将一些值添加到存储中。我尝试使用这种方法,但是却收到了一个错误:

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”。

7个回答

44

@MBehtemam,您可以考虑将此标记为确认解决方案。 - chrmod
为什么叫做“存储”而不是“存储器”? - Ronni Egeriis Persson
那只是一个名称。在您的组件中,您可以根据需要随意更改它(因此“store”也可以)。 - Jacob van Lingen
1
文档说明:“使用Ember.inject.service()注入与其属性名称相同的服务”。因此,我认为您需要执行“store: Ember.inject.service('storage')”@RonniEgeriis。 - J. Barca
3
@J.Barca,你是正确的。我认为在Ember 1.10版本发布时,storage作为store的别名。现在我只会使用 store: Ember.inject.service() - Jacob van Lingen
显示剩余2条评论

43

在一个组件中,与在应用程序启动时像路由或控制器一样自动注入存储(store)不同,这是因为组件被认为更加隔离。

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

@sly7_7的建议很好,如果您有很多需要访问存储的组件,则这可能是一种不错的方法。

另一种获得store的方法是获取包含controllercomponent所参考的store。在这种情况下,无论是哪个controller,每个controller都已经引用了注入到其中的store。因此,现在可以通过获取componenttargetObject(它将是围绕componentcontroller)来访问store

示例:

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

点击这里查看一个可运行的示例。

希望能对您有所帮助。

回应您评论中的嵌套组件更新

例如,如果您的子组件只嵌套了一层,则仍然可以使用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'));
  }
});

更新的示例


好的,我之前不知道组件的targetObject属性。我一定要更新一下自己关于组件的知识。我之前确信它是完全隔离的,没有任何访问控制器的权限。 - sly7_7
1
在组件中使用sendAction如何?可以使用sendAction和事件冒泡将操作发送到具有store的Route或controller吗? - MBehtemam
1
我有嵌套组件,但它对我不起作用。当我使用 console.log(this.get('targetObject.store')) 记录日志时,它会显示未定义。 - MBehtemam
我正在尝试从组件访问这个存储方式,我可以做到,但是从视图中我却做不到。 - MBehtemam
@MBehtemam 看起来 sendAction 是解决此问题的更好方法,因为理想情况下组件不应该直接操作 store。我已经编辑了这个答案来考虑到这一点。 - igorsantos07
显示剩余2条评论

14

自从 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"
}}

将属性传递给组件的文档在这里


这对我来说似乎是最佳实践,尽管我是Ember的新手。+1 - Kenan Banks
控制器正在被弃用。 - Epirocks
但是在我们获得可路由组件之后,控制器将被弃用;可以预期的是可路由组件将会注入存储吗? - wpjmurray

13

当前的 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上偷来的。


直接链接到源代码 app/initializers/component-store-injector.js - Eliot Sykes
1
在Ember 1.13中,store似乎被注册为service:store。 - Maksim Burnin

7

我不确定组件是否应该以这种方式使用,但如果你想的话,我觉得你可以声明一个初始化程序并将存储注入到所有组件中。

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

1
不错,只是我想补充一下,如果他只需要在某些组件中访问存储,那么两种解决方案都会创建依赖关系。 - intuitivepixel
我认为你的更好 :) - sly7_7
这个解决方案对你有用吗?我已经尝试过了,但是“store”属性在我的组件中没有显示出来。 - andrei1089
@andrei1089 我终于成功了。您需要在组件注册之前注册并注入store。 - sly7_7
2
registerComponents 被重命名为 registerComponentLookup,同时 store:main 不需要注册,它将在 App.ApplicationStore 设置后自动设置为一个存储类。现在所有这些事情都会抛出错误 :) - K..
显示剩余3条评论

6

可以通过依赖注入来注入存储库。

示例

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');
  },

});

谢谢!我已经寻找这样的东西很长时间了! :) - William Weckl

4

还有一种方法,目前没有人提到过,那就是简单地将controller.store传递给组件,例如:

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

@Marcello:糟糕,我错过了 - 感谢您的纠正。 - TrevTheDev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接