Appearance
How to use Templates
Introduction
This guide will help you understand how to use the template library to quickly create a UI for your services.
Where to get a project template
To use the Templates library
, select any of the following options on the start screen: New Project
or Open Project
or Open Templates
. Then go to the right half of the workspace and select the template you need in the Templates
tab. You can also use the search
and filters
to quickly find the template you need.
Files in the template
When you select a template, a new project is loaded containing two files:
index.js
– Implements the core functionality of the template.style.css
– Contains style definitions (Hidden by default).
TIP
To access the hidden files, click on the eye icon next to the files list.
index.js
File (TextToText template)
jsx
import React, { useState } from "react";
import StyledButton from "@integratedComponents/StyledButton";
import OutlinedTextArea from "@commonComponents/OutlinedTextArea";
import "./style.css";
const TextToText = ({ serviceClient, isComplete }) => {
const [output, setOutput] = useState();
const ServiceInput = () => {
const [textInput, setTextInput] = useState();
const isAllowedToRun = () => {
return !!textInput;
};
const onActionEnd = (response) => {
const { message, status, statusMessage } = response;
if (status !== 0) {
throw new Error(statusMessage);
}
// setOutput(message.getValue());
};
const submitAction = () => {
// const methodDescriptor = ;
// const request = new methodDescriptor.requestType();
// request.setValue(textInput)
const props = {
request,
preventCloseServiceOnEnd: false,
onEnd: onActionEnd,
};
serviceClient.unary(methodDescriptor, props);
};
return (
<div className={"content-box"}>
<h4>{"Input"}</h4>
<div className={"content-box"}>
<OutlinedTextArea
type={"text"}
label={"Text"}
helperTxt={"Enter text"}
value={textInput}
onChange={(event) => setTextInput(event.target.value)}
/>
</div>
<div className={"content-box"}>
<StyledButton
btnText={"Submit"}
variant={"contained"}
onClick={submitAction}
disabled={!isAllowedToRun()}
/>
</div>
</div>
);
};
const ServiceOutput = () => {
if (!output) {
return (
<div className={"content-box"}>
<h4>
{"Something went wrong..."}
</h4>
</div>
);
}
return (
<div className={"content-box"}>
<h4>
{"Output"}
</h4>
<div className={"content-box"}>
<OutlinedTextArea
type={"text"}
value={output}
/>
</div>
</div>
);
};
return (
<div className={"service-container"}>
{!isComplete ? <ServiceInput /> : <ServiceOutput />}
</div>
);
};
export default TextToText;
style.css
File (Hidden by default)
css
.service-container {
padding: 10px;
}
.content-box {
margin-bottom: 15px;
}
.content-box .MuiSelect-select {
min-width: 200px;
}
Customizing Templates for Your Service
Let's now transform the Text-to-Text
template into a simple Calculator Service
that performs addition
. We will explain every change in detail.
Transforming the Input Section
For a Calculator
, you need two numbers to perform an addition
operation. Therefore, we replace the single textInput
state variable with two variables: firstValue
and secondValue
. Both are initialized as empty strings."
TIP
Variables can be named differently based on your preference.
Change: Replace the Single Input with Two Inputs
- Before:
jsx
const [textInput, setTextInput] = useState();
- After:jsx
const [firstValue, setFirstValue] = useState(""); const [secondValue, setSecondValue] = useState("");
For the UI update, we add two separate input
fields with the titles before each, so that the user can identify and enter the first
and the second
numbers required for the addition
operation. The OutlinedTextArea
is used with a Heading-4
(h4
) to indicate and input required values.
Change: Update the UI to Display Two Input Fields
- Before (Original UI):
jsx
<h4>{"Input"}</h4>
<div className={"content-box"}>
<OutlinedTextArea
type={"text"}
label={"Text"}
helperTxt={"Enter text"}
value={textInput}
onChange={(event) => setTextInput(event.target.value)}
/>
</div>
- After:
jsx
<h4>{"Input values"}</h4>
<div className={"content-box"}>
<OutlinedTextArea
label={"First value"}
value={firstValue}
onChange={(event) =>
setFirstValue(event.target.value)
}
/>
</div>
<div className={"content-box"}>
<OutlinedTextArea
label={"Second value"}
value={secondValue}
onChange={(event) =>
setSecondValue(event.target.value)
}
/>
</div>
Updating the Submit Action
to Use the add
Method
We set the addition
operation by using Calculator["add"]
from the Calculator
service. The input values from the firstValue
and the secondValue
are assigned to the request using setA()
and setB()
, respectively. This ensures that the backend receives the correct values for the addition
operation.
Change: Update the API Call
Code
- Before (Commented-Out Code):
jsx
const submitAction = () => {
// const methodDescriptor = ;
// const request = new methodDescriptor.requestType();
// request.setValue(textInput)
const props = {
request,
preventCloseServiceOnEnd: false,
onEnd: onActionEnd,
};
serviceClient.unary(methodDescriptor, props);
};
- After:
jsx
const submitAction = () => {
const methodDescriptor = Calculator["add"];
const request = new methodDescriptor.requestType();
request.setA(firstValue);
request.setB(secondValue);
const props = {
request,
preventCloseServiceOnEnd: false,
onEnd: onActionEnd,
};
serviceClient.unary(methodDescriptor, props);
};
Updating the Response Handling
By un-commenting this line, the result returned from the backend is stored in the output
state, which will later be displayed to the user.
Change: Uncomment and Update the Response Handling
- Before (Commented-Out):
jsx
// setOutput(message.getValue());
- After:
jsx
setOutput(message.getValue());
Updating the Output Formatting
The heading {"Service call completed with output:"}
is used alongside other blocks to separate the block title
from the actual result
. The result
is displayed in a read-only OutlinedTextArea
, ensuring a consistent and clear presentation.
Change: Replace the Original ServiceOutput Component
- Before:
jsx
const ServiceOutput = () => {
if (!output) {
return (
<div className={"content-box"}>
<h4>
{"Something went wrong..."}
</h4>
</div>
);
}
return (
<div className={"content-box"}>
<h4>
{"Output"}
</h4>
<div className={"content-box"}>
<OutlinedTextArea
type={"text"}
value={output}
/>
</div>
</div>
);
};
- After:
jsx
const ServiceOutput = () => {
if (typeof output !== "number") {
return (
<div className={"content-box"}>
<h4>
{"Something went wrong..."}
</h4>
</div>
);
}
return (
<div className={"content-box"}>
<h4>
{"Service call completed with output:"}
</h4>
<div className={"content-box"}>
<OutlinedTextArea
value={output}
/>
</div>
</div>
);
};
Final File: index.js (Calculator – Addition)
Below is the complete index.js
implementation after all modifications:
jsx
import React, { useState } from "react";
import StyledButton from "@integratedComponents/StyledButton";
import OutlinedTextArea from "@commonComponents/OutlinedTextArea";
import { Calculator } from "./example_pb_service";
import "./style.css"
const ExampleService = ({ serviceClient, isComplete }) => {
const [output, setOutput] = useState(null);
const ServiceInput = () => {
const [firstValue, setFirstValue] = useState();
const [secondValue, setSecondValue] = useState();
const isAllowedToRun = () => {
return !!firstValue && !!secondValue;
};
const onActionEnd = (response) => {
const { message, status, statusMessage } = response;
if (status !== 0) {
throw new Error(statusMessage);
}
setOutput(message.getValue());
};
const submitAction = () => {
const methodDescriptor = Calculator["add"];
const request = new methodDescriptor.requestType();
request.setA(firstValue);
request.setB(secondValue);
const props = {
request,
preventCloseServiceOnEnd: false,
onEnd: onActionEnd,
};
serviceClient.unary(methodDescriptor, props);
};
return (
<div className={"content-box"}>
<h4>{"Input values"}</h4>
<div className={"content-box"}>
<OutlinedTextArea
label={"First value"}
value={firstValue}
onChange={(event) =>
setFirstValue(event.target.value)
}
/>
</div>
<div className={"content-box"}>
<OutlinedTextArea
label={"Second value"}
value={secondValue}
onChange={(event) =>
setSecondValue(event.target.value)
}
/>
</div>
<div className={"content-box"}>
<StyledButton
btnText={"Submit"}
variant={"contained"}
onClick={submitAction}
disabled={!isAllowedToRun()}
/>
</div>
</div>
);
};
const ServiceOutput = () => {
if (typeof output !== "number") {
return (
<div className={"content-box"}>
<h4>
{"Something went wrong..."}
</h4>
</div>
);
}
return (
<div className={"content-box"}>
<h4>
{"Service call completed with output:"}
</h4>
<div className={"content-box"}>
<OutlinedTextArea
value={output}
/>
</div>
</div>
);
};
return (
<div className={"service-container"}>
{!isComplete ? <ServiceInput /> : <ServiceOutput />}
</div>
);
};
export default ExampleService;
Summary of Changes
State and Input Fields:
- Changed: Replaced the single
textInput
with two state variables:firstValue
andsecondValue
. - Explanation: Two inputs are required to perform an addition operation; therefore, we created two state variables and updated the UI accordingly.
- Changed: Replaced the single
Submit Action Modification:
- Changed: Hardcoded the API call to use
Calculator["add"]
and converted input values from strings to numbers. - Explanation: By using the "add" method, we ensure that the backend performs addition.
- Changed: Hardcoded the API call to use
Response Handling:
- Changed: Un-commented and updated
setResponse(message.getValue());
. - Explanation: This captures the result returned by the backend and stores it in the state for display.
- Changed: Un-commented and updated
Output Formatting:
- Changed: Updated the ServiceOutput component to use
<h4>
tags for headings and an OutlinedTextArea to display the result, as shown in the provided format. - Explanation: This format clearly separates the heading ("Service call completed with output:") from the actual output, ensuring consistent presentation.
- Changed: Updated the ServiceOutput component to use
Required Stub Files for Any Service
For any service to work within the project, two essential stub files must be included:
example_pb_service.js
– Contains the interface for interacting with the service.example_pb.js
– Auxiliary file for service communication.
These files are automatically generated from the .proto
definition and are necessary for handling requests and responses between the frontend and the backend. Add them to our project to ensure proper functionality.
About example_pb_service.js
and example_pb.js
example_pb_service.js
:
This file provides an abstraction layer for making service calls. It contains method descriptors for each operation (e.g.,add
,subtract
) exposed by the service. Each method descriptor includes:- The expected input and output message types.
- The gRPC method name.
It simplifies service method calls by handling the details of the gRPC protocol.
example_pb.js
:
This file contains definitions of the message types used by the service, as defined in the.proto
file. These include:Numbers
: Represents the two numbers (a
andb
) involved in a calculation.Result
: Represents the output value of a calculation.
It provides logic to convert messages into a binary format (serialization) and back (deserialization), enabling communication with the service