MeteorCommunity / discussions

Track technical discussions in the Meteor community
89 stars 7 forks source link

Meteor Web Component UI / Polymer #5

Open grigio opened 9 years ago

grigio commented 9 years ago

I don't know if this is the right place to discuss about it, but I think "high level isolated components" should be easier to integrate in Meteor.

core topics:

Some links

raix commented 9 years ago

I got tired of google groups - its completely impossible to keep track of ideas and feature requests. Here at least we have a change to track and discuss things that can be referenced on github. I'm not closing ideas here, and if things get enough traction/votes we have a better chance to influence @meteor

raix commented 9 years ago

I'm not sure about polymer it self - but we need a pattern for creating reusable isolated components for blaze/meteor. I've played with it briefly at https://github.com/MeteorWidgets - having reusable components/widgets in general is important.

A feature in here could be a way to tag packages as "ui-components", making it easier to finde when searching.

I did ask @avital about this at Meteor day, but there was not eta on this. It could be a combination that they are short on time and they haven't found a good pattern yet.

dandv commented 9 years ago

A clearly recommended and well-integrated uniformly-styled modern set of widgets is what Meteor lacks most, towards rapid web app prototyping, I think. See What Meteor Lacks at the end of Why Meteor.

Something like Dijit or Kendo or Polymer/Paper would be great, but apparently it's not easy to integrate grids and other complex data components, and Bootstrap only has a few widgets.

aldeed commented 9 years ago

Currently looking into this with regard to having autoform natively support web components. No ETA, and probably won't be too soon.

marcodejongh commented 9 years ago

On one of the projects I am working on we tried to make re-useable components with spacebars. Actually pretty easy to do for simple stuff like buttons. But its not very suitable for anything more complicated.

