Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - Best practice: model properties on view or function calls?

For quite sometime I've been wondering about this question: when working with AngularJS, should I use directly the model object properties on the view or can I use a function to get the that property value?

I've been doing some minor home projects in Angular, and (specially working with read-only directives or controllers) I tend to create scope functions to access and display scope objects and their properties values on the views, but performance-wise, is this a good way to go?

This way seems easier for maintaining the view code, since, if for some reason the object is changed (due to a server implementation or any other particular reason), I only have to change the directive's JS code, instead of the HTML. Here's an example:

//this goes inside directive's link function
scope.getPropertyX = function() {
    return scope.object.subobject.propX;
}

in the view I could simply do

<span>{{ getPropertyX() }}</span>

instead of

<span>{{ object.subobject.propX }}</span>

which is harder to maintain, amidst the HTML clutter that sometimes it's involved. Another case is using scope functions to test properties values for evaluations on a ng-if, instead of using directly that test expression:

scope.testCondition = function() {
    return scope.obj.subobj.propX === 1 && scope.obj.subobj.propY === 2 && ...;
}

So, are there any pros/cons of this approach? Could you provide me with some insight on this issue? It's been bothering me lately, on how an heavy app might behave when, for example a directive can get really complex, and on top of that could be used inside a ng-repeat that could generate hundreds or thousands of its instances.

Thank you

like image 436
NunoM Avatar asked Nov 03 '14 23:11

NunoM


People also ask

What is not recommended in AngularJS?

It is tempting to do too much work in the AngularJS controller. After all, the controller is where the view first has access to JavaScript via $scope functions. However, doing this will cause you to miss out on code sharing across the site and is not recommended by AngularJS documentation.

Which of the following is the correct syntax for making a get call and handling the error in angular?

get(url) . then(function (response) { console. log('get',response) }) . catch(function (data) { // Handle error here });

What is view model in AngularJS?

In angular, the view is your HTML, the controllers are your angular controllers (no surprise on this one) and the ViewModel is the $scope object which you can access from either the controller or the view (it's kind of like the glue that links the controller to the view).

Which of the following is true about AngularJS expressions?

Which of the following is true about AngularJS expressions? Expressions are used to bind application data to HTML.


2 Answers

I don't think creating functions for all of your properties is a good idea. Not just will there be more function calls being made every digest cycle to see if the function return value has changed but it really seems less readable and maintainable to me. It could add a lot of unnecessary code to your controllers and is sort of making your controller into a view model. Your second case seems perfectly fine, complex operations seems like exactly what you would want your controller to handle.

As for performance it does make a difference according to a test I wrote (fiddle, tried to use jsperf but couldn't get different setup per test). The results are almost twice as fast, i.e. 223,000 digests/sec using properties versus 120,000 digests/sec using getter functions. Watches are created for bindings that use angular's $parse.

One thing to think about is inheritance. If you uncomment the ng-repeat list in the fiddle and inspect the scope of one of the elements you can see what I'm talking about. Each child scope that is created inherits the parent scope's properties. For objects it inherits a reference, so if you have 50 properties on your object it only copies the object reference value to the child scope. If you have 50 manually created functions it will copy each of those function to each child scope that it inherits from. The timings are slower for both methods, 126,000 digests/sec for properties and 80,000 digests/sec with getter functions.

I really don't see how it would be any easier for maintaining your code and it seems more difficult to me. If you want to not have to touch your HTML if the server object changes it would probably be better to do that in a javascript object instead of putting getter functions directly on your scope, i.e.:

$scope.obj = new MyObject(obj); // MyObject class

In addition, Angular 2.0 will be using Object.observe() which should increase performance even more, but would not improve the performance using getter functions on your scope.

It looks like this code is all executed for each function call. It calls contextGetter(), fnGetter(), and ensureSafeFn(), as well as ensureSafeObject() for each argument, for the scope itself and for the return value.

return function $parseFunctionCall(scope, locals) {
  var context = contextGetter ? contextGetter(scope, locals) : scope;
  var fn = fnGetter(scope, locals, context) || noop;

  if (args) {
    var i = argsFn.length;
    while (i--) {
      args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
    }
  }

  ensureSafeObject(context, expressionText);
  ensureSafeFunction(fn, expressionText);

  // IE stupidity! (IE doesn't have apply for some native functions)
  var v = fn.apply
        ? fn.apply(context, args)
        : fn(args[0], args[1], args[2], args[3], args[4]);

  return ensureSafeObject(v, expressionText);
};

},

By contrast, simple properties are compiled down to something like this:

(function(s,l /**/) {
    if(s == null) return undefined;
    s=((l&&l.hasOwnProperty("obj"))?l:s).obj;
    if(s == null) return undefined;
    s=s.subobj;
    if(s == null) return undefined;
    s=s.A;
    return s;
})
like image 101
Jason Goemaat Avatar answered Oct 05 '22 22:10

Jason Goemaat


Performance wise - it is likely to matter little

Jason Goemaat did a great job providing a Benchmarking Fiddle. Where you can change the last line from:

setTimeout(function() { benchmark(1); }, 500);

to

setTimeout(function() { benchmark(0); }, 500);

to see the difference.

But he also frames the answer as properties are twice as fast as function calls. In fact, on my mid-2014 MacBook Pro, properties are three times faster.

But equally, the difference between calling a function or accessing the property directly is 0.00001 seconds - or 10 microseconds.

This means that if you have 100 getters, they will be slower by 1ms compared to having 100 properties accessed.

Just to put things in context, the time it takes sensory input (a photon hitting the retina) to reach our consciousness is 300ms (yes, conscious reality is 300ms delayed). So you'd need 30,000 getters on a single view to get the same delay.

Code quality wise - it could matter a great deal

In the days of assembler, software was looked like this:

A collection of executable lines of code.

But nowadays, specifically for software that have even the slightest level of complexity, the view is:

A social interaction between communication objects.

The latter is concerned much more about how behaviour is established via communication objects, than the actual low-level implementation. In turn, the communication is granted by an interface, which is typically achieved using the query or command principle. What matters, is the interface of (or the contract between) collaborating objects, not the low-level implementation.

By inspecting properties directly, you are hit the internals of an object bypassing its interface, thus couple the caller to the callee.

Obviously, with getters, you may rightly ask "what's the point". Well, consider these changes to implantation that won't affect the interface:

  • Checking for edge cases, such as whether the property is defined at all.
  • Change from getName() returning first + last names rather than just the name property.
  • Deciding to store the property in mutable construct.

So even an implementation seemingly simple may change in a way that if using getters would only require single change, where without will require many changes.

I vote getters

So I argue that unless you have a profiled case for optimisation, you should use getters.

like image 43
Izhaki Avatar answered Oct 05 '22 23:10

Izhaki