The IBM TRIRIGA Application Platform introduces an MVC-based UX framework for Polymer-based applications. The model-view-controller (MVC) approach separates the application into three components or layers: the model, view, and controller.
The framework enables third-party application developers to build intuitive user interface more easily to meet business requirements and with improved performance. But the view layer is built upon the library of Polymer web components. Which means any application developer need to use Polymer web components for developing the views.
The problem arises when organization wants to use a different library to build the user interface within the perceptive application. And as there is no direct support from the framework to render components from different library and technology.
The aim of this code pattern is to demonstrate how to blend technologies (Polymer and React) together to obtain desired User Experience, using ‘PolyReact Wrapper’. At its core we will be rendering React Application within the Polymer web-components in TRIRIGA Perceptive Application framework.
In this code pattern, we will create a starter dashboard using TRIRIGA UX framework and IBM Carbon Design System / IBM Watson IoT Design Patterns and Assets Library which is based on React components. By the end of this code pattern, users will understand how to,
-
Create a TRIRIGA Perceptive Application
-
Develop a React application
-
Build a starter dashboard using IBM Carbon Design System / IBM Watson IoT Design Patterns and Assets Library React components
-
Render the React application in TRIRIGA Perceptive Application
-
At the end you should be able to see following dashboard
- Basics of TRIRIGA Perceptive Applications and UX Framework
- Basics of ReactJS
- NodeJS
- Creating TRIRIGA Perceptive Application
- Setting up the development environment
- Creating the application javascript
- Creating a standalone React Application
- Rendering React Application in TRIRIGA Perceptive Application
- Rendering Carbon Design System components / PAL in TRIRIGA Perceptive Application
To create perceptive application we need to create following modules.
More details can be found here
Steps to create Model Designer
- Go to Tools -> UX Designers -> Model Designer

- In Model Designer click on Add button

- Fill the Name,ID,Exposed Name and Description, for example "ibmSampleApp" and click on Create button

- Then click on Save & Close button

Now we have successfully created Model Designer.
Steps to create Web View Designer
-
Fill the Name,ID,Exposed Name and Description as "ibmSampleApp" make sure exposed name is all lowercase and separated by "-" e.g. "ibm-sampleapp".
Now we have successfully created Web View Designer.
Steps to create Model and View Designer
-
Fill the Name, ID, Exposed Name and Description as "ibmSampleApp".
Select Model Name and View Name as "ibmSampleApp", then click on Create button.

Now we have successfully created Model and View Designer.
Steps to create Application Designer
-
Fill the Name, ID as "ibmSampleApp", Instance ID as -1 and Exposed Name as "SampleApp".
Select App Name as "ibmSampleApp", then click on Create button.

