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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Props Support #41

Closed
mancioshell opened this issue Apr 14, 2020 · 13 comments · Fixed by #42
Closed

Custom Props Support #41

mancioshell opened this issue Apr 14, 2020 · 13 comments · Fixed by #42

Comments

@mancioshell
Copy link

single-spa have custom props feature, but i can't see the way how i could use it in single-spa-angularjs helper to initialize some props on my angularjs app.
Any advice?

@joeldenning
Copy link
Member

Hi @mancioshell - good question. single-spa-angularjs currently does not pass the props down to angularjs applications, but I'd be very open to adding that. In other helper libraries, we've passed the single-spa props down as props to React/Vue component. However, in single-spa-angularjs there is only a template string that gets injected into the DOM, instead of a component.

Perhaps the ideal would be adding the single-spa props to $rootScope.singleSpaProps? I'll put together a PR that does that.

While you're waiting for that PR, you can get the single-spa props by providing a second, custom mount lifecycle:

import singleSpaAngularJS from 'single-spa-angularjs';
import angular from 'angular';
const ngLifecycles = singleSpaAngularJS({
  angular: angular,
  mainAngularModule: 'app',
  uiRouter: true,
  preserveGlobal: false,
  template: '<my-component />',
});
export const bootstrap = ngLifecycles.bootstrap;
export const mount = [
  function (singleSpaProps) {
    return Promise.resolve().then(function() {
      console.log('single-spa props', singleSpaProps)
    })
  },
  ngLifecycles.mount
];
export const unmount = ngLifecycles.unmount;

@joeldenning
Copy link
Member

This is now avaialbe in https://github.com/single-spa/single-spa-angularjs/releases/tag/v3.2.0. You can access the props via the following:

$rootScope.singleSpaProps

@mancioshell
Copy link
Author

mancioshell commented Apr 15, 2020

Hi @joeldenning thank you for your support.
I have updated to the last release, but when i try to access singleSpaProps from $rootScope i received undefined.

app.run(['$log', '$rootScope', 'MessageService',
  ($log, $rootScope, MessageService) => {   
    $rootScope.$on("$routeChangeStart", () => {  
      $log.log("receive routeChangeStart ...");
      MessageService.clearMessages();
      console.log($rootScope.singleSpaProps) // undefined
    });
  }
]);

The strange things is, when i try to console.log $rootScope i see singleSpaProps in chrome console.

Cattura

@joeldenning
Copy link
Member

The following commit shows that it works - polyglot-microfrontends/account-settings@bd2a378. And you can see the singleSpaProps by checking the browser console at https://polyglot.microfrontends.app/settings

My code change to single-spa-angularjs adds the singleSpaProps to rootScope after angular.bootstrap() is called. It is possible that app.run() runs synchronously during angular's bootstrap function, which would mean the singleSpaProps have not yet been set on the root scope? I see you're waiting for the routeChangeStart event, I'm not sure if that happens synchronously or not either. What I would try is putting a breakpoint / console.log into single-spa-angularjs and into your app.run() call to see which one is running first.

@mancioshell
Copy link
Author

mancioshell commented May 4, 2020

It works for me if i inject $rootScope directly in a controller, but is undefined if i inject it in a service (using service or privider api).
I would like to use doAjax function (my custom prop) in a separate AjaxService in my AngularJS microfrontend.

@joeldenning
Copy link
Member

joeldenning commented May 4, 2020

I doubt that that is related to single-spa-angularjs (although I'm happy to be proven wrong 😄), and would guess that you have some issues in your angular code that is attempting to use them in the ajax function. Since single-spa props is defined on the $rootScope, you can do whatever you'd like with it, including injecting it into the DI namespace via angular.module().factory(). I would need a example code repo to convince me that there was somehow something weird with the single-spa props that makes them impossible to inject.

@joeldenning
Copy link
Member

One thing with the single-spa props is that they are only initialized once the single-spa application is mounted - if you attempt to access them before the application is mounted, they won't be present.

@mvximvs
Copy link

mvximvs commented Mar 27, 2021

HI @joeldenning, thank you for all your work... I have a question related to this case... what about using props in an angularjs component? I'm using this to isolate an old component, and I'm wondering if it is possible to pass the props in a component way to be able to reuse my bindings (input and outputs):

<component name="props.name" update="props.callbackFunction()><component/>"

Thanks :)

@mvximvs
Copy link

mvximvs commented Mar 27, 2021

I have to say that I've found the exactly same problem of @mancioshell... the console.log('$rootScope', $rootScope.singleSpaProps); works only on chrome... on my component is undefined...
Below the version of the libraries that I'm using:

  "dependencies": {
    "angular": "^1.7.9",
    "single-spa-angularjs": "^4.2.2"
  }

@joeldenning
Copy link
Member

Here's a code sandbox that shows how: https://codesandbox.io/s/serverless-river-0cm9r?file=/index.html

You can access the single spa props in templates via $root.singleSpaProps. For example:

<div>{{ $root.singleSpaProps.token }}</div>

I discovered a way to make sure that the application is rerendered when the props are set, and will submit a PR with that.

@joeldenning
Copy link
Member

The fix I mentioned above is released in https://github.com/single-spa/single-spa-angularjs/releases/tag/v4.2.3

@Xcone
Copy link

Xcone commented Jul 9, 2021

In case any one else still has issues:
I had trouble accessing the singleSpaProps from a controller class; where my component was part of the initial render. Google led me to this issue, so in case others fare the same way; here's how it's fixed for me. I am using singleSpaAngularJs 4.3.1.

With the code fragments below, the const props variable would be undefined, exactly how @mvximvs mentioned.

class wrapperController {
    constructor($scope, $rootScope) {
        const props = $rootScope.singleSpaProps; // undefined
        // etc.
myAppModule.component("wrapper", {
    controller: wrapperController,
    template,
});
const ngLifecycles = singleSpaAngularJS({
    angular: angular,
    mainAngularModule: "app",
    uiRouter: false,
    preserveGlobal: false,
    template: '<wrapper/>',
});

It was a bit of a goose chase, since console.log($rootScope) would show the data including the singleSpaProps, but console.log($rootScope.singleSpaProps) would be undefined. I later learned that if rootscope is stringified; then the string is logged; it would also not include singleSpaProps. There must be some form of race condition going on there; where Chrome only formats the logging a small moment later, and meanwhile the angular bootstrap finishes and the props are added by singlespa.

Regardless, I was able to work-around the issue with a small addition to the template of the main module. By adding ng-if="$root.singleSpaProps" the rendering is postponed until after single spa adds the props; after which the above code does work.

const ngLifecycles = singleSpaAngularJS({
    angular: angular,
    mainAngularModule: "app",
    uiRouter: false,
    preserveGlobal: false,
    template: '<wrapper ng-if="$root.singleSpaProps" />',
});

I'm by no means an AngularJS expert; but me and a colleague had a bit of a peek if this could somehow be fixed as part of singleSpaAngularJS parcel setup, but I don't think there's a way. The controller is already created as part of the angular bootstrap; and we could not find a way to pass the singleSpaProps before the bootstrap.

@joeldenning
Copy link
Member

Thanks for sharing, yeah I'm not sure of a way to force the rootScope.apply() to occur before any controllers/templates are processed.

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

Successfully merging a pull request may close this issue.

4 participants