Using Angular 9+
To be able to use the visual components in your Angular 9+ environment, wrap each component into an Angular component, and then render the React component using ReactDom.render
inside.
Depending on your use case, it might be easier to integrate our WebComponents library with your Angular app.
Step 1. Install dependencies.
Install the latest dependencies using either npm
or yarn
. Your application must be able to render React components from @gooddata/sdk-ui-all
using a unique ID (uuid
), and you also must be able to issue an invariant
exception if the DOM node is not available.
npm install --save uuid invariant react@^16.8.0 react-dom@^16.8.0 @gooddata/sdk-ui-all @gooddata/sdk-backend-bear
npm install --save-dev @types/react @types/react-dom
or
yarn add uuid invariant react@^16.8.0 react-dom@^16.8.0 @gooddata/sdk-ui-all @gooddata/sdk-backend-bear
yarn add --dev @types/react @types/react-dom
Step 2. Update the global configuration
Update your configuration to be able to use GoodData.UI in Angular:
- Add
(window as any).global = window;
topolyfills.ts
due to missingglobal
. - Add
"skipLibCheck": true
to yourtsconfig.json
to avoid misleading errors during compilation.
Step 3. Declare the Angular wrapper component.
The Angular wrapper component renders a React component and re-renders it on a property change.
The component wrapper must be able to render React components imported from @gooddata/sdk-ui-all
.
You can import any supported components from the package, and then either put them together using multiple React.createElement
functions, or make an abstract wrapper component that accepts a React component reference as a parameter.
The following examples are using a single KPI component:
kpi.component.ts:
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as uuid from "uuid";
import * as invariant from "invariant";
import {
Component,
Input,
OnInit,
OnDestroy,
OnChanges,
AfterViewInit,
} from "@angular/core";
import { Kpi, IKpiProps, newMeasure } from "@gooddata/sdk-ui-all";
import bearFactory, {
ContextDeferredAuthProvider,
} from "@gooddata/sdk-backend-bear";
// Just for illustration, you would probably create this once in your app and import here
const backend = bearFactory().withAuthentication(
new ContextDeferredAuthProvider()
);
@Component({
selector: "app-kpi",
template: '<span [id]="rootDomID"></span>',
})
export class KpiComponent
implements OnInit, OnDestroy, OnChanges, AfterViewInit {
@Input() measureId: string;
@Input() format: string;
@Input() workspace: string;
@Input() filters: any[];
@Input() onLoadingChanged?: any;
@Input() onError?: any;
public rootDomID: string;
protected getRootDomNode() {
const node = document.getElementById(this.rootDomID);
invariant(node, `Node '${this.rootDomID}' not found!`);
return node;
}
protected getProps(): IKpiProps {
const {
workspace,
measureId,
filters,
format,
onLoadingChanged,
onError,
} = this;
return {
workspace,
measure: newMeasure(measureId, (m) => m.format(format)),
filters,
onLoadingChanged,
onError,
backend,
};
}
private isMounted(): boolean {
return !!this.rootDomID;
}
protected render() {
if (this.isMounted()) {
ReactDOM.render(
React.createElement(Kpi, this.getProps()),
this.getRootDomNode()
);
}
}
ngOnInit() {
this.rootDomID = uuid.v1();
}
ngOnChanges() {
this.render();
}
ngAfterViewInit() {
this.render();
}
ngOnDestroy() {
// Uncomment if Angular issue that ngOnDestroy is called AFTER DOM node removal is resolved
// ReactDOM.unmountComponentAtNode(this.getRootDomNode())
}
}
If you want to render some charts, do the following:
Use a root dom node with the size defined:
columnchart.component.ts:
... import { ColumnChart } from "@gooddata/sdk-ui-all"; ... @Component({ selector: "app-column-chart", template: '<div style="height: 300px" [id]="rootDomID"></div>' }) ... }
Import the
main.css
file from@gooddata/react-components
to your global styles:styles.css:
@import "@gooddata/sdk-ui-charts/styles/css/main.css";
or
angular.json:
{ ... "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "styles": [ "src/styles.css", "node_modules/@gooddata/sdk-ui-charts/styles/css/main.css" ], "scripts": [] }, ... } ... }
If you are using the Pivot Table component, import the pivotTable.css
file into your global styles from @gooddata/sdk-ui-pivot/styles/css/main.css
. For more details about importing global styles in an Angular app, see the Angular documentation.
Memory Leak
When this article was last updated, there was an outstanding issue in Angular. ngOnDestroy
is called after a DOM node has already been removed. Not calling ReactDOM.unmountComponentAtNode(this.getRootDomNode())
results in memory leaks.
Verify whether the issue is present in your version of Angular. If not, uncomment the commented-out line in ngOnDestroy
.
Step 4. Use the component.
You are now ready to use the GoodData React components in your Angular app.
You can use wrapped components across your app, pass the component props to it, and even update them using data-binding.
<app-kpi workspace="xms7ga4tf3g3nzucd8380o2bev8oeknp" measureId="aaEGaXAEgB7U"></app-kpi>
If you want to handle the loading and error content yourself and you do not want to use the default LoadingComponent and ErrorComponent, pass a null explicitly:
LoadingComponent={null}
ErrorComponent={null}
For more information about including React components in Angular, see https://www.packtpub.com/books/content/integrating-angular-2-react.