4
\$\begingroup\$

I've got a view which has a corresponding template. The template looks something like like:

<div id="streamItems" class="left-list droppable-list"></div>

<div class="context-buttons">

    <div class="left-group">
        <div id="shuffleButtonView" class="view-placeholder"></div>
        <div id="repeatButtonView" class="view-placeholder"></div>
        <div id="radioButtonView" class="view-placeholder"></div>
    </div>

    <div class="right-group">

        <button id="saveStream" class="button-icon button-small save <%= userLoaded ? '' : 'disabled' %>" title="<%= userLoaded ? saveStreamMessage : cantSaveNotSignedInMessage %>">
            <i class="fa fa-save"></i>
        </button>

        <button id="clearStream" class="button-icon button-small clear" title="<%= chrome.i18n.getMessage('clearStream') %>" >
            <i class="fa fa-trash-o"></i>
        </button>
    </div>

</div>

I've passed into the template some variables such as userLoaded. This allows me to render the initial state of the saveStream button.

Now, the state of userLoaded has changed and I would like to update the button. I don't want to render my entire template again because streamItems is an expensive list to render.

So, I wrote the following:

initialize: function() {
    this.listenTo(User, 'change:loaded', this.toggleSaveStream);
},

toggleSaveStream: function () {
    var userLoaded = User.get('loaded');
    var templateHelpers = this.templateHelpers();

    this.ui.saveStreamButton.toggleClass('disabled', userLoaded);
    this.ui.saveStreamButton.attr('title', userLoaded ? templateHelpers.saveStreamMessage : templateHelpers.cantSaveNotSignedInMessage);
}

Which serves to keep my button up to date.

I'm unhappy with this solution because the logic for the disabled state of the button is duplicated into two spots. Is this a valid concern? How to handle?

Button is rendered disabled in template and I don't want to re-render whole template to change button state. How to stay DRY?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

From looking at it, the name of your function toggleSaveStream is lying, calling it twice will not make it toggle, it will look at the the status of the user and set the status accordingly. So I would simply call it updateSaveStreamButton.

Since it does that, you could call it at first, de-activating the button, and then once the user is loaded call it again.

Then you would simply have

<button id="saveStream" class="button-icon button-small save disabled>" title="">

And then

initialize: function() {
    this.updateSaveStreamButton();
    this.listenTo(User, 'change:loaded', this.updateSaveStreamButton);
},

updateSaveStreamButton: function () {
    var userLoaded = User.get('loaded');
    var templateHelpers = this.templateHelpers();

    this.ui.saveStreamButton.toggleClass('disabled', userLoaded);
    this.ui.saveSelectedButton.attr('title', userLoaded ? templateHelpers.saveStreamMessage : templateHelpers.cantSaveNotSignedInMessage);
}

This sounds too easy, so feel free to point what I am missing ;)

\$\endgroup\$
3
  • \$\begingroup\$ I am mostly concerned about the fact that I misrepresent the state of the button during the period of time between rendering the view and calling updateSaveStreamButton. If a naive developer were to look at my template -- they would have no idea that saveStream's state changes or that it has a title. It feels weird to be moving that logic out of my template, but I guess I have to if I don't want to duplicate it? \$\endgroup\$ Feb 11, 2014 at 17:39
  • \$\begingroup\$ Well, the state should be correctly represented unless it is possible for the user to be loaded already. You could put a comment in your HTML /* State is set in updateSaveStreamButton */ \$\endgroup\$
    – konijn
    Feb 11, 2014 at 18:47
  • \$\begingroup\$ The user can be loaded already -- thus the concern. I agree that it seems like the best solution is just to comment and keep it DRY in the JavaScript and not rely on a template. \$\endgroup\$ Feb 11, 2014 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.