norbi123
norbi123

Reputation: 41

Ember.js add css class for specific model id

I am working on chat app. I have template of component - rooms

<div class='border rooms'>
    <button class="add-room-container">
        <div >Add room</div>
    </button>
    {{#each model as |room|}}
     <button class="room-container" 
             {{action "onRoomClicked" room.id}} 
             {{bind-attr class="currentRoomId===room.id:currentRoom"}}>
         <div class="deleteRoom" {{action "deleteRoom" room.id bubbles=false}}>
             X
         </div>
         <div>{{room.name}}</div>
         <div>{{room.createdAt}}</div>
     </button>
    {{/each}}
</div>

and component js file import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    onRoomClicked(roomId) {
      this.set('currentRoomId', roomId);
    },
    deleteRoom(room) {
      console.log('room removed');
    }

} });

and I have problem with the line

{{bind-attr class="currentRoomId===room.id:currentRoom"}}

I would like to check if current button is the last which was clicked (it is set in onRoomClicked) and if it is then add currentRoom class to button

I was trying to set dynamic property but I don't know how can I access current model (room) id from component and then compare it like I do in template

Upvotes: 0

Views: 624

Answers (2)

Lux
Lux

Reputation: 18240

You have many different ways to do this. But first the question is a bit what ember version you are using. In current ember versions bind-attr should not be used anymore!

So first I assume you call your component like this:

{{my-component model=rooms}}

I would strongly recommend to not call your data model! Give them a speaking name. Do something like rooms=rooms!

So now you are in your component. What you probably figured out is that the problem is your {{#each}} loop, which results in a situation that you have no .js file for the context inside the {{#each}} loop.


The maybe best way to work around is to use a second component, which you call inside your each loop:

{{#each model as |room|}}
  {{room-line room=room currentRoomId=currentRoomId}}
{{/each}}

Then you can place your {{#each}} body inside your room-line.hbs:

<button class={{if isCurrentRoom "currentRoom"}}>
....

And you have a backing room-line.js where you can calculate the isCurrentRoom:

isCurrentRoom: Ember.computed('currentRoomId', 'room.id', {
  get() {
    return get(this, 'currentRoomId')===get(this, 'room.id');
  }
})

Another way to handle this is to inject a isCurrentRoom into your room. This can be done with .map(), or with an ArrayProxy and/or ObjectProxy.

Something like this in your components .js file:

calculatedRooms: Ember.computed('[email protected]', 'currentRoomId', {
  get() {
    return get(this, 'rooms').map(room => ({
      room,
      isCurrentRoom:get(room, 'id') === get(this, 'currentRoomId')
    }));
  }
}),

Then you can just use it in your {{#each}} helper:

{{#each calculatedRooms as |line|}}
   <button class="room-container {{if line.isCurrentRoom 'currentRoom'}}" 
     {{action "onRoomClicked" line.room.id}}>
    ...
{{/each}}

The third approach is to use a custom helper for this. This is maybe the easiest way to do it, but maybe also the ugliest, because you put logic in your templates.

A simple is-equal helper can help you:

export function isEqual(a, b) {
  return a === b;
}

export default Ember.Helper.helper(=);

Then you can just use it in your template:

{{#each calculatedRooms as |room|}}
   <button class="room-container {{if (is-equal currentRoomId room.id) 'currentRoom'}}" 
     {{action "onRoomClicked" room.id}}>
    ...
{{/each}}

If you choose this approch you can use ember-truth-helper which provides that helper, but you can also just write it yourself.


I would strongly recommend the first approach, its the far most elegant way to handle this.

Upvotes: 3

Alisdair McDiarmid
Alisdair McDiarmid

Reputation: 368

The easiest way to do this is using ember-truth-helpers, which gives you the incredibly useful eq helper in templates. Then you can write:

{{#each model as |room|}}
  <button
    class="room-container {{if (eq room.id currentRoomId) "currentRoom"}}" 
    {{action "onRoomClicked" room.id}}>
  </button>
{{/each}}

This is using an embedded if helper in your class attribute instead of bind-attr (which is deprecated and you should avoid).

Upvotes: 0

Related Questions