Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

two-way binding between object attribute array (enum on server) and ember checkbox group

Tags:

ember.js

I am looking for a solution to bind an object attribute that contains an array of string (representing an enum on the server) to a list of checkboxes. The binding should be two-way.

On the server, we have some enum defintion, e.g., Role with the values "ADMIN", "GUEST", "USER". A user object may have several of thus roles, thus the user object in Ember is of the form

App.User = Ember.Object.create({
    roles: ["USER", "ADMIN"]
});

In the user administration, there should be a group of checkboxes. One checkbox per role. So, it is possible to select none, all, or several.

I know that there is the Ember.Checkbox view that can be used for this. What I am looking for would be a easy and generic view to handle any kind of enums as mentioned above.

Thus, the questions are:

  • has somebody a nice solution for this?
  • does anybody know a opensource project providing such extensions to ember?

Thanks in advance. // ph

like image 391
Patrick Hammer Avatar asked Feb 02 '13 18:02

Patrick Hammer


1 Answers

A generic way to handle a two way binding between an Ember object and Checkboxes can be implemented using plain Ember.js without the need of any plugins if you are willing to sync the enums on the server and the client manually (using AJAX or WebSockets). Note that Ember can update the list of options with Checkbox automatically after a sync.

So henceforth, I will assume that you have am enum with roles as an Ember Array:

App.Roles = [ "USER", "ADMIN", "GUEST" ];

Then we will show the options available to the user in a CollectionView like this (the template is given below).

OptionsView = Em.CollectionView.extend({
    contentBinding: 'App.Roles',  // Show a list of _all_ available roles
    userBinding: 'App.User',      // This points to the active user
    tagName: 'ul',                // Shown as a <ul>
    itemViewClass: Em.View.extend({
        userBinding: 'parentView.user',  // For convenience
        templateName: 'user-roles' // Defined later
    })
});

The template for each option is:

<script data-template-name="user-roles" type="text/x-handlebars">
  <label> {{view App.RoleCheckbox 
            contentBinding="view.content"}} 
  {{view.content}}
  </label>
</script> 

Note that the use of <label> tag makes sure that the Checkbox's click event is fired on clicking anywhere on the tag.

Finally the App.RoleCheckbox is an extension of the Ember.Checkbox class which handles the checked property and click event to toggle the role:

App.RoleCheckbox = Em.Checkbox.extend({
    userRolesBinding: 'parentView.user.roles', // Points to the roles of the user

    checked: function () {
        var userRoles = this.get('userRoles');
        return userRoles.contains(this.get('content'));
    }.property('content', 'userRoles.@each'),

    click: function (evt) {
        var isPresent = this.get('checked'),
            userRoles = this.get('userRoles'),
            role      = this.get('content');

         if (!isPresent) { 
             userRoles.pushObject(role); 
         } else {
             userRoles.removeObject(role);
         }
     }
});

An working example of this is: http://jsfiddle.net/BLQBf/ (Look at the console to see the log messages)

Note that this is not completely Ember-esque, since the View is doing part of the job meant for the controller. Ideally, the click event would call a function on the RoleCheckboxController which would make changes to the User object.

like image 119
musically_ut Avatar answered Oct 16 '22 10:10

musically_ut