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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tag as input for allowing dynamic web elements loading #5

Closed
angelfraga opened this issue Aug 20, 2019 · 19 comments
Closed

Add tag as input for allowing dynamic web elements loading #5

angelfraga opened this issue Aug 20, 2019 · 19 comments

Comments

@angelfraga
Copy link
Contributor

Hi, great job.

You save my day 馃 but I think a new feature can be added.

Correct me if I am wrong but I think there is a case the library is not covering.
If we are in a shell app, and we want to load several micro-apps (web-elements) dynamically based on an incoming config, the shell shouldn't know about future tag elements.

For example, having this config as available micro-apps/widgets:

[
  { "tag": "micro-app1", "url": "/micro-apps/micro-app1/main.js" },
  { "tag": "micro-app2", "url": "/micro-apps/micro-app2/main.js" }
 ...
  { "tag": "micro-appN", "url": "/micro-apps/micro-appN/main.js" }
]

If we add a new input (tag) to the lazy-element.directive.ts, we could replace the original tag by the giving tag.

const elementTag = (this.template as any)._def.element.template.nodes[0]

The usage could look like this:

<div *axLazyElement="'/micro-apps/micro-app1/main.js'; tag: 'micro-app1'"></div>

Being converted to

<micro-app1 *axLazyElement="'/micro-apps/micro-app1/main.js'; tag: 'micro-app1'"></micro-app1>

At least, in my case, the shell app code doesn't know about the incoming config and tag elements and therefore I still have to inject the micro-apps using script elements.

@tomastrajan
Copy link
Member

hi @angelfraga !

That's a great use case, I was playing around with something like this, but even more dynamic where I wanted to specify tag under which will the element be registered by the directive and NOT element itself. This broke the change detection and lead to different errors.

What you propose is one step less... The element register itself as custom element with some tag as before, no change here.

But the directive will not know about that element. Definitely worth trying will have a look!

@angelfraga
Copy link
Contributor Author

Hi @tomastrajan, thanks for answering.
I see, anyways this library is still fine for me because I could use the service independently about how the elements are getting injected.

I am building an angular shell app which will load micro-apps (angular apps as web-elements) base on the user micro-apps configuration, but the shell itself doesn't know about which kind of micro-app or tag elements has to be injected. But each micro-app bundle is defining its own web element.
Therefore until now, I just was doing something like:

this.element.nativeElement.innerHTML = "<${microApp.definition.tag}></${microApp.definition.tag}>"

I saw your comment in the closed PR about change detection.
Maybe it sounds crazy or maybe there is a better way to do it, but I think I could define a new component on the fly for each micro-app definition which template will be filled by the micro-app tag + lazyElement directive.
Something like <${microApp.definition.tag} *axLazyElement="${microApp.definition.url} "></${microApp.definition.tag}> as on the fly component template. And then, inject via ng-template the new component reference .

What do you think about?

@tomastrajan
Copy link
Member

Hi @angrlfraga! This sounds very interesting! I am not sure if it is possible to inject HTML containing directives and bindings like [prop] and (click) on the fly and if yes, it would probably mean we need @angular/compiler to be available during runtime but definitelly something worth exploring further!

@tomastrajan
Copy link
Member

This could be something to investigate...

<wrapper>
  <ng-container ngProjectAs="counter">
    <counter></counter>
  </ng-container>
</wrapper>

@angelfraga
Copy link
Contributor Author

angelfraga commented Sep 4, 2019

Hi @tomastrajan ,

First of all, thanks for giving support to this issue. 馃憤

You are right, the compiler is necessary, in order to create an element on the fly which template is composed by the giving tag. Like here:

<${microApp.definition.tag} *axLazyElement="${microApp.definition.url} "></${microApp.definition.tag}>

On the other hand, ngProjectAs looks interesting. If i getting good your idea, it could be something like that, right? (being definition my micro-app definition)

  <ng-container [ngProjectAs]="definition.tag">
     <div  *axLazyElement="definition.url"></div>
  </ng-container>

Definitevly it is something to be investigated.

Let say it is working, then I could do something like that:

  <ng-container *ngFor="let definition of microAppDefinitions" 
                           [ngProjectAs]="definition.tag">
     <div  *axLazyElement="definition.url" 
               [customInput]="somethingToSet"
               (customEvent)="somethingToDo()"
               (ready)="registerSomething()"></div>
  </ng-container>

