Use Angular 2+
To be able to use the GoodData.UI Visual Components in your Angular 2+ environment, wrap each component into an Angular component, and then render the React component using ReactDom.render
inside.
1. Install dependencies
Install latest dependencies using either npm
or yarn
. Your application must be able to render React components from @gooddata/react-components
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.5.2 react-dom@^16.5.2 @gooddata/react-components rxjs-compat@6
npm install --save-dev @types/react @types/react-intl@2.3.8
or
yarn add uuid invariant react@^16.5.2 react-dom@^16.5.2 @gooddata/react-components rxjs-compat@6
yarn add @types/react @types/react-intl@2.3.8 --dev
NOTE: When using Angular 6+, be sure to add the (window as any).global = window;
snippet to the polyfills.ts
file due to missing global
.
2. 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/react-components
.
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 } from '@gooddata/react-components';
interface KpiProps {
measure: string;
projectId: string;
format?: string;
filters?: any[];
onLoadingChanged?: (any);
onError?: (any);
}
@Component({
selector: 'app-kpi',
template: '<span [id]="rootDomID"></span>'
})
export class KpiComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
@Input() measure: string;
@Input() projectId: string;
@Input() filters: any[];
@Input() format: string;
@Input() onLoadingChanged?: (any);
@Input() onError?: (any);
private rootDomID: string;
protected getRootDomNode() {
const node = document.getElementById(this.rootDomID);
invariant(node, `Node '${this.rootDomID} not found!`);
return node;
}
protected getProps(): KpiProps {
const {
projectId,
measure,
format,
filters,
onLoadingChanged,
onError
} = this;
return {
projectId,
measure,
format,
filters,
onLoadingChanged,
onError
};
}
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 4 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/react-components'; ... @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/react-components/styles/css/main.css"
or
angular.json:
{ ... "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "styles": [ "src/styles.css", "node_modules/@gooddata/react-components/styles/css/main.css" ], "scripts": [] }, ... } ... }
NOTE: If you are using the PivotTable
component, import the pivotTable.css
file into your global styles.
For more details about importing global styles in an Angular app, see the Angular documentation.
NOTE: When this article was last updated, there was an outstanding issue in Angular 4. 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
.
3. 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
projectId="la84vcyhrq8jwbu4wpipw66q2sqeb923"
measure="atSHqCtAePe4">
</app-kpi>
If you want to handle loading and error content yourself, and you do not want to use the default LoadingComponent and ErrorComponent, pass 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.