Formatting values in template


#1

Are there any best practices around formatting data in templates? I’ve been looking at Handlebars registerHelper but it doesn’t seem quite right. Making a helper for each type doesn’t seem quite right, so I tried combining them. Here’s what I’m working with:

Ember.Handlebars.registerHelper("format", function(prop, options) {
  var value = this[prop];
  var numberTypes = ["currency", "number", "percent"];
  var type = options.hash.type || "string";

  // Convert to number
  if (numberTypes.indexOf(type) !== -1) {
    var decimals = options.hash.decimals || 2;
    value = Number(value).toFixed(decimals);
    value = commaSeparateNumber(value);

    switch (type) {
      case "currency":
        value = "$" + value;
        break;
      case "percent":
        value += "%";
        break;
      }
  }
  else {
    value = value.toString();
  }

  return value;
});

I don’t want to be that guy, but the Angular way is so succinct and Handlebars just feels clunky.

Do you normally put format functions on the view? I’d like to get started on the right path before I throw something like this all over my application.

Thanks.


#2

fwiw, most people writing helpers used the bound helper approach described in the guides.

It seems strange that you wouldn’t know the datatype of the value you’re outputting; what’s the use case here?

But if it needs to be done this way, I’m curious how you would write something like this in Angular and how it would be way more succinct? Most of the code here involves discerning the type and branching based on the type; what would the equivalent be in Angular?


#3

I would do this on the model/controller via computed properties…

formattedPriceDollars: function () {
  var price = this.get('price'),
    formatted = parseFloat(price, 10).toFixed(2);

  return '$' + formatted;
}.property('price')

and use that in your template: {{formattedPriceDollars}}.


#4

In Angular, you would register a filter in your JavaScript for each type like this:

filters.filter('fromMsDate', function() {
  var oaDate = new Date(1899, 11, 30);
  var millisecondsOfaDay = 24 * 60 * 60 * 1000;

  return function(milliseconds) {
    return new Date().setTime((milliseconds * millisecondsOfaDay) + Date.parse(oaDate));
  };
});

Then, you’d use it in your template like this:

{{timestamp | fromMsDate}}

If you were recreating the same thing I wrote above, you’d call it like this if you wanted to format a percent to 2 decimals:

{{pctOfSomething | format:"percent":2 }}

If you’re going to go that route, I think the Handlebars options.hash is much nicer.

Angular filters are chainable as well, so if you wanted to take an MS date and actually change the output type, you could do this:

{{timestamp | fromMsDate | date:"h:mm A MMM d, YYYY"}}

I’m not sure how you would chain with Handlebars. Making helpers was working pretty well, but I ran into some issues trying to use Bound Helpers inside a block.

Talking about Angular in this forum feels like chatting with Bill Gates about my iPhone, but it’s just what I know. I can’t shake the feeling that Ember would be a better fit for big SPAs, so I want to see what it can do.


#5

Heh, no worries about talking about Angular here; it’s good for Ember to hear about what people are finding to be a better experience in Angular.

You changed examples on me though; can you show me how you’d write your original example in Angular?

The chained filter stuff demoes well, but in my opinion, it seems somewhat gimmicky and at the expense of riding dangerously close to “logic-ful” templating. I suppose if you’re really only using such a thing exactly once in an application, it’s nice to be able to throw {{timestamp | fromMsDate | date:"h:mm A MMM d, YYYY"}} in a template and have it work, but the cleanliness of your template is going to suffer, particularly if you need to use this in multiple places. Why not just use a named helper and put whatever “chaining” you want within the helper?


#6

For better or for worse, I think the filter structure in Angular encourages chaining. It sounds like you would simply suggest making a single Handlebars helper in Ember.

What would the helper look like if you have a number and you want to format two ways. The first is to display as currency. The second is to make it green/red if it’s above/below 0. Each filter should work by itself or combined.

That scenario has been a stumbling block for me.


#7

I apologize for responding to a stale thread, but I found I’m in the same situation. In my case, I’d like to be able to use the Handlebars-provided helpers (like input), but send my value through a formatter first. I don’t want to have to generate a separate computed field for each property on my model (I’m displaying a form with more than a dozen fields). How could I handle this kind of scenario in Ember?


#8

For what it’s worth, I made a view to handle this case.

App.Currency = Ember.View.extend({

  formattedCurrency: function(){
    var value = this.get('content');
    var formatted = parseFloat(value, 10).toFixed(2);
    if (Ember.isEmpty(value) || isNaN(formatted)){
      return '';
    }
    return '$' + formatted;
  }.property('content')

});

And in my template I use it as so:

{{#view App.Currency content=cost}}Formatted: {{view.formattedCurrency}}{{/view}}</p>

Here is an example: jsbin currency view