Explicitly specify a component's public API with `publicProperties`

Here’s a spike of something I was discussing with @ryanflorence yesterday:


TL;DR: if you specify an array of publicProperties in your component, then Ember will yell at you if you try to do something like {{x-foo non-public-property="234"}}. This is useful both for catching typos / mismatches in your component’s API and the template that’s rendering the component, as well as encouraging you not to needlessly fatten your component’s public API by forcing you to be explicit about what can be passed in. Again, it’s optional; components without publicProperties will work the same way they have in the past.

What do yall think?

edit: typo

I could be mis-informed but isn’t the Ember components spec informed by the web components spec? If that is the case what risk does Ember take in deviating from that?

Smells a bit of solution-solving. I don’t understand why components would get special treatment in this case over other objects. I’d rather see something like this:

export default EmberObject.extend({
    I should be documenting my public
    API anyway, and here is where that
    documentation goes
  somePublicProperty: undefined

And have a flag in development mode that notifies you when trying to get/set properties that were not specifically added to a prototpye. This seems generally useful, not just useful in the case of components.

@trek seems good; I’m not 100% sure of all the rules for when Ember internals cause v8 et al to go into hash object mode but it’d be a win if this dev flag also prevented a bunch of de-opts (of the object variety) due to late property sets.

@bcardarella the goal is that it should require minimal mental translation to take an Ember.Component and figure out what the WC equivalent is. This proposal doesn’t have anything diametrically opposed to WC; rather just some sugar that might not end up existing in the WC spec, and removing publicProperties in the translation to WC wouldn’t actually cause anything to behave any differently, so I think it’d be fine.

<3 <3 <3 <3 <3 <3 <3

(discourse, you’re a jerk, here is some text you jerk)

It seems like too much boilerplate as a required thing, but fine as an optional param. I would expect distributed opensource components to use it and quick one-off components in your own app to not bother.

1 Like

I’ve changed my mind.

With an html element (which we are mimicking) you can add whatever attribute you want.

I’m a bit late to the party, but I’ve been doing this with a convention in my components by prefixing internal properties with an underscore.

App.CountDownComponent = Ember.Component.extend({
  to: undefined,

  _clock: Ember.computed.alias('clock'), // injected global clock
  _timeRemaining: function(){ ... }.property('to', '_clock.second'),
  _daysRemaining: function(){ ... }.property('_timeRemaining'),
  _hoursRemaining: function(){ ... }.property('_timeRemaining'),
  _minutesRemaining: function(){ ... }.property('_timeRemaining'),
  _secondsRemaining: function(){ ... }.property('_timeRemaining'),

This way, it’s pretty clear (to me anyway) what is public and what is private. It’s pretty hard to ‘accidentally’ bind to an internal property in the template, and if you choose to violate that it’ll stand out pretty obviously in the template. In practice I find this works quite well.

I also briefly experimented with a ‘!’ prefix instead, as in…

App.SomeVeryRudeComponent = Ember.Component.extend({

That way, you can’t actually bind to that property from handlebars, because that’s not a valid ID, if you try your templates won’t even compile and the compiler will yell at you really loud :trollface:

That turned out to be a horrible idea :stuck_out_tongue: Certain things break unexpectedly (who would have thought ;-), and more importantly, there are legitimate cases where you do want to violate that – e.g. in a “combo” component (that provides a default composition of the “bare-bone” sub-components), in the component’s layout, in testing, etc.

1 Like