Now we have successfully created Application Designer.
You access sample application here - http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleApp. You should see a loading page.
- Create a new directory
mkdir SampleAppandcd SampleApp - Install tri-deploy and tri-template using node npm.
npm install @tririga/tri-deploy -gandnpm install tri-template -g - Create a new directory
mkdir ibm-sampleappand add a view to tri-deploy using tri-templatetri-template -t starter -e ibm-sampleapp -d ibm-sampleapp/, this will create a starting page template in ibm-sampleapp/ibm-sampleapp.html - Push the template file using
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1 - Now you will be able to see Starter View for ibm-sampleapp here -
http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleApp
At this stage your SampleApp directory should look like this
-
Open
ibm-sampleapp/ibm-sampleapp.htmlin editor of your choice and update the template to classic "Hello World"<link rel="import" href="../triplat-view-behavior/triplat-view-behavior.html"> <link rel="import" href="../paper-material/paper-material.html"> <dom-module id="ibm-sampleapp"> <template> <H1>Hello World</H1> </template> </dom-module> <script> Polymer({ is: "ibm-sampleapp", behaviors: [TriPlatViewBehavior] }); </script> -
Save the file and push it by using
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1and you will be able to see classic "Hello World" here -http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleApp
At this stage your SampleApp directory should look like this
-
Create a new javascript file called app.js in
ibm-sampleapp/app.jsand copy the content below to the same file.Polymer({ is: "ibm-sampleapp", behaviors: [TriPlatViewBehavior] }); -
Update the
ibm-sampleapp/ibm-sampleapp.htmlwith the reference of above created file as shown below.<link rel="import" href="../triplat-view-behavior/triplat-view-behavior.html"> <link rel="import" href="../paper-material/paper-material.html"> <dom-module id="ibm-sampleapp"> <template> <H1>Hello World</H1> </template> </dom-module> <script src="app.js"></script> -
Push the files using
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1
You should find the "Hello World" still working.
At this stage your SampleApp directory should look like this
We will use webpack to create standalone React Application
-
Open a new terminal and cd to
SampleAppdirectory -
Create a directory for standalone React Application
mkdir ReactApp && cd ReactApp/ -
Initialize application using npm
npm init -y -
Install dependencies
npm i react react-dom -S -
Install dev dependencies
npm i @babel/core @babel/plugin-proposal-class-properties @babel/polyfill @babel/preset-env @babel/preset-react babel-loader css-loader style-loader webpack webpack-cli uglifyjs-webpack-plugin -D -
Create
webpack.config.jsfile with following configurationsconst UglifyJsPlugin = require("uglifyjs-webpack-plugin") module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.css$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader", options: { url: false }// translates CSS into CommonJS }] } ] }, plugins: [new UglifyJsPlugin()] } -
Create
.babelrcfile with following content{ "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": ["@babel/plugin-proposal-class-properties"] } -
Create src directory
mkdir src && cd src -
Create
index.jsfile with following content in src directoryimport React from "react"; import ReactDOM from "react-dom"; import './style.css' const Hello = () => { return <div className="app">Hello World from React!</div>; }; ReactDOM.render(<Hello />, document.getElementById("root")) -
Create
style.cssfile with following content in src directory.app{ font-size: 8rem; color: #ff0000; } -
Update the build statement in
package.jsonfile, under scripts add the following"build": "webpack --mode production" -
Now build the React Application by running
npm run build -
Once the build is successful you will see a
distdirectory. Createindex.htmlfile with following content to verify the react application is running properly<div id="root"></div> <script src="main.js"></script> -
Open the index.html file in any browser you should be able to see following
At this stage your SampleApp directory should look like this
Important: Each time you run npm run build, make sure to Push your changes to TRIRIGA server using following command.
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1`
-
Replace the
ibm-sapmpleapp/ibm-sapmpleapp.htmlfile with the following content<link rel="import" href="../polymer/polymer.html"> <dom-module id="ibm-sampleapp"> <template> <poly-react-wrapper id="SampleApp"> </poly-react-wrapper> </template> </dom-module> <script> window.Polymer({ is: "ibm-sampleapp", attached: function(){ let loading = document.getElementById("app-loading-indicator"); if (loading) document.body.removeChild(loading); } }); </script> <script src="main.js"></script> -
Delete
ibm-sapmpleapp/app.jsfile
Update the SampleApp/ReactApp/index.js file to render the React Application in the Polymer web-component <poly-react-wrapper> which will wrap our React Application.
import React from "react";
import ReactDOM from "react-dom";
import './style.css'
const Hello = () => {
return <div className="app">Hello World from React!</div>;
};
window.Polymer({
is: "poly-react-wrapper",
attached: function () {
ReactDOM.render(<Hello />, document.getElementById("SampleApp"))
}
})
-
Go to directory
SampleApp/ReactApp. Runnpm run build -
Copy the build file to Perceptive Application by
cp dist/main.js ../ibm-sampleapp/main.jsand push your changes by running following command
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1 -
Open
http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleAppURL and you should see following -
Update the build statement in
package.jsonfile, under scripts with the following"build": "webpack --mode production && cp dist/main.js ../ibm-sampleapp/main.js"
At this stage your SampleApp directory should look like this
- Open a new terminal and cd to
SampleApp/ReactAppdirectory - Install additional dependencies
npm i carbon-addons-iot-react d3@">=5.0.0 <=5.14.2" -S - Install scss related dev dependencies
npm i postcss-loader sass-loader@7.1.0 node-sass autoprefixer -D - Create new
postcss.config.jsfile with the following contentmodule.exports = { plugins: [ require('autoprefixer') ] }; - Update
webpack.config.jsfile with following contentconst path = require('path'); const { dirname, join } = path; const UglifyJsPlugin = require("uglifyjs-webpack-plugin") module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.scss$/, use: [{ loader: "style-loader" // creates style nodes from JS strings }, { loader: "css-loader", options: { url: false }// translates CSS into CommonJS }, { loader: 'postcss-loader', options: { sourceMap: true, config: { path: 'postcss.config.js' } } }, { loader: "sass-loader",// compiles Sass to CSS options: { includePaths: [join(dirname('carbon-addons-iot-react'), 'node_modules')] } }] } ] }, plugins: [new UglifyJsPlugin()] } - Rename the
style.cssfile tostyle.scssfile. - Update the
index.jsimport statement fromimport './style.css'toimport './style.scss'. - Now run
npm run buildit should run successfully. - Push your changes to TRIRIGA server using following command.
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1
Important: Each time you run npm run build, make sure to Push your changes to TRIRIGA server using following command.
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1`
-
Go to directory
SampleApp/ReactApp. -
Update
index.jswith followingimport React from "react"; import ReactDOM from "react-dom"; import './style.scss' import { Header, } from "carbon-addons-iot-react"; const conf = { appName: "Sample Application", actionItems: [] }; const App = () => { return <> <Header {...conf} /> <div className="app">Placeholder for you app content</div> </>; }; window.Polymer({ is: "poly-react-wrapper", attached: function () { ReactDOM.render(<App />, document.getElementById("SampleApp")) } }) -
Update
style.scsswith the fallowing@import "~carbon-addons-iot-react/scss/styles.scss"; .app{ padding-top: 4rem; @include type-style("productive-heading-03"); } -
Now run
npm run buildthen Push your changes to TRIRIGA server using following command.tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1 -
Now you should see the Carbon UI shell in the perceptive app at
http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleApp
For more information on webpack.config.js for Carbon please refer here
At this stage your SampleApp directory should look like this
At this point you have successfully created a TRIRIGA Perceptive Application rendering React Application. Further we have rendered Carbon UI shell in the dashboard.
In this step, we will render few more components from Watson IoT Patterns and Assets Library.
Important: Each time you run npm run build, make sure to Push your changes to TRIRIGA server using following command.
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1`
-
Create a new directory called
appin pathSampleApp/ReactApp/srcand go toSampleApp/ReactApp/src/app -
Create a file
components.jsin the app directory with the following contentimport React from "react"; import { StatefulTable, StatefulTileCatalog, CatalogContent } from "carbon-addons-iot-react"; import Add from '@carbon/icons-react/lib/add/32'; //Sample table component export function Table(props) { return ( <StatefulTable columns={[ { filter: { placeholderText: 'pick a string' }, id: 'string', name: 'String' }, { filter: { placeholderText: 'pick a date' }, id: 'date', name: 'Date' }, { filter: { options: [ { id: 'option-A', text: 'option-A' }, { id: 'option-B', text: 'option-B' }, { id: 'option-C', text: 'option-C' } ], placeholderText: 'pick an option' }, id: 'select', name: 'Select' }, { id: 'secretField', name: 'Secret Information' }, { id: 'status', name: 'Status', }, { filter: { placeholderText: 'pick a number' }, id: 'number', name: 'Number' } ]} data={[ { id: 'row-1', rowActions: undefined, values: { date: '1973-03-14T23:33:20.000Z', number: 1, secretField: 'OewGc0QsMs', select: 'option-B', status: 'NOT_RUNNING', string: 'helping whiteboard as 1' } }, { id: 'row-4', rowActions: undefined, values: { date: '1973-09-04T14:13:20.000Z', number: 16, secretField: '46GYyWC0w0', select: 'option-B', status: 'NOT_RUNNING', string: 'can pinocchio whiteboard 4' } }, { id: 'row-16', rowActions: undefined, values: { date: '1981-04-13T08:53:20.000Z', number: 256, secretField: 'muYiOaIWGW', select: 'option-B', status: 'NOT_RUNNING', string: 'eat whiteboard pinocchio 16' } }, { id: 'row-22', rowActions: undefined, values: { date: '1988-07-04T06:13:20.000Z', number: 484, secretField: '8oCI6cqmQm', select: 'option-B', status: 'NOT_RUNNING', string: 'whiteboard can eat 22' } }, { id: 'row-31', rowActions: undefined, values: { date: '2003-08-16T02:13:20.000Z', number: 961, secretField: 'AAAAAAAAAA', select: 'option-B', status: 'NOT_RUNNING', string: 'helping whiteboard as 31' } }, { id: 'row-34', rowActions: undefined, values: { date: '2009-10-20T00:53:20.000Z', number: 1156, secretField: 'qcUSWgwIkI', select: 'option-B', status: 'NOT_RUNNING', string: 'can pinocchio whiteboard 34' } }, { id: 'row-46', rowActions: undefined, values: { date: '2040-03-22T03:33:20.000Z', number: 2116, secretField: 'YQmcwk2o4o', select: 'option-B', status: 'NOT_RUNNING', string: 'eat whiteboard pinocchio 46' } }, { id: 'row-52', rowActions: undefined, values: { date: '2058-11-08T16:53:20.000Z', number: 2704, secretField: 'uKQCema4E4', select: 'option-B', status: 'NOT_RUNNING', string: 'whiteboard can eat 52' } }, { id: 'row-61', rowActions: undefined, values: { date: '2091-01-30T12:53:20.000Z', number: 3721, secretField: 'wgO4iKuSyS', select: 'option-B', status: 'NOT_RUNNING', string: 'helping whiteboard as 61' } }, { id: 'row-64', rowActions: undefined, values: { date: '2102-12-19T19:33:20.000Z', number: 4096, secretField: 'c8iM4qgaYa', select: 'option-B', status: 'NOT_RUNNING', string: 'can pinocchio whiteboard 64' } }, { id: 'row-76', rowActions: undefined, values: { date: '2156-03-15T06:13:20.000Z', number: 5776, secretField: 'Kw0WUum6s6', select: 'option-B', status: 'NOT_RUNNING', string: 'eat whiteboard pinocchio 76' } }, { id: 'row-82', rowActions: undefined, values: { date: '2186-03-30T11:33:20.000Z', number: 6724, secretField: 'gqe6CwKM2M', select: 'option-B', status: 'NOT_RUNNING', string: 'whiteboard can eat 82' } }, { id: 'row-91', rowActions: undefined, values: { date: '2235-08-02T07:33:20.000Z', number: 8281, secretField: 'iCcyGUekmk', select: 'option-B', status: 'NOT_RUNNING', string: 'helping whiteboard as 91' } }, { id: 'row-94', rowActions: undefined, values: { date: '2253-03-03T22:13:20.000Z', number: 8836, secretField: 'OewGc0QsMs', select: 'option-B', status: 'NOT_RUNNING', string: 'can pinocchio whiteboard 94' } } ]} secondaryTitle="Sample Table" id="Table" lightweight={false} options={{ hasFilter: true, hasPagination: true, hasRowSelection: 'multi' }} useZebraStyles={false} view={{ pagination: { pageSize: 10, pageSizes: [10, 15], page: 1, totalItems: 14 }, table: { ordering: [ { columnId: 'string', isHidden: false }, { columnId: 'date', isHidden: false }, { columnId: 'select', isHidden: false }, { columnId: 'secretField', isHidden: true }, { columnId: 'status', isHidden: false }, { columnId: 'number', isHidden: false } ] }, toolbar: { activeBar: 'filter' } }} /> ); } //Sample tile component const longDescription = 'Really long string with lots of lots of text too much to show on one line and when it wraps it might cause some interesting issues especially if it starts vertically wrapping outside of tile bounds at the bottom of the tile'; const tileRenderFunction = ({ values }) => <CatalogContent {...values} icon={<Add />} />; const commonTileCatalogProps = { title: 'My Tile Catalog', id: 'entityType', tiles: [ { id: 'test1', values: { title: 'Test Tile with really long title that should wrap', description: longDescription, }, renderContent: tileRenderFunction, }, { id: 'test2', values: { title: 'Test Tile2', description: longDescription }, renderContent: tileRenderFunction, }, { id: 'test3', values: { title: 'Test Tile3', description: 'Tile contents' }, renderContent: tileRenderFunction, }, { id: 'test4', values: { title: 'Test Tile4', description: longDescription }, renderContent: tileRenderFunction, }, { id: 'test5', values: { title: 'Test Tile5', description: longDescription }, renderContent: tileRenderFunction, }, { id: 'test6', values: { title: 'Test Tile6', description: longDescription }, renderContent: tileRenderFunction, }, { id: 'test7', values: { title: 'Test Tile7', description: longDescription }, renderContent: tileRenderFunction, }, ], }; export function Tiles(props) { return ( <StatefulTileCatalog {...commonTileCatalogProps} search={{ placeHolderText: 'Search catalog', }} pagination={{ pageSize: 6 }} /> ); }Here we are creating two react components Table and Tiles using the standard components from Watson IoT Patterns and Assets Library.
-
Create a
index.jsin the app directory with fallowing content.import React from "react"; import { HeaderContainer, Header, SideNav, PageTitleBar, Tabs, Tab, } from "carbon-addons-iot-react"; import Home from "@carbon/icons-react/lib/home/24"; import Dashboard from "@carbon/icons-react/lib/dashboard/24"; import { Table, Tiles } from "./components"; const conf = { appName: "Sample Application", actionItems: [] }; const links = [ { icon: Home, isEnabled: true, metaData: { label: "Devices", href: "#", element: "a" }, linkContent: "Home" }, { isEnabled: true, icon: Dashboard, metaData: { label: "Devices", href: "#", element: "a" }, linkContent: "Dashboard" } ]; export function App() { return <> <HeaderContainer render={({ isSideNavExpanded, onClickSideNavExpand }) => ( <React.Fragment> <Header {...conf} isSideNavExpanded={isSideNavExpanded} onClickSideNavExpand={onClickSideNavExpand} /> <SideNav links={links} isSideNavExpanded={isSideNavExpanded} onClickSideNavExpand={onClickSideNavExpand} /> </React.Fragment> )} /> <div style={{ paddingTop: "3rem", paddingLeft: "3rem" }} > <PageTitleBar breadcrumb={[ <a href="/">Home</a>, <a href="/">Type</a>, <span>Instance</span> ]} isLoading={false} title={"Page Title"} collapsed={false} editable={false} /> <div style={{ paddingLeft: "2rem" }}> <Tabs ariaLabel="listbox" iconDescription="show menu options" role="navigation" selected={0} tabContentClassName="tab-content" triggerHref="#" > <Tab href="#" label="Tab label 1" role="presentation" selected={false} tabIndex={0} > <Table /> </Tab> <Tab href="#" label="Tab label 2" role="presentation" selected={false} tabIndex={0} > <Tiles /> </Tab> </Tabs> </div> </div> </>; };Here we are rendering the following components
- UI Shell
- Page Title with Breadcrumb
- Tabs
- Table
- Tiles
-
Update the
SampleApp/ReactApp/src/index.jsfile with the followingimport React from "react"; import ReactDOM from "react-dom"; import './style.scss' import {App} from "./app"; window.Polymer({ is: "poly-react-wrapper", attached: function () { ReactDOM.render(<App />, document.getElementById("SampleApp")) } }) -
Now open terminal and navigate to path
SampleApp/ReactApp -
Build the application by running
npm run build -
Push your changes to TRIRIGA server using following command
tri-deploy -t <tririga_url> -u <user> -p <password> -v ibm-sampleapp -d ibm-sampleapp -y 1 -
Now you should see the starter dashboard here -
http(s)://<TRIRIGA_HOST>:<PORT>/<CONTEXT_ROOT>/p/web/SampleApp

At this stage your SampleApp directory should look like this
This code pattern is licensed under the Apache License, Version 2. Separate third-party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the Developer Certificate of Origin, Version 1.1 and the Apache License, Version 2.














