---
title: Weekly Widget 2 - 2-Way Mustache Helpers
description: Weekly Widget 2 - 2-Way Mustache Helpers
image: https://www.bitovi.com/hubfs/canjs-banner-background.jpg
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting)
- ![Project Management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project Management](https://www.bitovi.com/services/agile-project-management-consulting)
- ![Product Design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product Design](https://www.bitovi.com/services/product-design-consulting)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting)
- [Design systems](https://www.bitovi.com/services/axure-figma-migration)
- [React](https://www.bitovi.com/services/frontend/react-consulting)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about delivering products and solving problems.
  
  ](https://www.bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships)
- [![Academy](https://www.bitovi.com/hubfs/icon%20-%20academy%20(4).svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse or contribute to our community
  
  ](https://www.bitovi.com/open-source)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

 January 27, 2013

# Weekly Widget 2 - 2-Way Mustache Helpers

 Weekly Widget 2 - 2-Way Mustache Helpers

![Justin Meyer](https://www.bitovi.com/hubfs/justin.jpeg)

 Justin Meyer

Share:

[![Twitter](https://www.bitovi.com/hubfs/limbo-generated/_astro/twitter-white.os3xLc3C_Z2nW4or.svg) ](https://twitter.com/intent/tweet?text=) [![Reddit](https://www.bitovi.com/hubfs/limbo-generated/imgs/icons/reddit.png) ](http://reddit.com/submit?url=)

This article shows how to make 2-way binding mustache helpers and use them in a basic form.

By default, CanJS supports 1-way binding templates. 1-way binding means that if data changes, the template is automatically updated. For example, if `attendee.name` changes, the following input's value is updated:

```
<input type="text" value="{{attendee.name}}">
```

2-way binding means the reverse is also true. If the user types a new value, the `attendee.name` is automatically updated. This article shows how roll your own 2-way mustache helpers that can be used like:

```
<input name="" type="text" value=""> <input checked="checked" type="radio" value="yes">
```

2-way binding is a heavily requested CanJS feature, but you can easily add your own today.

## The app

I recently got engaged and because I'm a prideful programmer, I decided to build my own wedding site. I started with a save-the-date form where potential attendees can indicate if they are attending and enter their address.

The form below is a close approximation of my site:

JSFiddle

## What it does

It allows the user to indicate if they can attend the wedding. If they indicate "Yes", the user will be able to enter their address. When they change the "Country", the placeholder for the state and zip localize for the specific country.

When an input value is changed, it immediately changes the value of the `attendee` object. This is immediately apparent by viewing the JSON data that is displayed under the widget. Similarly, if `attendee` is updated, the form values automatically update. This is 2-way binding!

To see it in action, click on "Payal's Grandmother Preset" button. This will simply populate the `attendee` object, which through the magic of live binding will immediately update the form.

Next, change the country input to "Canada". Updating the input updates the model, which again updates the JSON data displayed under the widget.

## How it works

This example is mostly powered by using [Mustache Helpers](http://donejs.com/docs.html#!Helpers). I have created the following mustache helpers: `value`, `checked`, `placeholder`, and `fadeInWhen`.

Before we see how to build them, I want to show what they do and how they are used.

### Mustache Helper APIs

**value** - 2-way binds an input to an [Observe's](http://canjs.us/#can_observe) property or compute.

```
<input name="" type="text" value="">
```

This updates `attendee.name` when the input's value is changed. It also updates the input's value when `attendee.name` is changed.

**checked** - 2-way binds a radio input's checked property to an Observe's property or compute.

Usage:

```
<input checked="checked" type="radio" value="yes"> <input checked="checked" type="radio" value="no">
```

This will check the first input element if `attendee.attending`'s value is "yes" and check the second input element if `attendee.attending`'s value is "no".

**placeholder** - Adds a dynamic placeholder for inputs. The placeholder value can be a string or property/compute.

Usage:

```
<input type="text" placeholder="">
```

However, because live-binding can change the value of the input, which requires cleaning up the placeholder, the live-bound value of the `value` property needs to be passed like:

```
<input type="text" value="" placeholder="">
```

**fadeInWhen** - fades in an element when a compute/property is truthy.

Usage:

```
<div id='address' {{fadeInWhen attending}}>
```

### Building the Form

Using the mustache helpers above, creating the form is simple. I create an attendee whose country defaults to "USA", create a map of placeholder data grouped by country, and make a helper function that returns the placeholders for a given country:

```
var attendee = new can.Observe({
    country: "USA"
  }),
  placeholders = {
    usa: {
      state: "state",
      zip: "zip code"
    },
    india: {
      state: "state",
      zip: "postal code"
    },
    canada: {
      state: "province",
      zip: "postal code"
    }
  },
  countryPlaceholders = function(){
    return placeholders[
      (attendee.attr('country')).toLowerCase()
    ];
  }
```

Next, I render the form template:

```
$("#attendee").html( can.view("attendeeMustache", {
    attendee: attendee,
    statePlaceholder: can.compute(function(){
        return countryPlaceholders().state
    }),
    zipPlaceholder: can.compute(function(){
        return countryPlaceholders().zip
    }),
    cityOrStateSetOrCountryNotUSA: can.compute(function(){
        return attendee.attr('state') || 
            attendee.attr('city') || 
            (   attendee.attr('country') && 
              attendee.attr('country') !== "USA"   );
    }),
    attending: can.compute(function(){
        console.log("re-eval");
      return attendee.attr('attending') === 'yes'  
    })
}));
```

The data passed into the template includes:

- attendee - the attendee Observe
- statePlaceholder - the `state` placeholder for the current attendee.country
- zipPlaceholder - the `zip` placeholder for the current attendee.country
- cityOrStateOrCountryNotUSA - returns true if we should be showing the city/state input elements
- attending - returns true if the user is attending

The template uses the data to setup the behavior of the form. The following 2-way binds the attendee's name:

```
<input name="name" type="text" value="" placeholder="name">
```

The following fades in the address part of the form when the `attending` returns true:

```
<div id='address' {{fadeInWhen attending}}>
```

The following 2-way binds `attendee.zip` to the input's value and live-binds `zipPlaceholder` as the input's placeholder:

```
<input name="zip" type="text" value="" placeholder="">
```

To demonstrate 2-way binding, I show the properties of the attendee anytime they are updated with:

```
attendee.bind("change", function(){
  $("#out").text( 
      JSON.stringify( attendee.attr() )
           .replace(/,/g,",\n ") 
  );
})
```

And update all the properties at once with:

```
$("#ba").click(function(){
  attendee.attr({
    name: "Payal's Grandmother",
    attending: "yes",
    country: "India",
    street: "1234 Punjab St",
    apt: "4u",
    city: "Hyderabad",
    state: "Andhra Pradesh",
    zip: "500001"
  })
})
```

Now lets see how these mustache helpers are created!

## Mustache Helper Code

### Value

I'll start with the 2-way binding helper - **value**. To start, I'll create a Value [can.Control](http://canjs.us/#can_control) constructor function that gets created like:

```
new Value(inputEl, {value: compute});
```

Where:

- `compute` is a compute (ex: `can.compute(30)`) that can be set and listened to for updates in its value.
- `inputEl` a form input element.

I create value like:

```
var Value = can.Control({
    init: function(){
        this.set()
    },
    "{value} change": "set",
    set: function(){
        this.element.val(this.options.value())
    },
    "change": function(){
        this.options.value(this.element.val())
    }
});
```

`"{value} change"` listens to the compute's value being changed and calls `set` which updates's the element's value. `"change"` listens for when the user changes the value of the input element and updates the compute's value.

I register a mustache helper with:

```
can.Mustache.registerHelper('value', function(value){
  return function(el){
    new Value(el, {value: value});
  }
});
```

Notice how the helper function returns a **function**. If a helper returns a function, that function gets called back with the `element` the helper is called within, in this case the `<input/>` element below:

```
<input name="name" type="text" value="">
```

So, when the inner function gets called back with the input element, I create a Value control that 2-way binds the compute that represents `attendee.name` with the input element's value!

Also notice how `this.options.value` is called as a setter and getter in the Value control above. If the argument passed to a mustache helper represents an Observe property, that property is converted to a `can.compute` function that can get or sets that property.

This means the mustache helper is called back with the `value` argument being something like:

```
value = can.compute(function(newVal){
    if(newVal !== undefined){
      attendee.attr('name',newVal)
    } else {
      return attendee.attr('name')
    }
})
```

This allows us to call `this.options.value(this.element.val())` to set it or `this.options.value()` to get the current value inside the Value control.

I followed the same pattern for all the other mustache helpers.

### Checked

The `Checked` control is almost exactly like `Value` except that it sets the `checked` property of the element when:

```
this.options.value() === this.element.val()
```

And, when the radio button is clicked, it only sets the compute value if the radio button is checked:

```
if(this.element.is(":checked")){
  this.options.value(this.element.val())
}
```

### FadeInWhen

`FadeInWhen` only binds one-way. It simply listens to when its value is truthy and animates; otherwise, it hides the element:

```
"{value} change": function(value, ev, newVal){
    if( newVal ) {
        this.element.fadeIn();
    } else {
        this.element.hide()
    }
 }
```

### Placeholder

The `Placeholder` control binds some text to the HTML5 placeholder attribute. For browsers that don't support it (IE8), it contains logic to display either the element's value or its placeholder.

The **placeholder** helper accepts two properties, `placeholder` and `value`. As `placeholder` can be a String, the helper converts it to a compute before passing it to the `Placeholder` control:

```
can.Mustache.registerHelper('placeholder',
  function(placeholder, value){
  return function(el){
    if(typeof placeholder === "string"){
      placeholder = can.compute(placeholder);
    }
    new Placeholder(el, {
      placeholder: placeholder,
      value: value
    });
  }
});
```

`Placeholder` listens to the input element's focus, blur, and change events, and `value` to determine if the `placeholder` value should be displayed.

## Conclusion

CanJS makes it super easy to roll your own 2-way live binding widgets. Collecting these and similar common widgets into a plugin is probably a smart idea.

With 2-way live binding, complex form logic becomes very simple to write and maintain.

This currently uses some fixes in 1.1.4 which should be dropping this week.

Give me some suggestions for next week!

[![Tag for open source](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) open source ](https://www.bitovi.com/blog/topic/open-source)[![Tag for development](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) development ](https://www.bitovi.com/blog/topic/development)

 Previous Post

![canjs-banner-background](https://www.bitovi.com/hs-fs/hubfs/canjs-banner-background.jpg?height=117&name=canjs-banner-background.jpg) [ Weekly Widget 1 - TreeCombo ](https://www.bitovi.com/blog/weekly-widget-1-treecombo)

  

 Next Post

![canjs-banner-background](https://www.bitovi.com/hs-fs/hubfs/canjs-banner-background.jpg?height=117&name=canjs-banner-background.jpg) [ Weekly Widget 3 - Paginated Grid ](https://www.bitovi.com/blog/weekly-widget-3-paginated-grid)

```json
{
  "@context" : "http://schema.org",
  "@type" : "Organization",
  "address" : {
    "@type" : "PostalAddress",
    "addressCountry" : "United States",
    "addressLocality" : "Libertyville",
    "addressRegion" : "IL",
    "postalCode" : "60048",
    "streetAddress" : "1134 Pine Tree Lane "
  },
  "alternateName" : "Bitovi",
  "areaServed" : {
    "@type" : "GeoCircle",
    "geoMidpoint" : {
      "@type" : "GeoCoordinates",
      "latitude" : "41.8781",
      "longitude" : "87.6298"
    },
    "geoRadius" : "5000 km"
  },
  "description" : "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
  "email" : "contact@bitovi.com",
  "image" : "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
  "logo" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
  "mainEntityOfPage" : {
    "@id" : "https://www.bitovi.com/blog/weekly-widget-2-2-way-mustache-helpers",
    "@type" : "WebPage",
    "description" : "Weekly Widget 2 - 2-Way Mustache Helpers"
  },
  "naics" : "541511",
  "name" : "Bitovi Web App Consulting",
  "sameAs" : [ "https://www.facebook.com/BitoviLLC/", "https://twitter.com/bitovi", "https://www.linkedin.com/company/bitovi" ],
  "telephone" : "312-620-0386",
  "url" : "http://bitovi.com"
}
```

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Justin Meyer"
  },
  "dateModified" : "November 29, 2018, 5:52:54 PM",
  "datePublished" : "2013-01-27 20:31:45",
  "description" : "Weekly Widget 2 - 2-Way Mustache Helpers",
  "headline" : "Weekly Widget 2 - 2-Way Mustache Helpers",
  "image" : {
    "@type" : "ImageObject",
    "url" : "https://www.bitovi.com/hubfs/canjs-banner-background.jpg"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg"
    },
    "name" : "Bitovi"
  }
}
```