Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE ember-htmlbars-scoped-helpers] #10244

Closed

Conversation

rwjblue
Copy link
Member

@rwjblue rwjblue commented Jan 18, 2015

Allows locally scoped helpers to be used in a components layout (or in the template block if using block params).


Local Helpers Example:

// app/components/x-foo.js

import Ember from "ember";

var makeBoundHelper = Ember.HTMLBars.makeBoundHelper;

export default Ember.Component.extend({
  capitalize: makeBoundHelper(function(params) {
    return params[0].toUpperCase();
  })
});
{{! app/templates/components/x-foo.hbs }}

{{capitalize 'blahzorz'}} === BLAHZORZ

Block Params Example:

// app/components/form-for.js

import Ember from "ember";
import FormForInput from "./helpers/input";

var makeViewHelper = Ember.HTMLBars.makeViewHelper;

export default Ember.Component.extend({
  formHelpers: {
    input: makeViewHelper(FormForInput)
  }
});
{{! app/templates/components/form-for.hbs }}

{{yield formHelpers}}
{{! app/templates/post/new.hbs }}

{{#form-for as |f|}}
  {{f.input label="Title" value=model.title}}
{{/form-for}}

Allows locally scoped helpers to be used in a components layout (or in
the template block if using block params).

----

Local Helpers Example:

```javascript
// app/components/x-foo.js

import Ember from "ember";

var makeBoundHelper = Ember.HTMLBars.makeBoundHelper;

export default Ember.Component.extend({
  capitalize: makeBoundHelper(function(params) {
    return params[0].toUpperCase();
  })
});
```

```handlebars
{{! app/templates/components/x-foo.hbs }}

{{capitalize 'blahzorz'}} === BLAHZORZ
```

----

Block Params Example:

```javascript
// app/components/form-for.js

import Ember from "ember";
import FormForInput from "./helpers/input";

var makeViewHelper = Ember.HTMLBars.makeViewHelper;

export default Ember.Component.extend({
  formHelpers: {
    input: makeViewHelper(FormForInput)
  }
});
```

```handlebars
{{! app/templates/components/form-for.hbs }}

{{yield formHelpers}}
```

```handlebars
{{! app/templates/post/new.hbs }}

{{#form-for as |f|}}
  {{f.input label="Title" value=model.title}}
{{/form-for}}
```
@chadhietala
Copy link
Contributor

Nice.

@jgwhite
Copy link
Contributor

jgwhite commented Jan 18, 2015

This is glorious

@taras
Copy link
Contributor

taras commented Jan 18, 2015

Damn... This is awesome!

@bcardarella
Copy link
Contributor

@rwjblue awesome, this is pretty much the last blocker for easy-form 2.0

@darkbaby123
Copy link
Contributor

One thing I want ask. In my understanding HTMLBars prefers <form-for> than {{form-for}} because of new action syntax. e.g below syntax can not used in {{}} mode:

<form-for on-submit={{action "saveUser"}}></form-for>

When it comes to {{f.input}}, does that mean we can only use {{}} syntax?

@rwjblue
Copy link
Member Author

rwjblue commented Jan 19, 2015

@darkbaby123 - the ember-htmlbars-component-generation is not enabled , but the examples above will still work properly when it lands. It would look like the following:

<!-- app/templates/post/new.hbs -->

<form-for as |f|>
  <f.input label="Title" value={{model.title}}></f.input>
<form-for>

@bcardarella
Copy link
Contributor

@rwjblue so this is a bit of a tangent, but if <f.input></f.input> is going to be valid HTMLBars syntax can we reopen the discussion around namespacing? <foo@input></foo@input>

/cc @stefanpenner

@taras
Copy link
Contributor

taras commented Jan 19, 2015

@rwjblue is it possible to access the parent component from the helper callback?

@rwjblue
Copy link
Member Author

rwjblue commented Jan 19, 2015

@bcardarella - Do you mean the ember-resolver namespace work that you did a couple months ago? I'm unsure that that discussion was closed. This may need an RFC to explore the details (and to have a central location for the discussion).

@taras - That is not really related to this feature. In 1.10 you can already yield the current component as your block param, and access its properties in the template. Example JSBin

@bcardarella
Copy link
Contributor

@rwjblue the ember-resolver namespace work was just part of it, I'm referring to actually being able to call components from another namespace than just your application's. For example, let's say there was an ember-cli addon called d3 and there was a pie-chart component in that addon, you might imagine calling this component that does not live in your application's namespace like: <d3@pie-chart></d3@pie-chart>

Last I recall it was decided not to support this as there was a desire to keep as close to spec HTML as possible. But in light of the syntax you outlined above, if we're no longer considering spec HTML the goal for HTMLBars then I would argue the door should be re-opened on the namespace syntax. If there are no objections then I can write an RFC for this, but I don't want to go through the effort unless it has already been vetted and turned down.

@rwjblue
Copy link
Member Author

rwjblue commented Jan 19, 2015

Last I recall it was decided not to support this as there was a desire to keep as close to spec HTML as possible.

@bcardarella - I don't remember this having being decided (although I may just be forgetting). We should absolutely have that discussion and officially decide if we want <foo@input> to work, but I think we need to make an RFC for that. If possible I'd like to keep the discussion in this PR focused on the ember-htmlbars-scoped-helpers feature being added.

@bcardarella
Copy link
Contributor

👍

@@ -33,6 +33,13 @@ export default function lookupHelper(name, view, env) {
return helper;
}

if (Ember.FEATURES.isEnabled('ember-htmlbars-scoped-helpers')) {
helper = view.getStream(name).value();
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Is it a :trollface: to ask if this should be bound?

@ef4
Copy link
Contributor

ef4 commented Jan 23, 2015

Updates based on discussion with several folks:

  • yielding helpers as block params seems good to go.
  • the helpers themselves should be unbound, if people need bound ones we can add a meta-helper analogous to the component helper.
  • locally scoped helpers in a component's own template (the "layout" case) need more design work, because they complicate the lookup rules and make it easy to confuse people. You can't tell by looking at them where they're coming from (unlike block params provided helpers, which are lexically scoped).

@fivetanley
Copy link
Member

@ef4 @rwjblue What's the status with this? Can somebody help get this merged?

@ef4
Copy link
Contributor

ef4 commented Apr 15, 2015

@fivetanley we've been pretty flat-out on trying to land glimmer. I think this is just waiting in the queue behind that.

@jonathanKingston
Copy link
Contributor

Would it not be more consistent to allow scoped based definitions much like <ul> and <ol> change the meaning of <li> elements.

If a <my-input> can have specialist bindings to components that are/extend from <my-form> then I feel the use cases here are resolved whilst keeping affinity to the HTML spec.

<label> does this automatically and sets the label.control element, it strikes me a declarative syntax would help users setup parameters like this.

@bcardarella
Copy link
Contributor

@ef4 with Glimmer landed, any updates on the status of this PR?

@stefanpenner
Copy link
Member

I'm still advocating for the more explicit

<my-form do |f|>
   <f.input value={{name}}> 
</my-form>

as it does not:

  • collide with outside components/helpers/keywords/properties
  • interleave poorly with other components

@rwjblue
Copy link
Member Author

rwjblue commented May 19, 2015

@stefanpenner - That is precisely what this PR did when proposed (take a peek at the tests and description).

@stefanpenner
Copy link
Member

@stefanpenner - That is precisely what this PR did when proposed (take a peek at the tests and description).

yup, but I believe this still lacks.

  • htmlbars support e.g. angle bracket <f.input
  • f.input being a component not a helper (and currying the factory + attributes)
  • nicer DSL for declaration – i think @ef4 had some ideas here.

the last two being the points I think we need to nail before landing. The first can be added later.

@rwjblue
Copy link
Member Author

rwjblue commented May 19, 2015

@stefanpenner - The example snippet you typed, is very nearly exactly what was implemented here (without angle-brackets).

Example from the description:

{{#form-for as |f|}}
  {{f.input label="Title" value=model.title}}
{{/form-for}}

Example test is here.

@jonathanKingston
Copy link
Contributor

For me the main issue with this is that I don't know any part of HTML shadow DOM or otherwise that matches this setup, which seems to be something that the Ember community have been pushing for with all the native HTML-ish syntax used.
Please note: I like the sytax and it's usage is something I really need in my application to keep it maintainable.

Instead would it make more sense in the declaration of the component to have special meaning when defined within a different component?

Much like a normal input would within a form element have a special meaning.

I guess I am asking more for the use cases here and how they might migrate to native browser features eventually. If it isn't something that belongs in the remit of HTML then surely it should remain within the framework much like link-to and Ember.Controller are separate.

@stefanpenner
Copy link
Member

shadow DOM

AFAIK shadow DOM does not have a corollary. WebComponents to be used only within a shadow root, are global, and pollute the entire realm. We don't believe this to be reasonable, as one component should not pollute the global namespace for its own domain specific sub-components, nor should they need to be names-spaced.

Consider, google-map component.

<google-map>
   <pin loc=...>
   <address loc=...>
</google-map>

In web-components world, pin and address are now global components. No other component can use these names, or a collision will occur.

In theory, we could say magicks occure, and pin and address helpers/components are magically scoped. But that becomes ambiguous when we interleave multiple components. Also, it force a very specific names which can not be adjusted. For example, assuming i do have pin component/helper, i cannot use it within the context of google-map.

Interestingly, If we looked to JavaScript, we would likely express a solution as:

formFor(function(form) {
  form.input({ label: "Title", value: model.title });
});

Looks similar to the Router.map DSL eh?

In HTMLBars, we could express the closure and lexical scoping in an aligned way.

{{#form-for as |f|}}
  {{f.input label="Title" value=model.title}}
{{/form-for}}

benefits:

  • the keyword is choosen by the user – in this case form
  • no additional collisions
  • easily compose multiple such components, just name the arguments accordingly.
  • same lexical scoping rules we are familiar with on the JS side.
  • unlikely to collide with DOM, as it is doubtful the DOM will do something like this... ??

cons:

  • new syntax <f.input>
  • unclear what to name the final components inDOM.

@jayphelps
Copy link
Contributor

Just chiming in to note that the current implementation of Shadow DOM as implemented in Chrome is almost certainly going to vary drastically in certain aspects to the one that gets finally recommended and shipped in all browsers. The vendors are finally back to the drawing board and discussing it. Node distribution e.g. <content select="something"> possibly replaced with "slots" proposal, shadow piercing changes, etc.

That said, there does not seem to be any actionable traction on custom elements scoped to a shadow root (which what I think you are all discussing). There's been mention of it and of some sort of namespacing mechanism (unrelated to XML namespaces) but I would guess there's just too much to solve for the simple cases first before they seriously consider either of these.

tl;dr I don't believe there will be any analogous in the browser for some time and we may in fact be the trailblazers on experimenting with options that will eventually become standards.

@stefanpenner
Copy link
Member

which what I think you are all discussing)

yes

@jonathanKingston
Copy link
Contributor

@jayphelps and @stefanpenner I was mentioning the usage of <f.input as I think there should be an 'air gap' between native looking features and Ember framework features; this is something that puts me off Angular and ColdFusion and so on. I think so far Ember has kept that separation pretty well.
Where Ember is pushing the boundaries so far has never tried to look like native API's and templating.
For example outlet/yield match the slots API somewhat but stay in non HTML syntax which means it can be migrated later.

Certainly namespacing and so on would be great for things like that. However in your example I would expect something like:

<google-map>
   <google-map-pin loc=...>
   <google-map=address loc=...>
</google-map>

Where <google-map-pin loc=...> could have special definable behaviour when within <google-map> this doesn't sound like an immediate advantage but it means:

  • Unified way to define component relations
  • Easier validation or templates

So for example I meant more in the component definition something like:

export default Ember.Component.extend({
  form: Ember.computed.relationship('parent', 'my-form-element'),
  actions: {
    click: function () {
      this.form.submit();
    }
  }
});

This is something I have mentioned to @jayphelps before with that <label and <input have this DOM relationship with their respective properties control, labels and form

Perhaps no one else visualises this separation in the same way.

@jayphelps
Copy link
Contributor

@jonathanKingston busy right now so haven't grok'd your entire reply and this thread yet but I absolutely 100% agree that we can and should do things just like browsers do now with respect to elements having contextual awareness. e.g. <option> tag is only "magical" insight a <select>.

This pattern has been used in an ad hoc way in Ember since the beginning, mostly with people relying on this.get('parentView') or this.nearestOfType(SomeView). It would be nice to have a built-in primitive for this like the Ember.computed.relationship example you show.

@stefanpenner
Copy link
Member

This pattern has been used in an ad hoc way in Ember since the beginning, mostly with people relying on this.get('parentView') or this.nearestOfType(SomeView). It would be nice to have a built-in primitive for this like the Ember.computed.relationship example you show.

It has been ad-hoc do to how brittle it is when the parent tree topology changes, and life-cycle timings as entangling parent/child during render via view traversal is dubious at best. (as we saw with nf-graph, doing registration in various life-cycle hooks)

This relationship could be curried when the parent component passes the child component factory forward.

@stefanpenner
Copy link
Member

This would be really unfortunate, to have apps with hundreds of components some contextual causing my templates to be riddled with my-other-copmonent-foo my-small-helper-bar, my-other-helper-who-only-works-in-my-component-foo.

This really just boils down to, globals are obviously a foot-gun everywhere, but in the DOM they are deemed OK for some reason. ^^ is so far the only proposal that reasonable solves this.

Remember, this is also how react solves it. It does so though, by just using JS's own lexical scoping. This is actually quite a handy compositional primitive. We can – with some effort – apply the pattern to our more constrained DSL. This will allow a nice level composition, with familiar concepts. At the cost of continuing to remain a superset of HTML.

@jayphelps
Copy link
Contributor

Agreed. I'm not against the proposal to have "shadow root scoped style" components. I LOVE the idea. Sorry if my spewing of thoughts implied otherwise. I basically hijacked this thread with some FYIs

Edit: I see what I said that implied it, my bad. I was just saying that you can and should follow the same pattern the browser does NOW. If this feature lands and you decide you do in fact want to use this it, even better!

@stefanpenner
Copy link
Member

Sorry if my spewing of thoughts implied otherwise. I basically hijacked this thread with some FYIs

I hope the DOM comes up with something as-well, because collisions with complex apps + sharing components is going to be crappy.

@jonathanKingston
Copy link
Contributor

I think this is trying to solve both namespaces and scope sharing:

Scope sharing

It has been ad-hoc do to how brittle it is when the parent tree topology changes, and life-cycle timings as entangling parent/child during render via view traversal is dubious at best. (as we saw with nf-graph, doing registration in various life-cycle hooks)

I'm not sure if putting this off because of complications is a good idea. However I'm not sure if I get the rationale to not have properties linking components together.

I get the feeling if this were to become native the syntax would become similar to:

<my-element scope="thing">
  <sub-element for="thing" />
  <sub-element for="thing" />
  <sub-element for="thing" />
</my-element>

Which I see being a little cumbersome for template authors to write to relate elements together when this happens already with DOM properties on element relationships.

Namespaces

I understand the name spacing argument completely (which I am still upset components didn't adopt the XML namespaces which was already implemented)

<company:my-element>
  <company:sub-element />
  <company:sub-element />
  <company:sub-element />
</company:my-element>

Ember syntax

As I noted before my distaste in using HTML-ish syntax for this can we assume the moustache style syntax will be still possible?

Again: I'm not against this change so much just not sure if it's going to last the test of time perhaps.

@stefanpenner
Copy link
Member

I think this is trying to solve both namespaces and scope sharing:

I believe this just tries to solve scoped

@joostdevries
Copy link

@stefanpenner Just read through this thread. Am I correct in summarizing your proposal/feedback as:

  • change this implementation from helpers to components (or both?)
  • support angle brackets?

@bcardarella
Copy link
Contributor

Is there any chance this is going to be part of 2.0? It is a blocker for easy-form 2.0 and I'm don't want to implement a half-way solution until this, or a similar feature, lands. easy-form itself is currently preventing others from upgrading beyond Ember 1.10

@stefanpenner
Copy link
Member

Yes this is strongly related to the contextual components pr. I believe @mmun may be spear heading some of the early htmlbars side work. This may be a good partof that

@bcardarella
Copy link
Contributor

@stefanpenner is there a pr # to follow for that? I don't see any open ones from @mmun

@ef4
Copy link
Contributor

ef4 commented Jun 23, 2015

@bcardarella It's an RFC: emberjs/rfcs#64

@rwjblue
Copy link
Member Author

rwjblue commented Aug 2, 2015

Closing this in favor of tracking on emberjs/rfcs#64.

@rwjblue rwjblue closed this Aug 2, 2015
@rwjblue rwjblue deleted the ember-htmlbars-scoped-helpers branch November 17, 2015 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.