|
| 1 | +- 2014-11-26 |
| 2 | +- RFC PR: |
| 3 | +- Ember Issue: |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Unlike Handlebars, HTMLBars parses HTML as it parses a template. |
| 8 | +Bound attributes are one syntax now possible. |
| 9 | + |
| 10 | +For example, this variable `color` is bound to set a class: |
| 11 | + |
| 12 | +```hbs |
| 13 | +<div class="{{color}}"></div> |
| 14 | +``` |
| 15 | + |
| 16 | +Though traditional HTML attribute syntax should be preserved (using |
| 17 | +`class` and not `className` for example), the default path will be |
| 18 | +to set attributes as properties on the DOM node. |
| 19 | + |
| 20 | +However this happy path has several important exceptions, and results |
| 21 | +in a few strange edge cases. This rfc will go into detail about the |
| 22 | +expected behavior without talking about the implementation of attribute |
| 23 | +on the Ember rendering pipeline. |
| 24 | + |
| 25 | +# Motivation |
| 26 | + |
| 27 | +`{{bind-attr` is a verbose syntax and difficult for new developers to |
| 28 | +understand. |
| 29 | + |
| 30 | +# Detailed design |
| 31 | + |
| 32 | +Given a use of bound attributes: |
| 33 | + |
| 34 | +```hbs |
| 35 | +<input type="checkbox" checked={{isChecked}}> |
| 36 | +``` |
| 37 | + |
| 38 | +There are three important inputs: |
| 39 | + |
| 40 | +* The element (`tagName`, `namespaceURI`) |
| 41 | +* The attribute name |
| 42 | +* The value (literal or stream) |
| 43 | + |
| 44 | +The following described the algorithm for updating the attribute/property |
| 45 | +value on an element. |
| 46 | + |
| 47 | +1. If the element has an SVG namespace, use `setAttribute`. Setting SVG attributes |
| 48 | + as properties is not supported. |
| 49 | +2. If the attribute name is `style`, use `setAttribute`. |
| 50 | +3. Normalize the property name as described in `propertyNameFor` below. If a normalized |
| 51 | + name is returned, set that property on the element (`element[normalizedPropName]`). |
| 52 | + If it is not returned, set with `setAttribute`. |
| 53 | + |
| 54 | +`propertyNameFor` is a normalization setup for attribute names that takes the element |
| 55 | +and attribute name as input. |
| 56 | + |
| 57 | +1. Build a list of normalized properties for the passed element `normalizedAttrs[element.tagName][elementAttrName.toLowerCase()] = elementAttrName` |
| 58 | +2. Fetch the normalized property name from this list `normalizedAttr = normalizedAttrs[element.tagName][passedAttrName.toLowerCase()]` |
| 59 | +3. Return this normalized attr. If an `attrName` is did not normalize to a property (for example `class`), null is returned |
| 60 | + |
| 61 | +### Acknowledged edge cases |
| 62 | + |
| 63 | +* Boolean attrs with blank string won't work like they would in HTML: `<input disabled="{{blankString}}">` would be false |
| 64 | +* Some selectors may not work as expected. `<input value="{{color}}">` will not result in a working `[value=red]` selector |
| 65 | + |
| 66 | +# Drawbacks |
| 67 | + |
| 68 | +None. |
| 69 | + |
| 70 | +# Alternatives |
| 71 | + |
| 72 | +Two obvious alternatives considered in detail are Angular and React. |
| 73 | + |
| 74 | +In **Angular 2.0**, [a new prop/attr/event syntax](http://www.beyondjava.net/blog/angularjs-2-0-sneak-preview-data-binding/) |
| 75 | +is being introduced. |
| 76 | + |
| 77 | +Setting an attribute just like setting an HTML attribute: |
| 78 | + |
| 79 | +```html |
| 80 | +<pui-tab title="What a nice tab!"> |
| 81 | +``` |
| 82 | + |
| 83 | +Properties are flagged with the `[]` syntax: |
| 84 | + |
| 85 | +```html |
| 86 | +<input [disabled]="controller.isInputDisabled"> |
| 87 | +``` |
| 88 | + |
| 89 | +Angular is limited by it's HTML templating here. The value must be quoted |
| 90 | +to have complex content, where as in HTMLBars it is easier to bend the |
| 91 | +rules to introduce literal values: `disabled={{controller.isInputDisabled}}`. |
| 92 | + |
| 93 | +Events are out of our immediate purview in this RFC, but for completeness |
| 94 | +note Angular's syntax: |
| 95 | + |
| 96 | +```html |
| 97 | +<button (click)="hide()">hide image</button> |
| 98 | +``` |
| 99 | + |
| 100 | +**React's JSX** has its own [property syntax](http://facebook.github.io/react/docs/jsx-in-depth.html), |
| 101 | +one that diverges from traditional HTML by focusing entirely on properties |
| 102 | +instead of attributes. This means the templates are well prepared for |
| 103 | +use with components, but also that JSX must maintain a large whitelist of |
| 104 | +special cases such as [supported tags](http://facebook.github.io/react/docs/tags-and-attributes.html) |
| 105 | +and [some HTML attributes](http://facebook.github.io/react/docs/jsx-gotchas.html). |
| 106 | + |
| 107 | +In general we would prefer to have Ember templates be as close to HTML |
| 108 | +as possible, without requiring developers to learn a new set of property |
| 109 | +names replacing the attribute names they already know. |
| 110 | + |
| 111 | +# Unresolved questions |
| 112 | + |
| 113 | +* How do we deal with `on*` attributes? |
| 114 | +* Should we do anything special about generic element properties like `<div outerhtml={{lol}}></div>`? |
| 115 | +* Should HTMLBars unbound attributes use the same alorithm? |
| 116 | + |
| 117 | +There is a spike of significant depth [in PR #9721](https://github.com/emberjs/ember.js/pull/9721) |
| 118 | +and a followup [in PR #9977](https://github.com/emberjs/ember.js/pull/9977). |
0 commit comments