Showing posts with label AMD. Show all posts
Showing posts with label AMD. Show all posts

2017-10-29

WebComponents via AMD - pragmatic view

What I need for apps is ability to use AMD dependencies for my and external web components:
  • use the external libs from their CDN on prod, and local on developer machine
  • own AMD-based JS loading web components via AMD (webpack, dojo, requirejs) dependencies
  • own HTML syntax web components which list dependencies in JS section via AMD
Why AMD API? It is running without any compilers/transpilers in browser and compatible with webpack in case of need the bundling.

Why AMD instead of ES6 imports?

While ES6 imports are giving ability to load module from relative path, that is not enough as libraries could be served from different locations including different domains. AMD provides runtime URL resolving based on configuration making a combination of local component development with CDN dependencies a breathe.

How to load WebComponents via AMD?

require([   "link-import!iron-demo-helpers/demo-pages-shared-styles.html|onload"
        ,   "link-import!iron-demo-helpers/demo-snippet.html"
        ,   "link-import!../test-element.html"
        ], (a,b,c)=> console.log(`${a}, ${b}, ${c} webComponents loaded`) );

Of course the AMD configuration should be set to define URL mappings and common script loaded

  var dojoConfig ={ async: true
                  , packages:
                      [ { name: "webcomponentsjs", location: webcomponent_root }
                      , { name: "polymer", location: polymer_root }
                      ]
                  , aliases:  [ [ "link-import"  , isLocal ? "/components/link-import/link-import.js" : "../../link-import.js" ]
                              , [ /(vaadin-)(.*)/, (m,x,y)=>`${vaadin_root}/${x}${y}`   ]
                              , [ /(iron-)(.*)/  , (m,x,y)=>`${iron_root}/${x}${y}`     ]
                              ]
                  , deps: [ "webcomponentsjs/webcomponents-lite" ] // common for all pages
                  };

Here the Polymer and Vaadin Elements are used as good example of WebComponent frameworks.

Your custom WebComponent could use Polymer 2.x syntax and defined as HTML. Just the script section will have a class declaration wrappd into define() and dependencies are listed as JS strings instead of link imports:

        const NAME = 'test-element'
        define( NAME
            ,   [   "link-import!polymer/polymer-element.html"
                ,   "link-import!iron-ajax/iron-ajax.html"
                ,   "link-import!vaadin-material-theme/vaadin-text-field.html" //,   "link-import!vaadin-valo-theme/vaadin-combo-box.html"
                ,   "link-import!vaadin-combo-box/vaadin-combo-box.html"
                ]
            , ()=>
            {
                class TestElement extends Polymer.Element
                {
                    static get is() { return 'test-element'; }

                    static get properties()
                    {
                        return  {   prop1: {
                                        type: String,
                                        value: 'test-element'
                                    }
                                };
                    }
                }

                window.customElements.define( TestElement.is, TestElement );
            });
        require([NAME]);

Where to find the working sample of loading and making WebComponent via AMD?

cdn.xml4jquery.com/ajax/libs/AmdHarness/link-import/test/demo/index.html

What library I need to use AMD loader in my WebComponent?

link-import on github or NPM

Want to go wild?

Help me with creation of module loader for JS and WebComponents as jQuery plugin. The async API for jQuery is already available via xml4jQuery, WebComponents integration is missing:
$('.my-el')
    .html('loading...')
    .linkImport('vaadin-combo-box/vaadin-combo-box.html') // path resolved by config
    .html('');
$('my-el')
    .html('loading...')
    .asWebComponent('vaadin-combo-box') // path resolved by config, WC loaded, and injected
    .prop('items',['red','green','blue]) // property assigned once WC is ready

//  will be auto-populated  even if the code run way later:
$.defineWebComponent( 'my-component' )
    .$on('load')
    .$then( ()=>$.get('colors.json') )
    .addWebComponent('vaadin-combo-box')
    .$then( (combo, colors)=> combo.items = colors );
Happy coding!

2014-11-02

AMD global require and relative path from HTML

In my environment there are lot of tests and templates are HTMLs with self-testing functionality. To make them path-agnostic I was using relative MIDs to same folder resources: scripts, templates and CSS.

ModuleFolder

  • MyModule.html
    • require(["./MyModule"],function(MyModule){ new MyModule({},createDivInBody() ).startup(); }
  • test/test.html
    • require(["../MyModule"],function(MyModule){ new MyModule({},createDivInBody() ).startup(); }
  • MyModule.js
    • require(["dojo/text!./MyModule.html","AMD/cssI!./MyModule.css"],function( template, css) {... }
  • MyModule.css
As dojo AMD and perhaps some other AMD implementations disrespect relative path on global require I need  either to work around or make AMD working as expected. By expected I mean not imaginary potential use of absolute path but real life expectation when looking on the code sniplet above.

There are 2 bugs exposed in the code:

  1. The repative path in HTMLs is treated not as module with JS extension assumed but rather abstract resource, so no JS extension added when path converted to URL. What it could be if not a JS and why there is a difference to MID ?
  2. In CSS plugin the relative path is not working either. It is computated relatively to dojo basePath
The bug fix is not an option(see bellow). What could be done?

  1. Shim the require() and rebase relative pathes with regard to existing basePath
  2. Create artificial "currentPage" package and place into require config, replace relative path dots with this package: require({paths:{currentPackage:location.href... }},["currentPackage/MyModule"]
  3. Use absolute package name: require(["lib/MyLib/PackageX/MyModule"
  4. give up on dojo and use another AMD loader
Perhaps someone could give a better suggestion which is quite appreciated.
If there is a solution, please share.

Regards,
suns

PS. Thank  Christophe Jolif's wisdom for this pain: https://bugs.dojotoolkit.org/ticket/14649
PPS. for interested will keep track on my blog.