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

[Angular] Init of rg4js doesn't work correctly if it's in constructor #383

Open
DownloadHelper opened this issue Jan 15, 2021 · 7 comments
Labels

Comments

@DownloadHelper
Copy link

Hi !

The standard init of rg4js for Angular ErrorHandler working good :

import * as rg4js from 'raygun4js';
import { ErrorHandler } from '@angular/core';

const VERSION_NUMBER = '1.0.0.0';
rg4js('apiKey', "apiKey");
rg4js('setVersion', VERSION_NUMBER);
rg4js('enableCrashReporting', true);
export class RaygunErrorHandler implements ErrorHandler {
  handleError(e: any) {
    rg4js('send', {
      error: e,
    });
  }
}

But that doesn't work correctly if we move the init into a constructor :

import * as rg4js from 'raygun4js';
import { ErrorHandler } from '@angular/core';

export class RaygunErrorHandler implements ErrorHandler {

  constructor() {
    const VERSION_NUMBER = '1.0.0.0';
    rg4js('apiKey', "apiKey");
    rg4js('setVersion', VERSION_NUMBER);
    rg4js('enableCrashReporting', true);
  }

  handleError(e: any) {
    rg4js('send', {
      error: e,
    });
  }
}

Do you have an idea? I would need this to get my configuration and my apiKey from an Angular service.

Thank you !

@samuel-holt
Copy link
Contributor

Hi @DownloadHelper,

Which version of Angular are you using? I managed to get this to work, but with the version number and API key stored in an environment object.

Thanks,
Sam

@DownloadHelper
Copy link
Author

DownloadHelper commented Jan 18, 2021

Hi @samuel-holt

Thank you for your response ! I'm using Angular 11.0.6.
That can work by using the environment.ts object. Unfortunately, i have to manage configuration in another application. So, my angular is calling and load the configuration in the main.ts class and then load the main AppModule.

  fetch('urlToGetMyConfig')
  .then(response => response.json())
  .then(config => {
    if (environment.production) {
      enableProdMode();
    }
    console.log("config loaded");

    platformBrowserDynamic([{ provide: APP_CONFIG, useValue: config}])
      .bootstrapModule(AppModule)
      .catch(err => console.error(err));
  })

But if we write some code before an angular class, that is execute before Angular lifecycle.

console.log("this is execute before config loaded");

export class RaygunErrorHandler implements ErrorHandler {

  constructor(){
    console.log("this is execute after config loaded");
  }

  handleError(e: any) {
    console.error(e);
    rg4js('send', {
      error: e,
    });
  }
}

That's why i need to init the rg4js into my constructor.

@samuel-holt
Copy link
Contributor

Hi @DownloadHelper,

This is not a common use case, and not one I've come across before. What's the reason for loading the config values from a server?

One avenue that you could look at might be a factory provider. See this blog post for how to handle async providers.

Regards,
Sam

@DownloadHelper
Copy link
Author

Hi @samuel-holt,

I need to load my config in another file than environment.ts because i have to respect a CI/CD principe : "build once deploy everywhere". If i use environment.ts file, i have to create something like environment.pre.ts and environment.prod.ts, so to deploy in pre we have to build like "ng build --pre" and to deploy in prod we have to build like "ng build --prod".
With the solution that externalize config, we have only one build "ng build --prod", and with that we can deploy in all different environment.
All is explain here : https://timdeschryver.dev/blog/angular-build-once-deploy-to-multiple-environments and i used this method : https://timdeschryver.dev/blog/angular-build-once-deploy-to-multiple-environments#platformbrowserdynamic.

Thank you for the link to handle async proviers. Unfortunalty, i already tried and this is the same result. With my main.ts i load config first and then load and bootstrap my app (AppModule). But when we write some js ABOVE Angular class/component : it seems to be run before all Angular lifecycle.
That's why it would be nice if it worked by initializing rg4js in a constructor, or somewhere IN the RaygunErrorHandler class.

Regards,
Antoine

@samuel-holt
Copy link
Contributor

Hi @DownloadHelper,

One idea I had was to use the V1 API to initialize Raygun4JS synchronously - this could be done outside of the Angular sphere (i.e. in the <head> of the document in index.html or equivalent).

For example:

<head>
  <script type="text/javascript">
    !function(a,b,c,d,e,f,g,h){a.RaygunObject=e,a[e]=a[e]||function(){
    (a[e].o=a[e].o||[]).push(arguments)},f=b.createElement(c),g=b.getElementsByTagName(c)[0],
    f.async=1,f.src=d,g.parentNode.insertBefore(f,g),h=a.onerror,a.onerror=function(b,c,d,f,g){
    h&&h(b,c,d,f,g),g||(g=new Error(b)),a[e].q=a[e].q||[],a[e].q.push({
    e:g})}}(window,document,"script","//cdn.raygun.io/raygun4js/raygun.min.js","rg4js");
  </script>
  <script>
  window.addEventListener('load', () => {
    fetch('urlToGetMyConfig')
      .then(response => response.json())
      .then(config => {
        Raygun.init(config.raygunApiKey, {
          // Add options here...
        });
      });
    });
  </script>
</head>

Let me know if that could work for you.

Thanks,
Sam

@egarkavy
Copy link

Hello there. This is not working in all the cases. global Raygun variable might not be assigned in time and the fetch will be executed faster. Eventually you will get the message like 'Raygun is not defined'

I have a screaming to you, dear developers.

STOP PUSHING YOUR LINE, please. I see numerous issues where people complain on that they can't define api key when they want.
You speak about incorrectness of that because "all must be known by the time the app is loaded". NO, it should not, let the business decide how to make their application. Don't tell people to make SSR just literally because of your library.

give just an option for some who really need it to initialize raygun asynchronously. Capture all errors as you do, Do not react in that one time setup in onLoadHandler. What the matter of all the setup if there is no API key?
Give a method which will do the thing and let developers to manually call it.
I have tried different cases.

  1. Raygun.init(...) - may result to Raygun not defined error if your fetch will be executed after Raygun.init code is run
  2. rg4js('boot') causes infinite loop if Raygun is not loaded in time

@darcythomas
Copy link
Contributor

If you use these setup instructions, does it work for you?

https://raygun.com/documentation/language-guides/javascript/angular-two/#using-raygun-with-server-side-rendering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants