Using CP inside hash's


#1

I would like to use a computed property inside a hash, like:

export default Ember.Controller.extend({
  computeds: {
    hiw: Ember.computed('model.hiw', function() {
      return {
        name: this.get('model.hiw.name')
      };
    })
  }
});

But i can’t. Its throwing an error:

this.get is not a function

I understand the scope issues… but still, Ember should work with that…

Thanks.


#2

The technical problem here is that get isn’t added to the prototype chain so it is not available on regular javascript objects. So in your case, the hash is the scope (like you said) and it doesn’t have get on it’s prototype. To make your code sample work, you would replace this.get('model.hiw.name') with Ember.get(this, 'model.hiw.name');.

However, even though what I provided is a solution to the question, I think it’s important for me to point out that this sort of nested hash (pojo) is not really recommended, especially as part of the class definition. It definitely causes lots of problems with scope as you’ve seen. Other really odd issue is that the hash is a reference object. So if you had multiple instances, they would all share the same hash object. So inside your Ember.computed the this value would be the same. The one exception here is the actions hash.

It looks like maybe you’re trying to group all of your computed properties as a form of organization. While I understand how that’s enticing, I would recommend it not be done in this manner. A better approach would be via naming conventions or even splitting up the code across several components (which may or may not be an option depending on the use case).


#3

If you understood them, you would know it is the way Javascript works, and there is nothing Ember can do about it.

Incidentally, you would also know how bad an idea what you are trying to do is, and how to work around it should you really want to do it anyway.


#4

Hei workmanw… Could i give me a example about splitting up across several components??


#5

@celo It really depends on your usecase. I’ll give you a basic example, but splitting logic up across multiple components depends on your application/use-case and every app is different.

Here is an example:

// components/message-user.js
export default Ember.Component.extend({
  user: null,
  messenger: Ember.inject.service(),
  
  message: '',
  
  displayName: Ember.computed('user.title', 'user.firstName', 'user.lastName', function() {
    let title = this.get('user.title'),
        firstName = this.get('user.firstName'),
        lastName = this.get('user.lastName');
    return `${title}. ${firstName} ${lastName}`;
  }),

  actions: {
    sendMessage(msg) {
      let message = this.get('message'),
          user = this.get('user');

      this.get('messenger').sendMessage(message, user);
      this.set('message', '');
    }
  }
});
{{!-- templates/components/message-user.hbs --}}
{{yield this}}
{{!-- templates/user-list.hbs --}}

{{#each model as |user|}}
  {{#message-user user=user as |messageUser|}}
    <label>Name: </label><span>{{messageUser.displayName}}</span><br>
    {{input value=messageUser.message}}
    <button {{action "sendMessage" target=messageUser}}>Send Message</button>
    <br><br>
  {{/message-user}}
{{/each}}

In this case, I’ve created a component called message-user. This component has a computed property that properly formats the user’s name and has an action to send a message. Using a component this way is called “block form”.

I made an ember-twiddle so you could see this in action: https://ember-twiddle.com/eab37534357fbeccb416?route=%2FuserList

I hope that helps.