25

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

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();
  }
});
4

5 回答 5

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/subtriggeron方法) ,则 Jeremy Green 的解决方案可以工作

于 2013-10-31T18:11:16.317 回答
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');
  }
}
于 2013-10-29T19:34:59.267 回答
6

将组件属性绑定到模板中的控制器属性:

    {{google-map componentProperty=controllerProperty}}

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

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

现在每次controllerProperty在控制器中改变,onChange在组件中都会被调用。

这个答案,第二段。

于 2015-03-04T23:19:18.440 回答
3

我认为在你的控制器中引用你的组件是可以的。确实,您的组件封装了它自己的行为,但是诸如此类的公共方法reload非常好。

我对此的解决方案是将电流传递controller给组件并在组件内的控制器上设置一个属性。

例子

模板.hbs:

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

组件.js:

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

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

现在在您的控制器中,您可以简单地使用this.get("refComponent").

史蒂芬

于 2014-04-19T22:48:29.457 回答
1

在您的组件调用内部:

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

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

于 2014-05-19T17:13:06.240 回答