It ends up not being totally un-readable. For example if we make a basic step wizard the use of step control component ends up looking like this:

    {{#_StepControl}}
        {{#assignTemplate obj=this prop='backButtonCaption'}}Back{{/assignTemplate}}
        {{#assignTemplate obj=this prop='nextButtonCaption'}}Next{{/assignTemplate}}
        {{#assignTemplate obj=this prop='finishButtonCaption'}}Finish{{/assignTemplate}}
        {{#_addItem}}
            {{assignValues id="stepEmailType" active=true}}
            {{#assignTemplate obj=this prop='indexCaption'}}1{{/assignTemplate}}
            {{#_setCaption}}Step 1{{/_setCaption}}
            {{#_setContent}}{{> step1}}{{/_setContent}}
        {{/_addItem}}
        {{#_addItem}}
            {{assignValues id="stepConfigure"}}
            {{#assignTemplate obj=this prop='indexCaption'}}2{{/assignTemplate}}
            {{#_setCaption}}Step 2{{/_setCaption}}
            {{#_setContent}}{{> step2}} {{/_setContent}}
        {{/_addItem}}
        {{#_addItem}}
            {{assignValues id="stepPreview"}}
            {{#assignTemplate obj=this prop='indexCaption'}}3{{/assignTemplate}}
            {{#_setCaption}}Preview{{/_setCaption}}
            {{#_setContent}}{{> Step3 }} {{/_setContent}}
        {{/_addItem}}
        {{assignValues finalizer=dostuffwithdata}}
    {{/_StepControl}}

All the assign helpers actually add extra data to the context of the template its how you configure the template.

awatson1978 commented 9 years ago

I had the same experience as @marcodejongh, which is why I've never been a real big fan of adding javascript parsing into the spacebar helpers. It mixes the responsibilities of files too much, and breaks the browsers HTML/JS/CSS division of responsibilities with regard to MVC. Same reason that inline <script> tags are an antipattern, imho.

That being said, the Blaze API really does provide a lot of the functionality needed to implement web components. Instead of using spacebar helpers, it just requires taking the time to actually use Blaze.getData, Blaze.toHTML, and Blaze.toHTMLWithData.

Also, I've been thinking that Blaze.View should be refactored to Blaze.Component or Blaze.Partial. It's like Deps, in that it's focused on core-development and not app-development. And in the same way that Deps got a lot of functionality into place, but eventually needed to be refactored to Tracker to be more app-developer centric, Blaze.View got a lot of functionality into place but isn't exactly what web browsers think of as what gets rendered into a ViewPort. It's wonky, and it seems to me that it might be better off as Blaze.Component or Blaze.Partial.

marcodejongh commented 9 years ago

@awatson1978 Do you have any examples you can share where you implement web components using the methods you mentioned?

grigio commented 9 years ago

In my opinion a way to have components is to have templates that you render when you need them (like https://github.com/grigio/meteor-overlay ) but it is very meteor specific. You should be also be able to attach a datasource to them, imagine a color-picker which may be reused to pick a color for different "color fields"

Space ui adds complexity but also a clear isolation https://github.com/CodeAdventure/space-ui

awatson1978 commented 9 years ago

@marcodejongh - Hmm, convoluted question. Lets see...

Here are links from a Card UI presentation given at the NY Meteor Meetup that's not strictly Meteor focused, but focused on Web Component integration.

I'm not 100% convinced Oasis and Conductor are where to start, as they're focused on sandboxed iFrames and HTML5 Web Messaging. It would be better to implement Web Components, then apply Oasis and Conductor. But they give some good research on what the end-goal might be.

However, having sat through those presentations and done a bit of research, I do think that webcomponent.js is maybe were we want to start.

And it looks like Ben Green has released the numtel:webcomponent package which wraps webcomponent.js.

Looking through the package, it's well documented, the MVC model seems consistent with browser standards, and Ben seems to know exactly what he's doing with this package. So, I'd recommend that everybody star that package, and rally around his efforts.

Once the polyfills are in place, then the question is one of packaging things up.

My first attempt at packaging up a component was a reactive-overlay package, similar to @grigio's meteor-overlay package. Mine is klunkier than his, and not as reusable. But it might be worth comparing/contrasting. It didn't work too well, and I wound up abandoning it for the most part.

The closest I've gotten to properly packaging things up since then is with maybe the clinical-ui-sidebar package. It doesn't even have any included HTML templating. But you can see it in action in the clinical-trials demo in the following two files:

https://github.com/awatson1978/clinical-trials/blob/master/client/app/app.layout.html https://github.com/awatson1978/clinical-trials/blob/master/client/app/sidebar/westPanel.html

The westPanel.html could be moved into the clinical-ui-sidebars package, in theory. But there are a couple of problems in practice.

  1. The sidebar has some design assumptions that won't be consistent between apps. It assumes that there's a navbar header, and adds 50px of padding at the top. It assumes that it will underlay the navbar, rather than overlay it. And so forth.

    Within the clinical-track, it's okay to make such design assumptions, because we can sort of say 'well, if it's going to use the ClinicalFramework, it's simply going to have a certain look-and-feel'. But that only goes so far. More generally, there will probably need to be some sort of configuration option for a component.

    In the case of sidebars, it might be something like

    {
    backgroundColor: Color,
    hasHeaderPadding: Boolean,
    hasLogo: Boolean,
    logoFile: String
    }

    That's not too horrible; even if somewhat tedious. And I wonder about using a package configuration pattern, like Nemo64 has done with meteor-bootstrap.

    EDIT: Looking through @grigio's meteor-overlay package, I think he has a very valid pattern for configuring the component as a JSON object applied in a javascript function. Ideally, we would have both, maybe?

  2. People are going to want custom list items. How do we go about injecting data into the HTML? That's the part that I'm hung up on, and where Blaze.toHTMLWithData seems like it might be the answer. But it returns as a string and isn't reactive, apparently. Which makes me question if it's really the solution.

    Maybe the solution is simply sub-templates and defining collection APIs in the documentation. Maybe clinical:ui-sidebars should subscribe to a UISidebars or _UISidebars collection.

Anyhow, I'm going to look into adding numtel:webcomponent as a dependency, moving the westPanel.html file into the clinical:ui-sidebars package, and seeing what kind of resulting webcomponent might be made by putting those pieces together.

Also, I've been leaning towards using SemanticUI for Web Components, and moving away from Bootstrap-3. Jack Lukic has attended some of the NY Meteor Meetups, and his MVC approach seems to align better with WebComponents and general browser architecture than jQuery/Bootstrap.

awatson1978 commented 9 years ago

Digging through the cookbook, I came up with maybe an even better example:

dropzone dropzone-ui demo

Dropzone comes with a complete HTML component, is very close to having an object oriented API in the spacebar helper, attaches the Dropzone object on the window, and provides a javascript API to manipulate the component.

grigio commented 9 years ago

Here is a test https://github.com/grigio/meteor-component-test/blob/master/example.js using the component API of @stubailo

I like it, is possible to have easly components with shared or standalone data sources.

yubozhao commented 9 years ago

Evented Mind has a iron component that is pretty easy to use, I use it for Form instead of using autoform

https://github.com/EventedMind/iron-component

dandv commented 9 years ago

@awatson1978

People are going to want custom list items. How do we go about injecting data into the HTML?

I'm working on making Sortable reactive for reorderable drag&drop lists. The way I let the user inject data is with a template.html in the package:

<template name="sortable">
    {{#each items}}
        {{> Template.contentBlock this}}
    {{/each}}
    {{setup}}
</template>
{{#sortable items=players options=myOptions}}

Did you mean something different?

awatson1978 commented 9 years ago

That's very close to what I had in mind, actually. How is {{> Template.contentBlock this}} different than simply calling {{> contentBlock}}?

Also, it would be interesting to be able to do Template.sortable.registerElement('sortable'); and reference the component in HTML:

<!--  items would then need to be wired up to a reactive data source somehow -->
<sortable></sortable>

<!-- maybe add an id? -->
<sortable id="foo"></sortable>

<!-- or how about adding a bunch of attributes? -->
<sortable id="foo" items="players" options="myOptions"></sortable>
grigio commented 9 years ago

@dandv it seems quite clean an reusable. But I'm still confused how to share the "reactive data source" or the events between the component and the main app

@awatson1978 if you use <sortable></sortable> isn't harder to distinguish "Meteor Components" in templates from just "Custom html tags"?

Main app

  <div id="players_wrapper">
    <p>Some custom player notes</p>
    {{#sortable items=players}}
      <li>{{name}}-{{rank}}</li>
    {{/sortable}}
  </div>

  <div id="generic_wrapper">
    <p>Generic</p>
    {{#sortable items=items}}
      <li>{{name}}-{{rank}}</li>
    {{/sortable}}
  </div>

Components

<template name="sortable">
    <ul>
    {{#each items}}
        {{> Template.contentBlock this}}
    {{/each}}
    </ul>
</template>
  Component.define(Template.sortable, {
    rendered: function () {
      var el = Template.instance().firstNode;
      var sortable = Sortable.create(el);
    },
    helpers: {
    },
    events: {
      // Should be nice to catch the UI update here
      // and update the generic collection from here
    }
  });
awatson1978 commented 9 years ago

@grigio... custom HTML tags are part of the W3C standard for webcomponents. Meteor doesn't get to simply make up it's own definition of what a webcomponent is.

Some background reference material:

I guess the question is: are we discussing WebComponents or are discussing some new-fangled "Meteor WebComponents"? I lean towards the former.

grigio commented 9 years ago

@awatson1978 I lean towards "Meteor Components" and then Web Components (/ Polymer), so a way to compose components which can emit events or share a reactive data source with the parent components (with existing technologies)

W3C Web Components are standards but:

I think the "Meteor Components" issue should be fixed before "Web Components integration", but some topics are common and can be fixed on both sides

awatson1978 commented 9 years ago

Hmm. It seems that we agree that the 'Meteor Components' implementation needs to be figured out before 'Web Components' can be implemented.

However, I'm not sure that there's any real difference between the shadow DOM specified in the WebComponents spec and what MDG has implemented within the Blaze virtual DOM. To turn it around, if you wanted an extremely detailed low-level step-by-step description of how Blaze works, and how it's specifically able to work it's magic within web browsers, just reference the Shadow DOM spec:

http://w3c.github.io/webcomponents/spec/shadow/

The trick to remember is that web standards work two ways. It's not enough that MDG create awesome libraries. The browser developers have to commit to creating environments that guarantee those awesome libraries will work.

I guess what I'm trying to say is that Blaze is the Meteor WebComponent API. Or the closest thing to it. It's just named wonky. Like Deps was a klunky name, and got renamed to Tracker. Try to think of Blaze with other API names. (I've never been a fan of the Blaze.View naming convention, and think that it missed the mark precisely for these kinds of reasons. )

Blaze.renderFromShadowDOM
Blaze.renderWithData
Blaze.remove
Blaze.Shadow
Blaze.currentShadowDom
Blaze.getShadowDom
Blaze.With
Blaze.If

Similarly, I'm not entirely convinced that there's any truly substantive difference between how W3C defines <template> and how Meteor uses it. In theory, someone could simply register a template element to get past the builder, and proceed to use the Template API exactly as the W3C expects:

Template.foo.registerElement('template');

And polyfills are obviously in place to patch browsers until they get native support. So, yeah. I dunno. I guess I just don't see there being substantive differences. I think it's mostly all in place.

awatson1978 commented 9 years ago

Just published a package using numtel:webcomponent to wrap a basic meteor component of sorts.

https://github.com/awatson1978/clinical-ui-alert-panel

The shadow DOM implementation that numtel:webcomponent provides will create a #shadow-root element, which expands to the meteor component. The meteor component is reactive, and will update the shadow DOM without any problem. So, that all seems to work very nicely.

The only catch is that it doesn't seem that styling is getting applied to the shadow DOM.

raix commented 9 years ago

I really like the paper / material (and the svg morpheus is a nice add on), I'm not that thrilled about Polymer, but it would be nice to benefit of the result. (I like the small animation details)

Core wants server-side rendering: Ref to core about this

Meteor roadmap ref: New object oriented api for ui components Template and helper namespacing Server side rendering

I want to be able to add check buttons etc. and not have to worry about animations/layout etc. (maybe colors)

I'Why arent we using svg animations a bit more, The ui could be pure svg?

awatson1978 commented 9 years ago

Well, Differential appears to have ditched Famo.us for Polymer?
https://www.youtube.com/watch?v=DsATeVvMIm4

Looks like we're generally on the the right track in this discussion though. Presentation had #shadow-root elements and all of it.

grigio commented 9 years ago

I had the time to test a bit better the Meteor + Polymer integration and personally I think we could close this issue :)

Here another example http://meteor-polymer-example.meteor.com and some more considerations

marcodejongh commented 9 years ago

@grigio I've been experimenting with it a little more and I must say I do end up making custom components a lot.

For example a data table solution as provided for normal applications will never work for meteor. So either I havent run into a way of making polymer listen to a reactive data property. OR I'm gonna end up recreating a lot of that kind of stuff.

grigio commented 9 years ago

@marcodejongh I agree, it depends what you mean with "component". A "reactive data table" doesn't seem something you can do in a Polymer component. But it should be a "Meteor Component" (not bind to a particular collection) which emits data changes via a ReactiveDict or some other reactive style ways (to be reusable in different contexts)

marcodejongh commented 9 years ago

@grigio Actually how I solve the reactive data table problem is by making the table rows polymer elements as well. I got it working in my proof of concept for cards But now I need to figure out how to do this with tables that can have a x amount of columns.

I let meteor handle the data, polymer just handles the visualisation. I know it can be done I just need to better understand the Polymer API.

grigio commented 9 years ago

@marcodejongh I did the same to display a list of Polymer items, but If you have a big list or table of elements probably is more efficient to use just one #shadow-root for a Polymer grid component like http://liuwenchao.github.io/aha-table/ and emit events that Meteor can manage

marcodejongh commented 9 years ago

@grigio Yes but how are you gonna make meteor reactively emit events that the aha-table can use? The tables I tried didn't pick it up if meteor reactively changed the data given to the element.

Maybe we should write a wiki/guide on best-practices using polymer components.

I figured out how to put my own custom components in a package here Also created a better polymer meteor wrapper package here

grigio commented 9 years ago

@marcodejongh in my opinion:

I like very much the combo Meteor + Polymer, but I still have some issues before to extract best pratices :)

marcodejongh commented 9 years ago

@ryw Care to weigh in in the discussion? Maybe share some insights from your experiences?

marcodejongh commented 9 years ago

@grigio I guess that makes sense the only problem I have with it is that I would like to use the component directly in my feature (meteor)template.

So I would actually much rather find a way of reactively updating the data of the polymer element instead of wrapping it. Gonna read up on the databinding options polymer provides see if theres anything I can (ab)use for this.

Ideally I would prefer my meteor templates to look like this:

<template name="someForm>
    <awesome-polymer-table data="{{miniMongoCursor}}">
           <column>id</column>
           <column>name</column>
    </awesome-polymer-table>
</template>
ryw commented 9 years ago

@marcodejongh we finally extracted some sample code https://github.com/Differential/polymer-demo

I didn't even realize that this lively discussion was going on over here — been pretty heads down lately trying to get http://usercycle.com MVP built. Great stuff though.

I've been encouraging MDG to try to fit W3C WebComponents into their vision for "Meteor components" because I really think WebComponents represents a potentially revolutionary leap in web application architecture — however it's still early and perhaps they don't end up "winning."

Integrating w/ Meteor has been painless, but we're just lightly using WebComponents so far (somewhat intentionally) because these are real products we're building that need to work :)

Happy to answer any other questions and share more code or explain what we're doing in the demo app.

grigio commented 9 years ago

@ryw Thanks for sharing. Do you think is possibile to extract Meteor-Polymer component in packages ? At least for custom components?

I think it could be nice that Meteor components could inject rows like this in imports.html

<link rel="import" href="/components/di-form/di-form.html">
<link rel="import" href="/components/di-button/di-button.html">
rgoomar commented 9 years ago

@grigio I don't think you could extract it from a package. It is a similar problem to the way CSS preprocessor files (like LESS and SASS) are handled for packages at this moment.

The way that @marcodejongh implemented it in packages works. But, after a certain amount of files, it may be importing too much. I mean, that was the reason behind using Vulcanize to concatenate the imports. But, on a small-scale application, it should work perfectly fine.

I'm excited to see what MDG does with Meteor Components.

ryw commented 9 years ago

Yeah I think it's possible, you can see that we're actually inserting rows in the head tag during the Meteor build process here: https://github.com/Differential/meteor-vulcanize/blob/master/vulcanize.js

Tricky part in my mind is how to encapsulate real Meteor functionality inside of a Web Component — @schnie and I have been talking about how there is an ajax component in Polymer, maybe there is a similar package that implements DDP — sort of a "Meteor Lite" library that can interact with a Meteor backend. But it was late, our thoughts were fuzzy, and we haven't talked about it since then :)

We've only worked so far on putting Web Components into our Meteor, not Meteor into our Web Components. I like to think of this stuff through a specific use case, and here's our first idea...

See https://usercycle.com/project/ekxMvhGEGWwWbHi8x?interval=day&range=quarter — we were discussing how cool it'd be if we could provide a <usercycleChart> tag that would allow our users to embed our charts in their own dashboard, and for that the component would have to talk to our servers via ajax or more ideally DDP, and DDP would be cool :) That's all the thinking we've done on it so far, but excited for the prospect.

marcodejongh commented 9 years ago

@ryw Think the most awesome way to do this is to use the interface of the core-ajax element.

Then turn it's inner workings into a DDP client maybe using asteroid. That way you can grab any polymer element in existence that supports ajax and use it without having to change the module. You just have to import the meteor-core-ajax element instead of the default version

Of course a actual DDP datatransport would be better. But from what I can see none of the elements support defining a custom datatransport. And I much rather sacrifice a small bit of reactivity in favor of ease of re-use of premade elements. I would just set crazy fast polling to the meteor-core-ajax element.

mquandalle commented 9 years ago

@philcockfield, does UI Harness intend to fix this issue?

offthegrass commented 9 years ago

what do people think to this approach?

https://github.com/atoy40/meteor-polymer-music

ryw commented 9 years ago

@offthegrass very cool :) I've talked w/ @schnie about an approach like this; going "pure polymer." One thing i noticed is "broken" back button and no routes. Have you looked into that yet? And have you tried using this on a larger app yet?

mitar commented 9 years ago

Just released: https://github.com/peerlibrary/meteor-blaze-components

Blaze Components for Meteor are a system for easily developing complex UI elements that need to be reused around your Meteor app.

Feedback welcome. :-)

awatson1978 commented 9 years ago

Initial read-through looks very promising, mitar. I like that you managed to get the inheritance and mixins working. Would like to see it all translated over to javascript, though. Am also thinking it would be great to see this wrapped in a webcomponent, so we could have custom defined html tags...

<ExampleComponent></ExampleComponent>
jadsonlourenco commented 9 years ago

@mitar Blaze Component that you did is so good, I try work with Polymer, but after some bugs to browser compatibility, etc. So I did some "component" like you, but your idea is much better, thank you! Now I will use that!

I agree with @awatson1978 to have custom HTML tag for each component, also use the same TAG name for register the component in JS, maybe <example-component></example-component>, to maintain de "web component" sintax.

rgoomar commented 9 years ago

@mitar Looks great! But, I would like to see it in JavaScript as well.

mitar commented 9 years ago

See live tutorial: http://components.meteor.com/

Would like to see it all translated over to javascript, though.

I can make an example.

Am also thinking it would be great to see this wrapped in a webcomponent, so we could have custom defined html tags...

My current plan is to allow wrapping web components into this components. So that you could use Blaze, React, or Polymer components interchangeably. So if you define a Polymer Component inside a this system, you could still do {{#PolymerTag args foo='bar'}}...{{/PolymerTag}} which would then be rendered as a <PolymerTag foo='bar'>...</PolymerTag>. So instead of going the webcomponent direction, I see them only as one possible implementation of these components.

But I have to think more about this. In any case, with current Blaze something like you are suggesting is not really possible. It would require changing the parser. So a job for somebody else.

grigio commented 9 years ago

Here is an example of a Meteor React reusable component https://github.com/grigio/meteor-react-overlay

mitar commented 9 years ago

Yes, I will do something similar. But the idea is that they all share the same API. So when you do {{> IncludedComponent}} you do not have to care what is their rendering technology.

aldeed commented 9 years ago

Nice. This is definitely a step in the right direction. I'd like to eventually have autoform components based on a more general component api like this. It may be impacted by the lexical scope project, and of course I'd want to see whether there is community/MDG support for it or if they have a different direction in mind.

mitar commented 9 years ago

I made an example in JavaScript and ES6: https://github.com/peerlibrary/meteor-blaze-components#javascript-and-es6-support

rgoomar commented 9 years ago

Thanks @mitar!

raix commented 9 years ago

looking promising @mitar maybe add Styles in there - eg. if at some point we want to leverage shadow dom or native...

awatson1978 commented 9 years ago

I'm really liking this so far. I'm thinking of adding this to most all of my projects, but would love to hear something from MDG that they're thinking of something along these lines also. Some more thoughts as I dig deeper into this:

# this seems redundant?
ExampleComponent.register('ExampleComponent');

# can we do this instead?
BlazeComponent.register('ExampleComponent');

# or this?
Templates.registerComponent('ExampleComponent');

Also, what other functions should the Styles class expose? I've just been using a parseStyle function, and am pretty much sold on the json styling objects.

parseStyle = function(json){
  var result = "";
  $.each(json, function(i, val){
    result = result + i + ":" + val + " ";
  });
  return result;
}

Is Styles being dependent upon a jQuery function reasonable? Would underscore or vanilla node be better?