web-dev-qa-db-fra.com

Comment appeler la méthode d'un composant à partir d'un contrôleur

J'ai un composant qui représente une carte et après une action dans mon contrôleur, je veux appeler une méthode sur le composant pour centrer la carte. Le code ressemble à ceci

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


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

modèle

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

J'ai trouvé une solution de contournement, mais je ne pense pas que ce soit la façon Ember de procéder.

{{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();
  }
});
25
axelhzf

Dans Ember, les vues (les composants sont des vues glorifiées) connaissent leur contrôleur, mais les contrôleurs ne connaissent PAS les vues. C'est par conception (MVC) pour garder les choses découplées, et donc vous pouvez avoir de nombreuses vues qui sont "alimentées" par un seul contrôleur, et le contrôleur n'est pas plus sage. Ainsi, lorsque vous pensez à la relation, des changements peuvent se produire sur un contrôleur et une vue réagit à ces changements. Donc, pour réitérer, vous ne devriez jamais essayer d'accéder à une vue/un composant à partir d'un contrôleur.

Il y a quelques options auxquelles je peux penser en traitant votre exemple.

  1. Intégrez le bouton à votre composant! Les composants sont destinés à gérer les entrées utilisateur, comme les clics sur les boutons, vous pouvez donc envisager de faire du bouton une partie du composant de carte et gérer les clics dans le hachage des actions de votre composant. Si ces boutons vont toujours accompagner le composant de carte, je recommande certainement cette approche.

  2. Vous pouvez avoir une propriété booléenne sur votre contrôleur comme isCentered, et lorsque le bouton est cliqué, il est défini sur true. Dans votre composant, vous pouvez vous lier à la propriété de ce contrôleur et réagir chaque fois que cette propriété change. Il s'agit d'une liaison bidirectionnelle, vous pouvez donc également modifier votre propriété liée localement sur false si l'utilisateur déplace la carte, par exemple.

    Manette:

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

    Composant:

    ...
    isCenteredBinding: 'controller.isCentered',
    onIsCenteredChange: function () {
        //do your thing
    }.observes('isCentered'),
    ...
    
  3. La solution de Jeremy Green peut fonctionner si vous mixez dans le mixage Ember.Evented dans le contrôleur (qui ajoute les méthodes pub/sub trigger et on)

23
Jason Monma

Vous pouvez utiliser on pour que votre composant écoute un événement à partir du contrôleur, puis vous pouvez utiliser trigger dans le contrôleur pour émettre un événement.

Donc, dans votre composant, vous pourriez avoir quelque chose comme ceci:

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

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

Et dans votre contrôleur, vous pourriez avoir:

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

Liez une propriété de composant à la propriété du contrôleur dans le modèle:

    {{google-map componentProperty=controllerProperty}}

Observez ensuite la propriété du composant dans le composant:

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

Maintenant, chaque fois que controllerProperty est modifié dans le contrôleur, onChange dans le composant sera appelé.

De cette réponse , deuxième paragraphe.

6
Felix

Je pense que c'est OK d'avoir une référence dans votre contrôleur à votre composant. Il est vrai que votre composant encapsule son propre comportement, mais les méthodes publiques comme reload etc. sont parfaitement bien.

Ma solution consiste à transmettre le controller actuel au composant et à définir une propriété sur le contrôleur au sein du composant.

Exemple

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

Maintenant, dans votre contrôleur, vous pouvez simplement obtenir une référence à votre composant avec this.get("refComponent").

Steffen

3
Steffen Brem

À l'intérieur de votre appel de composant:

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

Voir: http://emberjs.com/api/classes/Ember.Component.html#property_targetObject

1
Michael Benin