From e65928eecb3443943e3f9431e394e9bc59787952 Mon Sep 17 00:00:00 2001
From: Martin Staffa <mjstaffa@googlemail.com>
Date: Mon, 5 Jun 2017 20:23:12 +0200
Subject: [PATCH] docs($compile): add more info about optional bindings

This centralizes the info about optional bindings.
Also adds more examples to the $compile:iscp error.

Closes #15989
Closes #16025
---
 docs/content/error/$compile/iscp.ngdoc | 16 +++++----
 src/ng/compile.js                      | 45 ++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/docs/content/error/$compile/iscp.ngdoc b/docs/content/error/$compile/iscp.ngdoc
index 78f82f2f6c61..8153e44d4a05 100644
--- a/docs/content/error/$compile/iscp.ngdoc
+++ b/docs/content/error/$compile/iscp.ngdoc
@@ -10,13 +10,15 @@ myModule.directive('directiveName', function factory() {
   return {
     ...
     scope: {
-      'attrName': '@', // OK
-      'attrName2': '=localName', // OK
-      'attrName3': '<?localName', // OK
-      'attrName4': ' = name', // OK
-      'attrName5': 'name',    // ERROR: missing mode @&=
-      'attrName6': 'name=',   // ERROR: must be prefixed with @&=
-      'attrName7': '=name?',  // ERROR: ? must come directly after the mode
+      'localName': '@', // OK
+      'localName2': '&attr', // OK
+      'localName3': '<?attr', // OK
+      'localName4': ' = attr', // OK
+      'localName5': ' =*attr', // OK      
+      'localName6': 'attr',    // ERROR: missing mode @&=<
+      'localName7': 'attr=',   // ERROR: must be prefixed with @&=<
+      'localName8': '=attr?',  // ERROR: ? must come directly after the mode
+      'localName9': '<*'  // ERROR: * is only valid with =
     }
     ...
   }
diff --git a/src/ng/compile.js b/src/ng/compile.js
index a2d9730c3920..ef2e65ad379d 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -303,21 +303,22 @@
  *   name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
  *   localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
  *   value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
- *   `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
- *   `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
- *   optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
- *   will be thrown upon discovering changes to the local value, since it will be impossible to sync
- *   them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
+ *   `localModel` and vice versa. If the binding expression is non-assignable, or if the attribute
+ *   isn't  optional and doesn't exist, an exception
+ *   ({@link error/$compile/nonassign `$compile:nonassign`}) will be thrown upon discovering changes
+ *   to the local value, since it will be impossible to sync them back to the parent scope.
+ *
+ *   By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
  *   method is used for tracking changes, and the equality check is based on object identity.
  *   However, if an object literal or an array literal is passed as the binding expression, the
  *   equality check is done by value (using the {@link angular.equals} function). It's also possible
  *   to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
- *   `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
+ *   `$watchCollection`}: use `=*` or `=*attr`
  *
   * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
  *   expression passed via the attribute `attr`. The expression is evaluated in the context of the
  *   parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
- *   local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
+ *   local name.
  *
  *   For example, given `<my-component my-attr="parentModel">` and directive definition of
  *   `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
@@ -347,6 +348,36 @@
  *   and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
  *   then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
  *
+ * All 4 kinds of bindings (`@`, `=`, `<`, and `&`) can be made optional by adding `?` to the expression.
+ * The marker must come after the mode and before the attribute name.
+ * See the {@link error/$compile/iscp Invalid Isolate Scope Definition error} for definition examples.
+ * This is useful to refine the interface directives provide.
+ * One subtle difference between optional and non-optional happens **when the binding attribute is not
+ * set**:
+ * - the binding is optional: the property will not be defined
+ * - the binding is not optional: the property is defined
+ *
+ * ```js
+ *app.directive('testDir', function() {
+    return {
+      scope: {
+        notoptional: '=',
+        optional: '=?',
+      },
+      bindToController: true,
+      controller: function() {
+        this.$onInit = function() {
+          console.log(this.hasOwnProperty('notoptional')) // true
+          console.log(this.hasOwnProperty('optional')) // false
+        }
+      }
+    }
+  })
+ *```
+ *
+ *
+ * ##### Combining directives with different scope defintions
+ *
  * In general it's possible to apply more than one directive to one element, but there might be limitations
  * depending on the type of scope required by the directives. The following points will help explain these limitations.
  * For simplicity only two directives are taken into account, but it is also applicable for several directives: