benmacgowan
benmacgowan

Reputation: 185

Attribute binding in view based on 2 separate pieces of data

In the controller of a view within my Ember.js application, I have got an array of Travel types and another value with the current user's travel preferences.

Within the view, I need to add a CSS class to an element based on if the current travel type is a user's preferred travel preference.

This is as far as I have got, I just cannot seem to think how to connect the two up? Any help would be appreciated.

Values are hard-coded for benefit of this post, but the travelType and travelPreferences result from an AJAX call (and using Ember Objects)

Controller:

App.IndexController = Em.ArrayController.extend({
    user: {
         travelPreferences: [
              "1", 
              "3"
         ]
    }
    travelTypes: [
         { TravelTypeID: '1', TravelTypeDescription: 'Car' }, 
         { TravelTypeID: '2', TravelTypeDescription: 'Train' }, 
         { TravelTypeID: '3', TravelTypeDescription: 'Walking' }, 
         { TravelTypeID: '4', TravelTypeDescription: 'Bike' }
    ]
});

View

<ul>
    {{#each travelTypes}}
        <li>{{TravelTypeDescription}}</li>
    {{/each}}
</ul> 

So on the <li>, I need to put class="preferred" if the ID in the travelPreferences array matches up with the ID of the travelType.

Upvotes: 1

Views: 109

Answers (2)

Georgi Atsev
Georgi Atsev

Reputation: 3045

First, I would change the regular javascript objects into Ember objects, so I can use the binging functionality:

    user: Ember.Object.create({
        travelPreferences: [
            "1",
            "3"
        ]
    }),
    travelTypes: [
    Ember.Object.create({
        TravelTypeID: '1',
        TravelTypeDescription: 'Car',
        preferred: false
    }),
    Ember.Object.create({
        TravelTypeID: '2',
        TravelTypeDescription: 'Train',
        preferred: false
    }),
    Ember.Object.create({
        TravelTypeID: '3',
        TravelTypeDescription: 'Walking',
        preferred: false
    }),
    Ember.Object.create({
        TravelTypeID: '4',
        TravelTypeDescription: 'Bike',
        preferred: false
    })],

Next, I would observe for changes in the current user preferences and update the travel types' preferred property:

onUserPreferencesChanged: function () {
    var controller = this;
    controller.get('travelTypes').map(function (travelType) {
        travelType.set('preferred', false);
    });
    this.get('user.travelPreferences').map(function (prefId) {
        controller.get('travelTypes').filterProperty('TravelTypeID', prefId).map(function (travelType) {
            travelType.set('preferred', true);
        });
    });
}.observes('user.travelPreferences.@each'),

And finally, I would bind to the preferred flag of each travel type in the travel types' list.

    <ul> 
        {{#each travelTypes}} 
        <li {{bindAttr class = "preferred"}}>
            {{TravelTypeDescription}} 
        </li>
        {{/each}}
    </ul>

Here is a working fiddle, to wrap it all up: http://jsfiddle.net/B4d6K/4/ Read more about the framework and play with the binding and view mechanisms and you will see that you can do anything with it. This is probably not the cleanest solution, perhaps you will come up with something better after reading a bit.

Upvotes: 2

ianpetzer
ianpetzer

Reputation: 1838

I recently asked a similar question, in terms of iterating over an array and then using some conditional logic to change how the items are reflected. The answer that I was supplied worked very well for me. See:

Handlebars Helper to insert view into template based on conditional logic

I would suggest using an ArrayController to iterate over the travel types. Then specify itemController (which extends ObjectController) to control the display of each element. The ObjectController can have a method getTravelTypeClass which you can use to return the desired class for each travel type based on some logic.

Upvotes: 0

Related Questions