Platform: Docs: Add recipe guide for using Store with custom elements

Created on 14 Mar 2019  ·  43Comments  ·  Source: ngrx/platform

People have asked how you use a single Store across multiple custom elements.

Requirements:

  • A guide that shows how to set it up.
  • A downloadable live example under projects/ngrx.io/examples that is referenced in the guide.
Accepting PRs Docs community watch

Most helpful comment

I read the title thinking of a different scenario, maybe it's easier to implement?
For example, if I had counter-ng-element that keeps its own state, say { counter: number; } - how can element instances get a new store instance? I would like to develop a component knowing that I don't have to worry about multiple instances interfering or sharing each other's states.

Edit: is this worthy of a new issue or a separate story?

All 43 comments

Hey @brandonroberts ,
I can start with this, can you tell me how should i get started,if you can guide me to any examples or demo.

@santoshyadav198613 In theory, it shouldn't be much different than implementing NgRx in a regular scenario. Custom elements can have services (injectables) injected into the constructor just like regular components.

My recommendation would be to create a GitHub repository that:

  • Implements a simple NgRx Store with a Counter (number)
  • Has a custom element that includes a button to dispatches an increment action on the store
  • Has a custom element that includes a button to dispatch an decrement action on the store
  • Has a custom element that displays the current counter value from state

Once we prove this works we can back into an article/guide that shows how to do this.

Here's the Angular docs page for custom elements: https://angular.io/guide/elements#using-custom-elements

What do you think @brandonroberts?

Thanks @wesleygrimes ,

Will start working on the app.

Regards,
Santosh

@santoshyadav198613 any luck on the app? I was thinking about taking a crack at this, but don't want to steal your thunder.

Hey @wesleygrimes,
I am travelling right now, will start working from tomorrow, you will have something to review by this weekend.

Regards,
Santosh

@santoshyadav198613 Sounds good my friend!

@santoshyadav198613 this is looking good! Can you take it a step further and make each piece a separate custom element? Right now it's all together in one counter element, but I think the end goal is to show multiple counter elements communicating with each other through the Store.

@wesleygrimes

@santoshyadav198613 this is looking good! Can you take it a step further and make each piece a separate custom element? Right now it's all together in one counter element, but I think the end goal is to show multiple counter elements communicating with each other through the Store.

thanks sure will start working on it.

Hi @wesleygrimes ,
Please verify now, we have 4 elements.

Hi @wesleygrimes ,
Please verify now, we have 4 elements.

@santoshyadav198613 This is looking good!

@timdeschryver @brandonroberts What do you think? Should the example go any further like showing the elements being used in a non-angular, vanilla js scenario? Or is this good enough?

Hi @timdeschryver @brandonroberts ,

Any update to move forward with this task.

I think it's a good start. Probably would be better to build the components in a separate lib and use them in the example.

Sounds good, will make the changes.

Hi @brandonroberts ,

Added a library project, the only problem is it is not working on stackblitz.
But verified locally works fine. Let me know if any changes needed.

Yeah I would expect issues on Stackblitz. This kind of edge case doesn’t always work there. I will pull locally and test out.

-Wes

On Apr 8, 2019, at 2:02 PM, Santosh Yadav notifications@github.com wrote:

Hi @brandonroberts ,

Added a library project, the only problem is it is not working on stackblitz.
But verified locally works fine.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@santoshyadav198613 looks good to me!

@santoshyadav198613 looks good to me!

thanks @brandonroberts So where we want the documentation to be added.

I wasn't up to date with Angular elements, but this is neat!
Good job @santoshyadav198613 😄
You should consider writing a post about this, I'm sure Angular in Depth would publish it if you would like to.

Thanks @timdeschryver ,

Sure will start writing in weekend, would be happy if you can review it before i publish.

Definitely agree. A post on this would be good

