How to properly set checkbox checked attribute from reactive data source on render

Chad Kruse picture Chad Kruse · May 2, 2014 · Viewed 9.2k times · Source

I have multiple checkboxes whose checked attributes represent boolean values in a collection. My code properly tracks the click event on the checkboxes and updates the values in the collection. However, when the page first loads, and when the user navigates away from the page, the checked attribute does not get set properly, despite the session and collection values being correct.

The html

<input type="checkbox" id="feedEmailSubscribe" checked={{isChecked}}>

Helper JS

Template.layout.isChecked = function () {
  var id = $this.id;
  return Session.get(id) ? "checked" : "";
};

I first pull down the collection and set a session variable for the checkbox via an 'invites' route

// invites
  this.route('invites', {
    path: ':_id/invited-guest/VIP',
    waitOn : function () {
      return Meteor.subscribe('guests', this.params._id);
    },
    data: function () {
      Session.set('guestId', this.params._id)
      return Guests.findOne({_id: this.params._id});
    },
    action : function () {
      var q = Guests.findOne({_id: this.params._id});
      if (this.ready()) {
        Session.set('guestName', q.name);
        Session.set('feedEmailSubscribe', q.feedEmailSubscribe);
        //...I then redirect the user
      }
    },
  });

I can verify the Sessions get set. Here is what I have for the settings route (where the checkbox exists)

// settings page
  this.route('settings', {
    path: '/settings',
    waitOn : function () {
      return Meteor.subscribe('guests', Session.get('guestId'));
    },
    data: function () {
      return Guests.findOne({_id: Session.get('guestId')});
    },
    action: function () {
    if (this.ready())
        this.render();
    },
    onAfterAction: function() {
      setPageTitle('Settings');
    }
  });

I can verify the Session exists and that it properly changes once clicked after page render. I suspect I'm either not using Blaze correctly as briefly discussed in the Blaze wiki, or I've got something wrong in my iron-router setup?

Thanks in advance for the help!

Answer

Bern&#225;t picture Bernát · May 4, 2014

In the helper, your line

  return Session.get(id) ? "checked" : "";

should be changed to

  return Session.get(id) ? "checked" : false;

This is because the empty string generates the empty html attribute checked="". An empty boolean attribute represents a true value according to HTML spec. You have to omit the attribute in order to have a false value, this can be achieved by returning false, null or undefined, according to the Meteor documentation as Bozhao referred.

Don't be surprised because the resulting code doesn't show up as an attribute. Blaze uses the element.checked JavaScript attribute and not the checked HTML attribute to actually set the checkedness. The HTML attribute only specifies the default value, and has no meaning once the user or a JS code has changed the checkedness. Blaze treats such specially the input value, input checked, textarea value and option selected attributes.