如何从控制器调用组件的方法

时间:2013-10-27 13:30:53

标签: ember.js

我有一个代表地图的组件,在我的控制器中执行操作后,我想调用组件上的方法来使地图居中。代码看起来像这样

App.PlacesController = Ember.Controller.extend({
  actions : {
    centerMap : function () {
        // how to call from here to GoogleMapComponent.centerMap ??
    }
  }
});


App.GoogleMapComponent = Ember.Component.extend({
  centerMap : function () {
  }
});

模板

{{google-map}}
<button {{action "centerMap"}}>Center Map</button>

我找到了一种解决方法,但我不认为这是Ember的做法。

{{google-map viewName="mapView"}}
<button class="center-map">Center Map</button>

App.PlacesView = Ember.View.extend({
  didInsertElement : function () {
    this.$(".center-map").click(this.clickCenterMap.bind(this));
  },

  clickCenterMap : function () {
    this.get("mapView").centerMap();
  }
});

5 个答案:

答案 0 :(得分:23)

在Ember中,视图(组件是美化视图)知道他们的控制器,但控制器不了解视图。这是设计(MVC)以保持事物解耦,因此您可以拥有许多由单个控制器“供电”的视图,并且控制器不是更明智的。因此,在考虑关系时,控制器可能会发生更改,视图会对这些更改做出反应。所以,重申一下,你永远不应该尝试从控制器中访问视图/组件。

在处理你的例子时,我可以想到几个选项。

  1. 使按钮成为组件的一部分!组件用于处理用户输入,例如按钮单击,因此您可能需要考虑使按钮成为地图组件的一部分并处理组件的操作哈希中的单击。如果这个按钮总是伴随地图组件,那么我当然推荐这种方法。

  2. 您的控制器上可能有一个布尔属性,如isCentered,当单击该按钮时,它设置为true。在组件中,您可以绑定到该控制器的属性,并在该属性发生更改时做出反应。这是一个双向绑定,因此,如果用户移动地图,您也可以将本地绑定属性更改为false。

    控制器:

    ...
    isCentered: false,
    actions: {
        centerMap: {
            this.set('isCentered', true);
        }
    }
    ...
    

    组件:

    ...
    isCenteredBinding: 'controller.isCentered',
    onIsCenteredChange: function () {
        //do your thing
    }.observes('isCentered'),
    ...
    
  3. 如果你将Ember.Evented mixin混合到控制器中(其中添加了pub / sub triggeron方法)

答案 1 :(得分:9)

您可以使用on让您的组件从控制器侦听事件,然后您可以使用控制器中的trigger发出事件。

因此,在您的组件中,您可能会遇到以下情况:

didInsertElement : function(){
  this.get('controller').on('recenter', $.proxy(this.recenter, this));
},

recenter : function(){
  this.get("mapView").centerMap()
}

在您的控制器中,您可以:

actions : {
  centerMap : function () {
    this.trigger('recenter');
  }
}

答案 2 :(得分:6)

将组件属性绑定到模板中的controller属性:

    {{google-map componentProperty=controllerProperty}}

然后观察组件中的组件属性:

    onChange: function () {
        // Do your thing
    }.observes('componentProperty')

现在每次在控制器中更改controllerProperty时,都会调用组件中的onChange

来自this answer,第二段。

答案 3 :(得分:3)

我认为在控制器中为组件提供引用是可以的。确实,你的组件封装了它自己的行为,但像reload等公共方法完全没问题。

我的解决方案是将当前controller传递给组件,并在组件内的控制器上设置属性。

示例

template.hbs:

{{#component delegate=controller property="refComponent"}}

component.js:

init: function() {
   this._super.apply(this, arguments);

   if (this.get("delegate")) {
      this.get('delegate').set(this.get("property") || "default", this);
   }
}

现在,您可以在控制器中使用this.get("refComponent")简单地获取对组件的引用。

斯特芬

答案 4 :(得分:1)

在您的组件调用内部:

var parentController = this.get('targetObject');

请参阅:http://emberjs.com/api/classes/Ember.Component.html#property_targetObject