Wohoo! Congrats! Looking forward to seeing how 2.0 pans out!
The gist of this feedback is that IMHO the 2.0 class-based definition adds stutter to a very clean API, albeit at a few advantages.
I know Polymer 2.0 will have a compatibility layer for the old factory method, but I think the old factory method should be kept as the recommended element creation method. IMHO, it makes it much easier to grok an element's functionality at a glance--and I promise I'm not saying this because I've been staring at those for a while now.
Supporting arguments based on visual guide on migrating.
MyElement
from multiple behaviors will now either require a really long line, or a line that's broken and has to then be closed with many parentheses, whereas before we just used an array of class-like objects.static get is() { return 'my-element'; }
repeats the element's class name and is much longer than just is: 'my-element'
: Keeping the factory-like Polymer() helper would allow the class to be dynamically created, would be shorter, and would not require two versions of the MyElement name.static get config() { return ...}
similar argument to that of is
, except that it also takes observers, but not listeners. The map-style API we had with observers and listeners was really terse and really easy to understand. Removing listeners from here breaks that terseness and adds a bit of complexity.customElements.define
is another piece of boilerplate that seems ripe for extracting into a factory method.In short, I think you could (and should) hide the fact that you're creating a class in a factory method, and keep the clean API we already have. Yes, classes are convenient, but I think in this case, they are getting in the way of keeping the API clean.
I have to disagree, the legacy method for constructing elements is far from what Polymer aims to be: a web component base/library.
How do you define a web component?
class MyElement extends HTMLElement { }
As you can see, Polymer 2.x stays very close to this (extends Polymer.Element
), as it should.
Your arguments against such a class are the reason Polymer 1.x was so far from how a normal web component looks. We shouldn't be aiming to make your code shorter but rather to provide a base element to inherit rather than HTMLElement
. In 2.x, what you see is what gets defined as a custom element, rather than being a configuration object which internally produced such a definition (1.x) hidden away from you.
Yup, you're right... Polymer tries to be a thin layer above on top of the native API, and since the existing API isn't that complicated (extends HTMLElement
), there's no need to complicate things or obfuscate them by adding a layer/factory method. You've convinced me that keeping that base class style is an okay idea, and I concede.
Keeping code short and easy to parse for the human brain should still be at the top of the priorities though. So how about the following amendment to my initial ideas?
is
? It seems to duplicate the MyElement name. Maybe something like this?class Polymer.Element {
static get is() {
return this.name.replace(/\B[A-Z]([A-Z])*/g, '-$&').toLowerCase()
}
}
properties
and observers
part of the metadata/config but not listeners
and hostAttributes
, seems a bit arbitrary and adds to the learning curve. How about either moving properties and observers out into their own static getters, or moving listeners and hostAttributes into the config?The “how do you define a web component?” question, on a more abstract level, is the one that we have been debating/dealing with.
In the simplest, non-extended, component compositions, imagine just having an input element, in a repeater, with a bind to its “type” property, class and value passing properties; i.e:
In this collection pseudo-form pages, what is the element? What is the component? What is the class that we apply? At the “element-level”, the input-element could be a check-box, a number input, a radio-button, a date-time, a search, or even —with a little more sugar(as we implemented)—a searchable, animated input with a animated dropdown menu of search options based on the property being inputted.
What constitutes the component “class” or type of the inner template’s page?
Is it a set of 8 inputs with dropdowns? (A common implementation for us.)
Is it a one page check box form?
What “elements” will be there? Who will know?
In our implementation, we had no idea ahead of time. Whatever prototypes of users, work-schedules, questions, calendars, or events, w/e were being used in the “input interface”, we just sliced up the properties on the proto constructor and--based on data-structure and property names with specific uses of underscores and camel case—automatically created labels, local property names, and baskets of property names to fill value objects handled in our multi-page pseudo-forms.
This model, which was hyper fast, extremely portable, low-overhead, and super re-usable seems beyond, at least our ability, to recreate in new polymer or the v1 spec.
For example, how do we define the class and its attributes without factories, or the ability to talk to child elements prior to load?
How, in any of these instances, do we even begin to answer the question What is the “component”
?
If the above is laid-out as follows:
@jfrazzano , did your comment have a particular point you wanted to get across? I saw a few points here and there, but I'm not sure if there was an overarching concept you were trying to get across.
I just think we should be frank about changes and not gloss over some important technical and legal hurdles that v1 and related standards have created.
Things will be different in a few years but livelihoods depend on a lot of the advice. I believed the response a gloss and felt an example of performance differences would be an easier and less confrontational way of sending out a word of caution to an earnest inquirer.
If I was misguided in the attempt, I apologize.
Sent from my iPhone
On Sep 29, 2016, at 10:13 PM, Bernie Telles [email protected] wrote:
@jfrazzano , did your comment have a particular point you wanted to get across? I saw a few points here and there, but I'm not sure if there was an overarching concept you were trying to get across.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
Is there anything that can be done with Polymer 2.0 syntax but not with 1.0 syntax? I chose to use Polymer because of its ease of use and expressive syntax. If we are moving to a class based and verbose version, I see it's losing one of its advantage. How long will the new versions keep compatibility with 1.0? If the 1.0 syntax will finally deprecate, then we will have to switch to the new syntax from now.
I come at this from a slightly different perspective. For a long time now, the norm has been that whenever you want to do something to a web page you add some javascript to do it.
With web components, and particularly the data binding system introduced with Polymer, this moved from an imperative approach to much much more a declarative approach. The more I use Polymer the more I find this to be a good thing..
This syntax for Polymer 2 seems a small step back towards the use of code rather than declaration to achieve things. What was a static is: 'my-element'
has become a line of codestatic get is() {return 'my-element';}
and similarly for the config. What was a declarative list of listeners now has to be added by code..
It maybe a small step, but it seems to be the wrong way.
I have wondered in the past, since Polymer({is:'my-element'})
is normally declared inside <dom-module id="my-element">
why the is was necessary and couldn't be derived from the context in which it was declared. So whilst I understand the move to subclass the code from a PolymerElement
I wonder if some of the other config stuff could be achieved by declaring it inside <dom-module>
in some way.. I bit like how <fixture>
elements work for testing perhaps?
there's already a proposal for an alternative syntax for that. you can already use it with babel.
class MyElement extends Polymer.Element {
static is = 'my-element'
static config = {
properties: {
foo: {type: String, value: 'bar'},
},
observers: [
'onFooChanged(foo)',
],
}
}
Edit: changed HTMLElement
to Polymer.Element
How about this?
Removes the is
and promotes properties
and observers
to static getters instead of being nested in gonfig. I'm still iffy about the inheritance model. I have lots of components that inherit from 3 or more behaviors, and inheriting from them would be messy with this API.
class MyElement extends HTMLElement {
// unnecessary "is" if it is automatically computed
static properties = {
foo: {
type: String,
value: 'bar'},
}
static observers = [
'onFooChanged(foo)'
]
}
}
@azakus @ebidel @kevinpschaaf any thoughts?
Personally, I think the config
makes it a little clearer what this thing is. Calling it properties
, you occasionally get people trying to access this.properties.<whatever>
.
I think the team is trying to stick to the standard here, so I doubt they'd be willing to recommend class fields until the proposal becomes part of spec. But I agree it looks nicer, and hopefully we'll get there.
It's not immediately clear from the README how inheritance works with the config object. I poked around and it appers that at initialization time the element walks the prototype chain to collect and collate all of the config objects. So that shouldn't be a problem. Unless you're just talking about the actual appearance of:
MyEl extends Three(Two(One(Polymer.Element))) { ... }
... Which I agree can look a little funky. There are probably some ways to sugar that if you don't like the looks.
@arthurevans that would actually be very easy.
Define your base element in a file:
let MyElement = Three(Two(One(Polymer.Element)));
And import this definition into your element definition you can do
class MyCustomElement extends MyElement {}
So instead of importing polymer/polymer-element.html
(as replacement for polymer/polymer.html
in 1.0) you import your own definition.
@btelles I totally agree with your proposal of:
class MyElement extends HTMLElement {
static properties = {
foo: {
type: String,
value: 'bar'
}
},
static observers = [
'onFooChanged(foo)'
]
}
It is concise and elegant.
In the future we could take it even further and use decorators for that, so that this:
import {Element, is, observe, prop, readOnly, notify} from 'Polymer'
@is('string-length-counter')
class StringLengthCounter extends Element {
@prop(String) input = ''
@prop(Number) @readOnly @notify length = 0
@observe('input')
onInputChanged() {
this.length = this.input.length
}
}
...is equivalent to:
class StringLengthCounter extends Polymer.Element {
static get is() { return 'string-length-counter' }
static get config() { return {
properties: {
input: {type: String, value: ''},
length: {type: Number, value: 0, readOnly: true, notifies: true},
},
observers: [
'onInputChanged(input)',
],
}}
onInputChanged() {
this.length = this.input.length
}
}
The code for the simplest decorators on the Polymer side would be just:
function is(name) {
return function (proto) {
proto.is = name
}
}
function observe(...props) {
return function (proto, method) {
proto.observers = proto.observers || []
proto.observers.push(method + '(' + props.join(', ') + ')')
}
}
This should already work with following babel plugins:
babel-plugin-transform-decorators-legacy
babel-plugin-transform-class-properties
Why do we need even need a static is
?
Polymer({})
needed it because it defined the class and registered the custom element in one go.
I can't find any part of polymer that interacts with this.is
before the dom element is fully created. ie: It should be possible for the class portion to infer is
from the tagName
without needing a pre-defined value for it.
Sure it's needed to register a custom element, but the base dom api for that is customElements.define(is, class)
. So I'm fully in favour of patterns like:
class MyElement extends Polymer.Element {
// ... stuff, but no `is` definition ...
}
Polymer.define('my-element', MyElement);
// or
Polymer.define('my-element', class extends Polymer.Element {
});
Some people seem to be complaining about the boilerplate of defining a class _and_ calling a function. But making class definition implicitly register that class somewhere sounds like a horrible abuse of the class syntax. So as long as class Foo
is being used I don't believe there is a reasonable way of eliminating the separate function call boilerplate.
@dantman separate function call is not a Polymer stuff but Custom Elements v1 API:
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements
Makingis
optional will require something other naming convention, won't it?
@web-padawan From everything I understand, at this point is
is pretty much now just a lower case version of the tag name. The is="x-foo"
attribute has even been deprecated. And is
in the context of customElements.define
just tells the DOM what tagName to apply the class to.
So a getter on Polymer.Element
like get is() { return this.tagName.toLowerCase(); }
seems like it would be enough.
@dantman it is most likely used to tie dom-module instances into the element definition. a dom-module with an id="foo-bar"
will be tied up with a get is() { return 'foo-bar'; }
.
As you already said, too, it is used for registration.
Seems the best you can do is try figure out the element name from the class name, but not sure how reliable that would be and how difficult it is to get hold of the class name (From inside the base class).
@43081j But that only matters if the dom-module reference needs to be done before document.createElement('foo-bar')
goes and creates an instance of your FooBar
class with tagName
set on it.
I did a search for usage of is
within the codebase and I can't find anything to suggest that polymer needs to know the value of is
early on.
And registration is separate, that is part of customElement.define
which is not going to go away; there's no reason that the class needs to statically know what is being passed as the custom element name.
Without is
, you don't know the tag name just by knowing the class (you can only guess it by convention... MyElement -> my-element).
@dantman as per README.md this is what I was referring to:
Implement a static template getter to provide a template to stamp inside the element. By default the first found in a
with an id matching the element's is property is used.
@43081j Sure, in the current unfinished API template()
is static, but the finalization process that uses it is only triggered once the non-static _initializeProperties()
when a document.createElement('foo-bar')
triggers it.
There's no reason the class finalization has to be done statically. template()
could just as easily be part of the prototype with a per-is cache.
ah now i see what you're saying, you think we only need it after creating an instance (so we just this.tagName
). if you're completely certain it is only ever used _after_ instantiating, it could be there for legacy reasons possibly.
I'm not sure beyond that without reading through the source.
We've landed on what we intend to be the final Polymer 2.x class-based syntax based on much of the feedback here.
A major benefit of moving to ES6 class based syntax is standard JavaScript-based support for subclassing and super
calls; that said, native ES6 classes have restrictions that make the more declarative "property"-baed approach not possible (without some of the speculative transpilation described above), hence the need for getters. We think this is a reasonable tradeoff.
Please see the diffs in the README here, which explain a lot of the questions regarding why is
is required, etc. https://github.com/Polymer/polymer/pull/4332/commits/e2118aa4d7f81dfed2d5c1e96903d6c0bdacdce0?short_path=04c6e90#diff-04c6e90faac2675aa89e2176d2eec7d8
In general, there is a retraction in concept from a state aware, component-based, layered build, that encourages extensibility and a higher degree of mpolymorphism/app like browser like behavior l in the 1.0 spec and related tech
These limits are unfortunate and while many of the limits can be patched or glossed in numerous ways, the highest end performance, as well as international wai aria legal standards for authors and vendors present an unfortunate, and often debilitating development, legal, and contract garnering development path. These issues cannot be glossed
And, as a result, Some Larger development entities are shifting toward shard doms for a variety of the above stated reasons: no liability, better performance, polymorphism and just avoidance of an altogether unfortunate situation.
My point, which I preferred to imply rather than state, was the same point made at this year's io, a point far too often NOT PRESSED by advocates:
We are all big boys and girls here; we engage in large scale private and public business and even the most novice of developers can be the proverbial nail that "lost a kingdom" but for a...
In these dynamic often turbulent times, it would seem
comfort or a confidence game is the last reason any one of us is here.
We are here it's to build "awesome", to participate in ventures that can and will change the world.
The start, and I believe only respectful and reasonable way to pursue such "do no harm and do some awesome" set of goals is to be straight up with one another about the shifts in tech.
Refrain from any whining or complaint about change. Change is good. (For example, I think it's awesome, we got a chance to really invest in string literals, template strings, iterative binding, fragmented doms assembled with partial char codes....)
Native has so much power and speed, if one is willing to handle the dev responsibility.
It's not for everyone. But it's also not something a "use the platform" motto would seem to encourage.
In the thread, I saw someone asking an earnest question, and I didn't believe it fair to recast that earnest question, explained via simple example, and gloss the over-arching issues above(speed, next Dom shift, legitimate contracting issues). This is a livelihood for many. Futures depend on clear open discourse. I believed someone was getting a "gloss" and real world more complex but still simple examples could illustrate the danger I perceived
I could be way off here. It's not my place perhaps to speak. And it's not the nineties anymore, when I was young and q comment like "did u have a point" would have been just well silly To me. Now it actually matters. What we do here matters
We can share and build an as of yet I unimaginable future.
With change world as option one, other choices just seem lame
And, to me at least, being frank with one another seems just what the whole experience and community work is all about
I didn't think the advice was frank
I offered a counter example
That was the point.
You cool now with the comment? I already regret even thinking of sending this, but I just don't believe we can all do our best here if we can't be frank, honest and even sometimes a little surly.
I hope you took no offense and if I am totally off base here. Let's share code. You know a lot more than I do and I am always eager to learn and help.
Sincerely,
Jason
Sent from my iPhone
On Sep 29, 2016, at 10:13 PM, Bernie Telles notifications@github.com wrote:
@jfrazzano , did your comment have a particular point you wanted to get across? I saw a few points here and there, but I'm not sure if there was an overarching concept you were trying to get across.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
Sent from my iPhone
On Sep 29, 2016, at 10:13 PM, Bernie Telles notifications@github.com wrote:
@jfrazzano , did your comment have a particular point you wanted to get across? I saw a few points here and there, but I'm not sure if there was an overarching concept you were trying to get across.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
Most helpful comment
I have to disagree, the legacy method for constructing elements is far from what Polymer aims to be: a web component base/library.
How do you define a web component?
As you can see, Polymer 2.x stays very close to this (
extends Polymer.Element
), as it should.Your arguments against such a class are the reason Polymer 1.x was so far from how a normal web component looks. We shouldn't be aiming to make your code shorter but rather to provide a base element to inherit rather than
HTMLElement
. In 2.x, what you see is what gets defined as a custom element, rather than being a configuration object which internally produced such a definition (1.x) hidden away from you.