Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

docs($compile): add more info about optional bindings #16025

Merged
merged 4 commits into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/content/error/$compile/iscp.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't & used as an example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea. It's just a list of examples. * also makes no appearance

'localName5': 'attr', // ERROR: missing mode @&=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't mention < 😁

'localName6': 'attr=', // ERROR: must be prefixed with @&=
'localName7': '=attr?', // ERROR: ? must come directly after the mode
}
...
}
Expand Down
37 changes: 33 additions & 4 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,16 +303,15 @@
* 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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggestion about optional attributes seems correct. Why remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed it because I centralized the info after the individual descriptions.

* `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
Copy link
Member

@gkalpak gkalpak Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This info sounds correct too. Why remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is correct: http://plnkr.co/edit/7x6kyfbpWIFCFgTmZb2Z?p=preview
As mentioned below, it will only throw _"upon discovering changes to the local value, since it will be impossible to sync

  • them back to the parent scope"_

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, thanks. This is a more specific case than is handled by the PR.

* optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
* `localModel` and vice versa. If the binding expression is non-assignable, 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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whyyyy??? 😢

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like above, the info about optional attributes is further down. Note that the &, < and @ sections have nothing about optional attributes either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

*
* * `<` 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
Expand Down Expand Up @@ -347,6 +346,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 before the attribute name.
* See the {@link error/$compile/iscp the Invalid Isolate Scope Definition error} for definition examples.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the the --> the

* This is useful to refine the interface directives provides.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provides --> 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need a controllerAs here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope: #15110
This is actually quite powerful ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh...you just can't use it on the template. Cool 😃

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:
Expand Down