@tomastrajan
Copy link
Member

@angelfraga yes that's the idea, I didn't try it yet but was speaking with a collegue today about the topic and he pointed me towards the ngProjectAs so if you tried it and it worked I could implement it also in the library!

@tomastrajan
Copy link
Member

@angelfraga I guess I have other solution! Have to test it a bit, but if this works is extra simple, hopefully also robust :D

@angelfraga
Copy link
Contributor Author

angelfraga commented Sep 4, 2019

Great,
Actually I am trying out but without success.
https://stackblitz.com/edit/angular-elements-with-ngprojectas

I am just trying everything ng-template, ng-container, ng-content. One has to be the good one 馃槃

@tomastrajan
Copy link
Member

I just got this to work...

 <div
              *axLazyElement="
                'https://unpkg.com/@material/mwc-button@0.6.0/mwc-button.js?module';
                loadingTemplate: loading;
                module: true;
                tag: 'mwc-button'
              "
              raised
              (click)="increment()"
            >
              Increment
            </div>

but have to explore bit more before adding it as a feature to make it work with Module style configuration

@tomastrajan
Copy link
Member

Basically I overwrite (this.template as any)._def.element.template.nodes[0].element.name = this.tag; and this tag is passed in through *axLazyDIrective

but will prolly make a dedicated <ax-dynamic-lazy-component> for it instead of the <div> or <span> that way the functionality can be scoped to that without influence on anything else but lets see, will go to sleep now but will have a look tomorrow!

@tomastrajan
Copy link
Member

Because now we support

// pre-configured LazyElementsModule
const options: LazyElementModuleOptions = {
  elementConfigs: [
    { tag: 'ion-button', url: 'https://unpkg.com/@ionic/core@4.6.2/dist/ionic/ionic.js' }
  ]
};

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  declarations: [FeatureComponent],
  imports: [
    LazyElementsModule.forFeature(options),
  ]
})
export class FeatureModule { }

so they have to play nicely together!

@angelfraga
Copy link
Contributor Author

It sounds great 馃憤
are you working on a specific branch?

@tomastrajan
Copy link
Member

No just changed couple of lines on local right now, will push something tomorrow, but I am getting pretty confident we can make this work!

@angelfraga
Copy link
Contributor Author

It sounds great 馃弲

(this.template as any)._def.element.template.nodes[0].element.name = this.tag;

instead of using the tag directly as I did. (which was wrong)

const elementTag = !!this.tag ? this.tag : (this.template as any)._def.element.template.nodes[0]

let see tomorrow 馃槂

@tomastrajan
Copy link
Member

Yes, we have to re-assign tag in that template node so it's rendered, the elementTag was only used for logging ;)

@tomastrajan
Copy link
Member

Ok, looks like we will go with *axDynamicLazyElement="'tag-name'; url: 'https://whatever.com/element.js'" ... But yeah, tomorrow :D

@tomastrajan
Copy link
Member

But it work!

@tomastrajan
Copy link
Member

tomastrajan commented Sep 5, 2019

Fixed in 2442e60

馃摝 v8.6.0

Release info: https://twitter.com/tomastrajan/status/1169551355142463488

@mohammedzamakhan
Copy link
Contributor

@tomastrajan looking that the above implementation of 2442e60, I feel that it is missing one main component, that is change of tag name is not supported. What I mean is, suppose if we have these couple of module configurations

LazyElementsModule.forRoot({
    elementConfigs: [{
      tag: 'mwc-button',
      url: 'https://unpkg.com/@material/mwc-button@0.6.0/mwc-button.js?module',
      isModule: true,
    }, {
      tag: 'ion-button', url: 'https://unpkg.com/@ionic/core@4.6.2/dist/ionic/ionic.js'
    }]
  })]

and in my template I have

<button (click)="updateTag()">Update Tag</div>
<div *axLazyElementDynamic="tagName"></div>

and when I try to update the tag, I will not be able to do it. I think its important to support it.

class MyComponent {
    tagName = 'mwc-button';

    updateTag() {
        this.tagName = 'ion-button';
   }
}

does that make sense @tomastrajan @angelfraga?

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

No branches or pull requests

3 participants