AngularJS MTV Meetup: Best Practices (2012/12/11)

By: Angular

1690   16   350132

Uploaded on 12/12/2012

http://www.meetup.com/AngularJS-MTV/events/93943412/
Presentation slides available at: http://goo.gl/CD0Is
Live from the Mountain View, CA meetup, Miško Hevery discusses the advantages and disadvantages of various design choices when structuring an app in AngularJS, and some of the best practices that we use in our own development.

Some of the topics we'll cover:
- Style best practices that we use in our own projects at Google
- Handling different browsers when writing directives (e.g. IE)
- Comparisons between different ways of structuring your application:
+ using controller vs scope
+ using directive prefix vs namespace
+ using element name vs attribute name vs class

Comments (13):

By anonymous    2017-09-20

Executive Summary:

In AngularJS, a child scope normally prototypically inherits from its parent scope. One exception to this rule is a directive that uses scope: { ... } -- this creates an "isolate" scope that does not prototypically inherit.(and directive with transclusion) This construct is often used when creating a "reusable component" directive. In directives, the parent scope is used directly by default, which means that whatever you change in your directive that comes from the parent scope will also change in the parent scope. If you set scope:true (instead of scope: { ... }), then prototypical inheritance will be used for that directive.

Scope inheritance is normally straightforward, and you often don't even need to know it is happening... until you try 2-way data binding (i.e., form elements, ng-model) to a primitive (e.g., number, string, boolean) defined on the parent scope from inside the child scope. It doesn't work the way most people expect it should work. What happens is that the child scope gets its own property that hides/shadows the parent property of the same name. This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works. New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view, ng-include and ng-if all create new child scopes, so the problem often shows up when these directives are involved. (See this example for a quick illustration of the problem.)

This issue with primitives can be easily avoided by following the "best practice" of always have a dot '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.

--AngularJS Wiki -- The Nuances of Scope Prototypal Inheritance


I tried to do the '.' notation in my scope but whenever I try to reference it like this:

$scope.moment.description = ""; 

It says "Cannot read property 'description' of undefined.

The code needs to create the object before assigning a value to a property:

$scope.moment = {};
$scope.moment.description = "";

//OR

$scope.moment = { description: "" };    

Original Thread

By anonymous    2017-09-20

Use property accessor bracket notation with the this context keyword:

<div ng-repeat="count in [3,4,5,6,7,8,9,10,11,12]" class="padding-top-10">
  <span>{{this["head"+count]}}</span>
</div>

With Angular Expressions the this keyword evaluates to the $scope of the expression.

Be aware that the ng-repeat directive creates child scopes and that this expression finds the value by prototypical inheritance. This can cause problems when using this in ng-model directives. (This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models)

For more information, see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?.

Original Thread

By anonymous    2017-09-20

You can use md-radio-group and md-radio-button inside table exactly the way you did. The reason your code doesn't work is not obeying the "always use a dot rule".

this is a working example

Original Thread

By anonymous    2017-11-27

Core AngularJS directives such as ng-repeat, ng-switch, ng-view, ng-include and ng-if all create new child scopes. As a result, the form controller will attach its API to that child scope. In this case the uib-tabset directive is transcluding to a child scope.

To access form controls on the parent scope, use the ng-form directive to create a nested form:

<form name="top">
    <div ng-if="AngularExpression">
       <ng-form name="formA" novalidate>
           <input ng-model="userData.testA" required />
       </ng-form>
   </div>
</form>

<div ng-show="top.formA.$error.$required">
   ERROR: input required
</div>

Also it is important to follow the "best practice" of always have a dot, '.', in your ng-models.

For more information, see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?.

Original Thread

By anonymous    2018-05-14

This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m – watch 3 minutes worth. Misko demonstrates the primitive binding issue with `ng-switch`.

Original Thread

By anonymous    2018-05-14

http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m – watch 3 minutes worth. Misko demonstrates the primitive binding issue with `ng-switch`.

Original Thread

By anonymous    2018-05-29

I think you are running into this issue when binding directly to a primitive: https://github.com/angular/angular.js/wiki/Understanding-Scopes

Emphasis mine

Scope inheritance is normally straightforward, and you often don't even need to know it is happening... until you try 2-way data binding (i.e., form elements, ng-model) to a primitive (e.g., number, string, boolean) defined on the parent scope from inside the child scope. It doesn't work the way most people expect it should work. What happens is that the child scope gets its own property that hides/shadows the parent property of the same name. This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works. New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view, ng-include and ng-if all create new child scopes, so the problem often shows up when these directives are involved. (See this example for a quick illustration of the problem.)

