ember.jsember-cliember.js-view

ember-cli: bind template action from view created using lookup


Ember : 1.10.0

Ember Data : 1.0.0-beta.16

jQuery : 1.11.2

I'm using this.container.lookup in order to create a view dynamically in my controller like this:

var popup = this.container.lookup('view:map-popup', {singleton: false});
var ctrl = this.container.lookup('controller:map-popup', {singleton: false});
popup.set('controller', ctrl);
popup.createElement();

the view is defined like this:

export default Ember.View.extend({
  templateName: "mapPopup",
  classNames: ["map-popup"]
});

The view template:

<a class="popup-closer" {{action 'closePopup'}}></a>
<div class="popup-content">{{content}}</div>

and the view's controller:

export default Ember.ObjectController.extend({

  hide: function() {
    console.log("i'm hidden");
  },

  actions: {
    closePopup: function() {
      this.hide();
    }
  }
});

The view is inserted correctly in the DOM by my controller:

<div class="ember-view map-popup" id="ember643">
  <a data-ember-action="645" class="popup-closer"></a>
  <div class="popup-content">571187.3674937992,5721182.413928764</div>
</div>

But nothing happens when I click on the popup-closer.

I would be glad if someone could show me how to bind the action to the view's controller.

Following albert advice, I tried to setup a component:

//app/components/map-popup.js
export default Ember.Component.extend({
  classNames: ['map-popup'],
  layoutName: 'components/map-popup',

  content: function() {
    return "some content";
  },

  hide: function() {
    console.log('hidden');
  },

  actions: {
    closePopup: function() {
      this.hide();
    }
  }
});

template is the same. as before, but inthe templates/components folder.

The result is worst than before. I get the popup to display the function as text instead of the content text...

enter image description here

The action is now triggered! That's great, the problem with the content not being displayed has bene solved using jquery:

  setUp: function() {
    this.$('.popup-content').html(this.content());
  }.on('didInsertElement'),

I think it's ugly, I would much rather have a one way binding the the {{content}} in handlebar... But I cannot figure out how to do this.


Solution

  • @albertjan is right. This has to be a component. Thats the right direction.

    Now for the issue that content() is not returning values can be solved if you make a computed property for it like this

    export default Ember.Component.extend({
      classNames: ['map-popup'],
      layoutName: 'components/map-popup',
    
      /*Notice the .property('') */
      content: function() {
        return "some content";
      }.property(''),
    
      hide: function() {
        console.log('hidden');
      },
    
      actions: {
        closePopup: function() {
          this.hide();
        }
      }
    });
    

    Why this works, When you use a simple function in template its nothing more than a attribute having some string value (which is function() body)

    To use the function's return value Ember use concept of computed properties.

    Computed properties also accept arguments and are useful for combining results of two or more properties like this. Its output updates when any dependent properties changes.

    Example can be found here on Emberjs.com

    var Person = Ember.Object.extend({
      // these will be supplied by `create`
      firstName: null,
      lastName: null,
    
      fullName: function() {
        var firstName = this.get('firstName');
        var lastName = this.get('lastName');
    
       return firstName + ' ' + lastName;
      }.property('firstName', 'lastName')
    });
    
    var tom = Person.create({
      firstName: 'Tom',
      lastName: 'Dale'
    });
    
    tom.get('fullName') // 'Tom Dale'