Design – Displaying complex objects in AngularJS

angularjsdesignfiltering

What's the best way to display complex objects in AngularJS in your opinion?

You have some data from a service used in multiple places in an app. You need to display multiple properties, each with a specific filter for that particular place.

I can name 3 different approaches, but I'm unsure on how to implement them.

Plunker

Sample (and simple) object

Let's say I display this object as follows: John SMITH ($30.00).

{
    firstName: 'John',
    lastName: 'Smith',
    pocket: 30
}

I could write:

{{person.firstName}} {{person.lastName | uppercase}} ({{person.pocket | currency}})

But what if I have it as an attribute, like in a ng-option

Long string

So I tried this:

<select ng-options="(p.firstName + ' ' + (p.lastName | uppercase) + ' ' + '\(' + (p.pocket | currency) + '\)') for p in persons" ng-model="person"></select>

This is long and unreadable.

Function

I tried the function route:

$scope.formatPerson = function(person) {
    return person.firstName + ' ' + $filter('uppercase')(person.lastName) + ' ' + '\(' + $filter('currency')(person.pocket) + '\)';
}
<select ng-options="(formatPerson(p)) for p in persons" ng-model="person"></select>

Again this isn't very DRY. Where would a function like this belong? In the service that provides the object?

Filter

This one I like best:

app.filter('person', function($filter) {
  return function(person) {
    return person.firstName + ' ' + $filter('uppercase')(person.lastName) + ' ' + '\(' + $filter('currency')(person.pocket) + '\)';
  }
});

I need to be wary of non-person objects, and I want to apply the filter for more complex objects. Are these filters only designed for primitive data types? Could more complex objects lead to slowdown, as the filter is constantly running through the object tree?


Are there others approaches to tackling the problem, or is one of these approaches correct?

Best Answer

CacheFactory would be the most portable and easiest to debug:

function sharedCache($cacheFactory)
   {
   var sharedCache = $cacheFactory.get('sharedCache') ? $cacheFactory.get('sharedCache') : $cacheFactory('sharedCache');

   return sharedCache;
   }

function bar(sharedCache)
   {
   var data = {
     firstName: 'John',
     lastName: 'Smith',
     pocket: 30
     };

   sharedCache.put('sharedCache', data);
   }

bar.$inject = ['sharedCache', '$scope'];
sharedCache.$inject = ['$cacheFactory'];

angular.module('foo',[]);
angular.module('foo').constant('sharedCache', sharedCache);
angular.module('foo').run(bar);

Filters can read the data and output using ng-include and the templateCache. For example:

function bop(name)
  {
  var data = baz.data.get('sharedCache');
  var tmpl = '';

  try
     {
     tmpl = tmpl.concat('<h1>',data.firstName, '</h1>');
     baz.tmpl.put(name, tmpl);
     }
  catch(e)
     {
     console.log(e);
     }
  }

function baz($templateCache, sharedCache)
  {
  baz.tmpl = $templateCache;
  baz.data = sharedCache;
  return bop;
  }

baz.$inject=['$templateCache', 'sharedCache'];
angular.module('foo').filter('baz', baz);

With this markup:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="foo">
  <div ng-include="'userContent' | baz"></div>
</div>
Related Topic