This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.

The plunker linked to above above shows your issue directly (source below):

javascript:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {

  /*
  ng-repeat generates new scopes which will be child scopes of the scope within
  which they are generated. In other words, this scope is the parent scope for
  the child scopes generated by the ng-repeat in this example. Child scopes
  inherit things from their parent's scope.
  */

  // The initial main image 
  var initialImg = "http://3.bp.blogspot.com/-z8kzafZYkfQ/UERf6IbjJJI/AAAAAAAAALE/qaAxqqawXpM/s1600/Cat+Pictures+1.jpg";

  /*
  A primitive holding the URL for the main image

  This scope's child scopes will "shadow" this primitive, which basically means
  they'll get their own copy that is initialy the same value. The child scopes
  can only see their own copy though, so modifying the value in the child scope
  does not affect the value in the parent scope.
  */
  $scope.mainImgUrl = initialImg;

  /*
  An object holding the URL for the main image

  This scope's child scopes will NOT get their own copy of this object.
  Referencing main or main.imgUrl in the child scope will reference this object
  on this scope (unless the child scope explicitly define its own "mainImg" object.)
  */
  $scope.mainImg = { url: initialImg };

  // Our 'thumbnail' images
  $scope.images = [
      "http://happy.fm/wp-content/uploads/2011/10/random-owl.jpg",
      "http://www.superhumor.com/emoticonos/8761.gif"
  ];

});

html:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.0.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" data-semver="1.0.7"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">

    <h1>ng-click inside ng-repeat setting value on parent scope</h1>

    <p>
    Example to illustrate the nuances of prototypical inheritance. See 
    <a href="http://docs.angularjs.org/tutorial/step_10#comment-977962885">this Angular Tutorial Step 10 question</a>
    and
    <a href="https://github.com/angular/angular.js/wiki/Understanding-Scopes">Understanding Scopes</a>
    .
    </p>


    <h3>Using primitive:</h3>

    <div class="example">

      <img class="mainImg" ng-src="{{mainImgUrl}}" />

      <p>This is the parent scope with the main image.</p>

      <p>$scope.mainImgUrl == {{mainImgUrl}}</p>

      <div class="thumbs">

        <p>Thumbs generated with ng-repeat, with ng-click setting <strong>$scope.mainImgUrl</strong> (click on them to see what happens):</p>

        <div class="thumbDiv" ng-repeat="img in images">

          <img class="thumb" ng-src="{{img}}" ng-click="mainImgUrl = img" />

          <p>This is a child scope generated by ng-repeat.</p>

          <p>$scope.mainImgUrl == {{mainImgUrl}}</p>

        </div>

      </div>

    </div>


    <h3>Using object:</h3>

    <div class="example">

      <img class="mainImg" ng-src="{{mainImg.url}}" />

      <p>This is the parent scope with the main image.</p>

      <p>$scope.mainImg.url == {{mainImg.url}}</p>

      <div class="thumbs">

        <p>Thumbs generated with ng-repeat, with ng-click setting <strong>$scope.mainImg.url</strong> (click on them to see what happens):</p>

        <div class="thumbDiv" ng-repeat="img in images">

          <img class="thumb" ng-src="{{img}}" ng-click="mainImg.url = img" />

          <p>This is a child scope generated by ng-repeat.</p>

          <p>$scope.mainImg.url == {{mainImg.url}}</p>

        </div>

      </div>

    </div>

  </body>

</html>

Original Thread

By anonymous    2018-06-22

Problems with data hiding can be avoided by binding to a property of an object. Similar to the "best practice" of always have a '.' in your ng-models. Watch this [YouTube video](http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m) Misko demonstrates the primitive binding issue with `ng-switch`.

Original Thread

By anonymous    2018-07-02

Use of `$parent` is a [code smell](https://en.wikipedia.org/wiki/Code_smell), a symptom of a deeper problem. When there is more than one child scope between the data and the `ng-model`, it won't work. It is better to use a named object. See this [Youtube video](http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m). Misko demonstrates the primitive binding issue with `ng-switch`.

Original Thread

By anonymous    2018-07-02

Use of `$parent` is a [code smell](https://en.wikipedia.org/wiki/Code_smell), a symptom of a deeper problem. When there is more than one child scope between the data and the `ng-model`, it won't work. It is better to use a named object. See this [Youtube video](http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m). Misko demonstrates the primitive binding issue with `ng-switch`.

Original Thread

Popular Videos 2286

Submit Your Video

If you have some great dev videos to share, please fill out this form.