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

Implement {{component}} helper #10093

Merged
merged 1 commit into from Jan 3, 2015
Merged

Conversation

lukemelia
Copy link
Member

This PR implements the {{component}} helper, which allows passing of a param that is resolved and used to look up the component. Leveraging the first feature described above, it also automatically will swap the component for another when the bound value changes.

See #5007 for more discussion and background.

What's left:

  • first pass of implementation
  • collect code reviews and apply feedback
  • create {{component}} helper docs
  • basic test coverage
  • more code reviews and apply feedback
  • more test coverage, especially around actions
  • more code review and apply feedback?

@lukemelia
Copy link
Member Author

@mmun @krisselden @mixonic would love a review and feedback

this.notify();
},
destroy: function(){
if (this._super$destroy()) {
Copy link
Member

Choose a reason for hiding this comment

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

this._super$destroy doesn't seem to be defined?

@igorT
Copy link
Member

igorT commented Dec 31, 2014

Might be nice to warn if 'ember-htmlbars-component-helper' is defined but 'ember-htmlbars-binding-aware-view-helper' is not


var self = this;

this._boundViewOptions.viewStream.subscribe(function() {
Copy link
Member

Choose a reason for hiding this comment

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

You seem to using a different style for subscribing here compared to component.js. Any reason we can't do
this._boundViewOptions.viewStream.subscribe(this._updateBoundChildView, this) ?

@mixonic
Copy link
Sponsor Member

mixonic commented Dec 31, 2014

Excited to see this feature get fleshed out :-) 👍 will review more.

@igorT
Copy link
Member

igorT commented Dec 31, 2014

Seems that using a ContainerView would mess with views that care about their parentView, though those are mostly components. If I am reading the PR correctly, using the component helper wouldn't create an intermediate component right?

@tomdale
Copy link
Member

tomdale commented Dec 31, 2014

Given that we will likely remove {{view}} or extract it into a plugin in 2.0, I'm 👎 on adding new functionality to it. {{component}} helper seems cool though.

"The `component` helper expects exactly one argument, plus name/property values.",
params.length === 1
);
var container = this.container || this._keywords.view.value().container;
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Again I'd suggest read instead of accessing value()

@mixonic
Copy link
Sponsor Member

mixonic commented Dec 31, 2014

Agreed w/ @tomdale re: new view functionality.

import { read, isStream } from "ember-metal/streams/utils";
import EmberError from "ember-metal/error";

function ComponentLookupStream(source, componentLookup, container) {
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I'd like to suggest you explore chainStream for this logic instead of making a custom stream. chainStream, like read is non-stream safe.

Values get into the chain via scope instead of an object instance.

var component = chainStream(source, function(){
  var componentParam = read(source);
  return componentLookup.lookupFactory(componentParam, container);
});
// component is maybe a component, maybe a stream of component values

Because both the utils are non-stream safe, you don't even need to branch the initial logic based on if this is a stream.

I think chainSteam could greatly reduce the amount of code required to implement this feature.

@mmun also mentions that lookupComponent could possibly return a stream itself, instead of wrapping it.

@workmanw
Copy link

@tomdale It would be a shame to loose {{view}}. I know people are generally encouraged to use {{component}}, but we frequently have tiny little one off view classes that live on the parentView. They're not meant to ever be reused and have a tightly coupled relationship with the parent view, they exist for one purpose. We could make them components, but our list of components would be a lot longer and less organized.

Just my $0.02.

@igorT
Copy link
Member

igorT commented Dec 31, 2014

Components now support / in names so you can organize them much easier.

@mmun
Copy link
Member

mmun commented Dec 31, 2014

@workmanw We will support coupled components without needing the {{view}} helper.

@@ -199,17 +205,23 @@ export function viewHelper(params, hash, options, env) {
viewClassOrInstance = View;
}
} else {
viewClassOrInstance = readViewFactory(params[0], container);
var viewParam = params[0];
if (Ember.FEATURES.isEnabled('ember-htmlbars-binding-aware-view-helper') && isStream(viewParam)) {
Copy link
Member

Choose a reason for hiding this comment

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

Feature flag checks have to be by themselves for them to be properly stripped by defeatureify. This would have to be rewritten as:

if (Ember.FEATURES.isEnabled('ember-htmlbars-binding-aware-view-helper')) {
  if (isStream(viewParam)) {
    /* stuff */
  }
}

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Thanks for catching this! I was thinking it was busted.

@lukemelia
Copy link
Member Author

Thanks for all the great feedback. I'm laid up with a cold today, but look forward to taking another pass.

@workmanw
Copy link

workmanw commented Jan 1, 2015

@mmun I'm sorry, I don't understand what you mean. Are you saying, {{component boundComponentClass}} would be supported? If not, would you mind elaborating?

@rwjblue
Copy link
Member

rwjblue commented Jan 1, 2015

@workmanw - Yes, {{component someBoundProp}} should absolutely work (and is what this PR is all about).

@workmanw
Copy link

workmanw commented Jan 1, 2015

@rwjblue Awesome. Thanks a lot. It wasn't clear to me if "someBoundProp" could be a class object as well as a string name.

@rwjblue
Copy link
Member

rwjblue commented Jan 1, 2015

Should be a string name to be looked up in the container.

@mmun
Copy link
Member

mmun commented Jan 1, 2015

@workmanw I mean that we won't deprecate the view helper until there are well defined upgrade paths. Localized / scoped components is something we've been thinking about for a while but nothing concrete yet. Expect an RFC before EmberConf.

@lukemelia lukemelia changed the title Add bindable view support to {{view}} helper and implement {{component}} helper Implement {{component}} helper Jan 1, 2015
@lukemelia
Copy link
Member Author

@igorT @mmun @mixonic @rwjblue @tomdale Just pushed a second pass, removing the new view feature and applying the excellent feedback. A few questions:

  1. lookupHelper is used in the base of typical component rendering, and it delegates to the view helper. (https://github.com/emberjs/ember.js/blob/master/packages/ember-htmlbars/lib/system/lookup-helper.js#L51). I have maintained that pattern for the {{component}} helper, but am open to another approach if that is not desirable.
  2. The intermediate BoundViewView that is being created in this PR's current incarnation is a ContainerView subclass. Is this acceptable, or should this be a component? I'm not sure of the pros and cons to each approach.

import EmberError from "ember-metal/error";

/**
`{{component}}` is a `Ember.Handlebars` helper for adding instances of
Copy link
Member

Choose a reason for hiding this comment

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

Ember.HTMLBars or "template" helper? Once 1.10 lands Ember.Handlebars is for backwards compat only (and Handlebars itself isn't actually used/needed)...

@mmun
Copy link
Member

mmun commented Jan 2, 2015

@lukemelia great work. I plan to do a thorough review soon.

@tomdale
Copy link
Member

tomdale commented Jan 2, 2015

We discussed this at the weekly meeting and reached consensus that this is good to merge once @mmun finishes his review.

@module ember
@submodule ember-htmlbars
*/
import Ember from "ember-metal/core"; // Ember.warn, Ember.assert
Copy link
Member

Choose a reason for hiding this comment

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

maybe flagged aswell so we don't pull in the extra bytes

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't follow. Do you have an example of this, @stefanpenner?

….resolving.to.component.name}}` to dynamically update the component when the bound value changes.
mmun added a commit that referenced this pull request Jan 3, 2015
@mmun mmun merged commit eff1014 into emberjs:master Jan 3, 2015
@tobyzerner
Copy link

Couple of questions/thoughts:

  • Does the helper work with a component class object or instance, like the view helper did?
  • Does it mean that we can easily use components named without a hyphen? e.g. {{component "button"}}

@stefanpenner
Copy link
Member

Does the helper work with a component class object or instance, like the view helper did?

both

Does it mean that we can easily use components named without a hyphen? e.g. {{component "button"}}

yes

@mmun
Copy link
Member

mmun commented Jan 3, 2015

@stefanpenner We should enforce component lookup via strings and disallow classes and instances, no? I don't want people writing inline component classes inside of their components until the story has been fleshed out.

@bcardarella
Copy link
Contributor

Perhaps I am missing the obvious but what advantage does this have over:

{{#if isMarketOpen}}
  {{live-updating-chart}}
{{else}}
  {{market-close-summary}}
{{/if}}

Functionally it appears this implementation does exactly the same but adds additional code to Ember.

@twokul
Copy link
Member

twokul commented Jan 3, 2015

@bcardarella if I'm not mistaken, this will allow to pass a component name to the helper dynamically. So you will avoid the need to have if statements in your template. Say something like:

{{select content=chartTypes selection=chartType}}
{{component chartType}}

where there's a select with different chart types and as soon as you select a different type, a new component is rendered (i.e. the selection is barChart, bar-chart-component will be rendered, etc.)

@rwjblue
Copy link
Member

rwjblue commented Jan 3, 2015

@bcardarella - there are many cases where the actual component name to use is determined at runtime (like from a model property). This was not previously possible out of the box for components.

@lukemelia
Copy link
Member Author

Does the helper work with a component class object or instance, like the view helper did?

@tobscure @stefanpenner @mmun Test coverage only covers name lookups, and IIRC, component class references are not supported per discussions during the issue/PR process. It would be trivial to support class references, but my understanding from @mmun is that we want to come up with another approach to support those use cases.

@lukemelia lukemelia deleted the helper-binding-support branch January 3, 2015 19:43
@rwjblue
Copy link
Member

rwjblue commented Jan 3, 2015

It would be trivial to support class references, but my understanding from @mmun is that we want to come up with another approach to support those use cases.

Confirm, we only want to support strings to be looked up in the container (not component instances or classes) at this point. As you said, it would be possible to add support for instances and classes, but we definitely do not want to start with so much extra API surface.

@rwjblue
Copy link
Member

rwjblue commented Jan 11, 2015

As of #10193 this is enabled by default on canary (and slated for 1.11 barring any issues).

@shinzui
Copy link

shinzui commented Mar 5, 2015

How are you supposed to pass data to your component while using this helper?

@lukemelia
Copy link
Member Author

@shinzui name value pairs, just like on your components, see tests for an example (https://github.com/emberjs/ember.js/pull/10093/files#diff-de0c559d2c25a9811bd9876f63269098R36)

@shinzui
Copy link

shinzui commented Mar 5, 2015

@lukemelia great, thanks. I asked based on reading the code, i should have tried it.

hlm pushed a commit to hlm/sl-ember-components that referenced this pull request May 19, 2015
@sandstrom
Copy link
Contributor

Are there plans to enhance this to allow passing component classes? The discussion above mentions this functionality, but I don't see it in the commit.

It would be useful for 'private' components, that are only used within a parent component (which they are coupled with).

@ef4
Copy link
Contributor

ef4 commented Jun 16, 2015

@sandstrom see emberjs/rfcs#64

I think that is the solution for private child components.

@sandstrom
Copy link
Contributor

@ef4 thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet