Skip to content

Commit

Permalink
Updating to eliminate attributeBindings
Browse files Browse the repository at this point in the history
  • Loading branch information
ef4 committed Feb 28, 2015
1 parent 1bede8d commit 710b27c
Showing 1 changed file with 55 additions and 30 deletions.
85 changes: 55 additions & 30 deletions active/0000-element-and-fragment.md
Expand Up @@ -34,8 +34,8 @@ template **must** be defined with the `<element>` keyword.
If you place this template in `my-component.hbs`:

````handlebars
<element class="special {{class}}" data-my-id={{id}}>
Hello {{name}}.
<element class="special {{attrs.class}}" data-my-id={{attrs.id}}>
Hello {{attrs.name}}.
</element>
````

Expand All @@ -48,7 +48,7 @@ It can be invoked like this:
And will result in this output:

````html
<div class="special magical" data-my-id="1">
<div class="special magical" data-my-id="1" id="1" name="Tomster">
Hello Tomster.
</div>
````
Expand All @@ -70,6 +70,57 @@ The output changes to
</span>
````

## `<element>` HTML Attribute Semantics in Detail

There are three places that may legitimately need to set a component's
HTML attributes:

1. The caller of a component may intend one of more of the provided
attributes to go directly onto the HTML element:

<my-component title="Tomster" model=hamster>

Every attribute passed into a component in this way gets set on
the component's `attrs` object, which is how components receive
arguments in general. Any attribute with a primitive value
(string, number, boolean) *also* gets set by default as an HTML
attribute on the component's element. So in the example above,
`title="Tomster"` will appear by default on the component's
element, but `model` will not, assuming `model` is an `object`.

2. The component itself may set its own HTML attributes:

<element title="Mr {{attrs.title}}">

This is logically equivalent to calling `setAttribute` directly on
the component's element in the `didInsertElement` hook, and
setting up a corresponding observer to keep the value up to
date. **It takes precedence over any values set by the caller.**

It is important to understand that `<element>` only sets HTML
attributes, it does not set `attrs`. So the above example leaves
the original caller provided `attrs.title` alone, but overrides
the `title` HTML attribute to have a modified value.

3. The component's parent class may *also* have attribute settings in
its `<element>` that should be inherited. A child components takes
precedence over its parent component's attributes.

This comment has been minimized.

Copy link
@mmun

mmun Mar 1, 2015

This doesn't cover inheritance (e.g. mergeable class name bindings across classes). It would be nice to crack this nut.

This comment has been minimized.

Copy link
@ef4

ef4 Mar 1, 2015

Author Owner

It covers most of inheritance, but you're right, the case of merging a single attribute is still missing. I think we can do that too.

What's needed is the equivalent of super for accessing your parent's value for an attribute. class="{{super}} additional".


So we can summarize the complete semantics as this series of logical
steps:

1. First, any caller-provided attributes with primitive values are
set as HTML attributes.

2. Next, the component asks its parent class to set HTML attributes
based on what appears in the parent's `<element>`. This recurses
back so that the most primitive ancestor is setting first, and
descendants have the chance to override their ancestors.

This comment has been minimized.

Copy link
@mmun

mmun Mar 1, 2015

The strategy for accessing your "parent template" needs to be explained. I like embedding the template into the class, but we could consider using module reflection to infer the name of the parent component's template.

This comment has been minimized.

Copy link
@ef4

ef4 Mar 1, 2015

Author Owner

I think the implementation detail of how we find the parent class's template is relatively straightforward. Embedding should always be optional -- it's a good choice for a library. Otherwise we follow naming conventions.

This comment has been minimized.

Copy link
@mmun

mmun Mar 1, 2015

You can't recover the name of a component simply from it's class, hence you cannot recover a component's container-registered template from its class either. Give a string like "x-foo" you can, of course, look up both the template and the class, which is what we do.

Now say you have XFooComponent which extends XParentComponent. Your template will contain the string {{x-foo}} but is it knows about templates/x-foo, but how will it know about templates/x-parent?

This problem is compounded by namespaced components like {{foo/x-bar}}.


3. Finally, the component sets attributes based on its own
`<element>`.


## `<fragment>` semantics

`<fragment>` is very simple, it just declares that your template is a
Expand Down Expand Up @@ -107,28 +158,7 @@ with.

## What happens to `attributeBindings`, `classNames` and `classNameBindings`

In the vast majority of cases, users won't need these anymore, and
will be able to get the same control directly from their template
using `<element>`.

However, we will still keep them to handle trickier inheritance
situations, where a base class or mixin needs to control element
attributes in all derived classes. Only library authors are likely to
need to know about them, and most users should be steered toward using
`<element>` instead.

We can also put:

````js
attributeBindings: ['tagName', 'class']
````
in the base `Component` implementation so that these properties will
propagate from the caller to the `<element>` by default.

Any attributes set directly on `<element>` in a component's definition
**take precedence over** `attributeBindings`. We make a special
exception for `class`: `classNameBindings` and `classNames` will be
merged with any value for `class` provided on the `<element>`.
They get deprecated.

## Fragment names

Expand Down Expand Up @@ -169,11 +199,6 @@ it can define behaviors common to both.
Adds a tiny amount of arguable boilerplate to templates that was not
there before.

This proposal doesn't eliminate `attributeBindings`,
`classNameBindings`, and `classNames`. I think it would be a good idea
to eliminate them, but we need a detailed design for some other way to
do inheritance -- one idea would be template macro expansion.

# Alternatives

Instead of `<element>`, components could just use their own real tag
Expand Down

0 comments on commit 710b27c

Please sign in to comment.