@santoshyadav198613 Feel free to send any draft of the post my way (and I'm sure the other guys too) and we will be happy to review, edit, etc... before you publish.

Hi @wesleygrimes @timdeschryver @brandonroberts ,
Here is the draft https://medium.com/@santosh.yadav198613/using-angular-elements-with-ngrx-bc655e1eb212

Please suggest the changes

@santoshyadav198613 we will review. Certainly has potential to be a great read!

We should also think about getting this or similar content into a recipes page in the docs for NgRx now that you have proved it works.

@brandonroberts @wesleygrimes Can we have one example of custom element's exposed method where we call a ngrx action. I was trying to work with custom elements + NgRX and tried to integrate the custom element in an angularjs app. Amazed to see everything working seamlessly!!! But I am stuck at one place. Whenever from my angularjs app I am calling the custom element exposed method change detection is not happening.

Calling code in angular js
const customElement= angular.element('hello-ce')[0]; customElement.refreshSettingProgressValue(settingsPage);

Exposed method in Angular 7 + @ngrx/store(7.3.0) + Custom Elements:

  ngOnInit(): void {
    this.isLoading$ = this.store.pipe(select(landingPageQuery.getLoading));
    this.categories$ = this.store.pipe(
      select(landingPageQuery.getAllLandingPage)
    );
  }
  @Input()
  public refreshSettingProgressValue = function (this: CompType, settingCategoryCode: string) {
    this.categories$.pipe(
      mergeMap(categories => categories),
      map(category => category.settings),
      mergeMap(settings => settings),
      filter(setting => setting.settingCodeType.code === settingCategoryCode),
      take(1)
    ).subscribe({
      next: (setting) => {
        this.store.dispatch(new ResetSettingsProgressValue(setting));
        this.store.dispatch(new LoadSettingsProgressValue(setting));
      }
    })
  }.bind(this);

I tried calling the _refreshSettingProgressValue _ inside Angular7 app on click of a dummy button and everything works but when I call it outside Angular context I am not able to see the changes in UI.
P.S _LoadSettingsProgressValue_ has an effect where we call some api. I see the call is also happening and reducer is also updating the state but change detection doesn't seem to kick in.

I have already tried ngzone-element-strartergy but it didnt resolve my issue

  ngDoBootstrap(): void {
    const elements: any[] = [
      [HelloWorldComponent, 'hello-ce'],
    ];
    for (const [component, name] of elements) {
      const strategyFactory = new ElementZoneStrategyFactory(component, this.injector);
      const el = createCustomElement(component, { injector: this.injector, strategyFactory  });
      customElements.define(name, el);
    }
  }

I believe we can call changeDetection explicitly but with ngrx how and where can we do that?

Thanks
Sid

Happy to hear this also works for AngularJS 😄
I agree that you'll have to trigger Angular's CD here.

I think currently the only options would be to listen to actions inside the component itself or a better way would to trigger the CD when the select emits a new value.

this.categories$ = this.store.pipe(
      select(landingPageQuery.getAllLandingPage)
      tap(_ => this.changeDetectionRef.detectChanges())
);

Hi Guys,
Let me know where we can add this in guide section, also reviews for blog please.

@timdeschryver
Finally could get the change detected by running inside ngZone like below:

  this.ngZone.run(() => {
        this.store.dispatch(new ResetSettingsProgressValue(setting));
        this.store.dispatch(new LoadSettingsProgressValue(setting));
  });

Tried firing change detection on every emit value after

    this.categories$ = this.store.pipe(
      select(landingPageQuery.getAllLandingPage),
      tap(_ => this.changeDetectionRef.detectChanges())
    );

but it doesn't work!!!. I am not sure why. Can anyone please have an explanation.
Thanks

HI @wesleygrimes @timdeschryver ,
Should i publish it.

Thanks for review @brandonroberts , i have made the changes.

I didn't make it all the way through. I will leave more feedback later today

@santoshyadav198613 Now that you have the blog posted, would you be willing to work on submitting a PR that morphs the blog post into a guide for the docs? I suspect there will be a lot of back and forth, but it would be helpful to get this started. If you need help on submitting PR's for docs, let us know.

Sure @wesleygrimes PR coming soon.

One thing guys, if we add this example, we have already seen Angular Libraries are not supported on StackBlitz. We can give only download option.

Hi @brandonroberts , @wesleygrimes where to add the guide, should I add it in Recipes section under Getting Started of Store.

Hi Santosh,

How about under Store > Recipes > Custom Elements?

Thanks,
Wes

On May 4, 2019, at 2:21 PM, Santosh Yadav notifications@github.com wrote:

Hi @brandonroberts , @wesleygrimes where to add the guide, should I add it in Getting Started section of Store a new section for Store With Angular Elelments


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

I read the title thinking of a different scenario, maybe it's easier to implement?
For example, if I had counter-ng-element that keeps its own state, say { counter: number; } - how can element instances get a new store instance? I would like to develop a component knowing that I don't have to worry about multiple instances interfering or sharing each other's states.

Edit: is this worthy of a new issue or a separate story?

Nice article! @santoshyadav198613
Any chance you could do the same for Angular 9? Or do you think it would work out the same way as it did in Angular 7?

Closing this for now

I read the title thinking of a different scenario, maybe it's easier to implement?
For example, if I had counter-ng-element that keeps its own state, say { counter: number; } - how can element instances get a new store instance? I would like to develop a component knowing that I don't have to worry about multiple instances interfering or sharing each other's states.

Edit: is this worthy of a new issue or a separate story?

@METACEO Any update on this? Need my element instances have separate store instances so that they don't interfere.

@Ashwin2488 nah, I haven't experimented with anything like this in awhile. If I had to jump back into this today, I'd probably start with this @ngrx/component-store - how does this look to you?

FYI @Ashwin2488, sure enough, ngrx/component-store seems to be a good lead!

Local state management with @ngrx/component-store by Alex Okrushko || Angular Conference
https://www.youtube.com/watch?v=zMtubR7etsE

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gperdomor picture gperdomor  ·  3Comments

Matmo10 picture Matmo10  ·  3Comments

NathanWalker picture NathanWalker  ·  3Comments

itprodavets picture itprodavets  ·  3Comments

shyamal890 picture shyamal890  ·  3Comments