Creating a Fully-Custom Graphical Representation
The process for creating a fully-custom graphical representation is very similar to the “Rain or Shine” example in the previous section. There are four steps:
1. Create a custom component that extends the AbstractGraphicalComponent class.
2. Create the client-side JavaScript renderer for the new component.
3. Add an entry to the enumeration of graphical representations.
4. Add the xconf entry that connects your enumeration value with your component class.
The differences from the simple “Rain or Shine” example above and a full graphical representation are that in a full implementation your component class must extend AbstractGraphicalComponent and implement GraphicalComponent, not MultiStateIconComponent, and your component is also responsible for handling the client-side rendering.
Create the Custom Component
Your custom component needs to implement the one API in GraphicalAttRepresentationHandler, three APIs from GraphicalComponent, and one API from GuiComponent class.
• public boolean isValidForDatatype(final Class datatype);
This GraphicalAttRepresentationHandler API is called by the Type and Attribute Management utility to determine whether your graphical representation is available for attributes with the specified datatype. For example, if your representation only applies to integer-valued attributes, like the traffic light example, the API would return true when datatype was java.lang.Long and false otherwise.
• public GraphicalComponent newInstance(Object value, Map<String, Object> metaData) throws WTException;
This GraphicalComponent API is called to create new instances of your graphical component. The parameters include the value of the calculated attribute that is to be displayed, and a map of the values that were specified in the corresponding graphical representation parameters attribute. This method should return a new instance of itself initialized with the specified parameters.
• public JSONObject toJSON() throws WTException;
This GraphicalComponent API is called to convert your graphical component into the JSON object that is passed from the server to the client where it is rendered. The choice of what to pass is entirely up to you. The infrastructure adds one additional item named “comparable” to whatever this method returns.
• public String getJSRendererName();
This GraphicalComponent API is called to determine the name of your client-side renderer. This method should return the full name of the JavaScript method that implements your client-side renderer. For the example below, this function would return “XyzCorp.pieChart.renderer”.
• public Comparable getInternalValue()
This GuiComponent API is called to get the value used for sorting and grouping in client-side tables. The returned value is included in the JSON value passed to the client, in a field named “comparable”. This comparable value is used for sorting.
Create the Client-Side Renderer
The client-side renderer of your custom graphical representation is a JavaScript renderer. This piece of code uses the JSON value produced by the component in the previous step to produce the final HTML representation.
It is important to avoid name conflicts in the JavaScript code. So it is best practice to put all of your JavaScript code in a private name space. For our example, let’s assume your work for Xyz Inc. and you are implementing a new pie chart graphical representation. Your JavaScript name space could be “XyzCorp.pieChart”.
The skeleton of your renderer should look like this:
XyzCorp = {};
XyzCorp.pieChart = {};
XyzCorp.pieChart.renderer = function(table, column, row) {
var data = jsca.columns.getCellValue(column, row);
…
return “<the html value as a string>”;
}
XyzCorp.pieChart.renderer.exportRenderer = function(table, column, row) {
var data = jsca.columns.getCellValue(column, row);
…
return <the export value>;
};
XyzCorp.pieChart.renderer.exportRendererForGWT = function(data) {
…
return <the export value>;
};
XyzCorp.pieChart.renderer.searchFunc = function(table, column, row) {
var data = jsca.columns.getCellValue(column, row);
…
return “<the search value as a string>”;
};
XyzCorp.pieChart.renderer.groupFunc = function(table, column, row) {
var data = jsca.columns.getCellValue(column, row);
…
return “<the group label as a string>”;
};
|
• The first two lines create the name space. Each of the following functions implements a piece of the client-side UI for your graphical representation. The first thing each of these functions does is retrieve into “data” the JSON value that was produced on the server. This value is cached on the client and fetching it does not create client-server traffic. Then each function computes a return value to reflect its aspect of the UI. If there was a serious problem on the server, the value of data may be a simple string, not the expected JSON value. So, it is typical for these functions to first check whether the value is as expected. For example:
if (data && data.myExpectedField1) { … handle expected values … } else { … handle the server-generated error string … }
• The first and main “renderer()” function produces the HTML that represents this element on the rendered page. If the server sent an error string, it should be returned as the result of this function. Otherwise, you can use the JSON value in data to generate whatever HTML is desired.
• The second “exportRenderer()” function returns the string value to be exported for this element. This export value may appear in any of the files produced by the “Export List to File” action in the Windchill UI.
• The third “exportRendererForGWT()” function returns the string value to be exported for this element in GWT, Cadx, and PSB based UIs. This export value may appear in any of the files produced by the “Export List to File” action in the Windchill GWT-based UIs.
• The fourth “searchFunc()” function returns the string value that represents this element during search-in-table operations. If a search string matches any substring of this function’s return value, the corresponding element is included in the search results.
• Finally, the fifth “groupFunc()” function returns the label that is used to identify the group containing this element. When a user uses the table column header menu to “Group By This Field”, the table is sorted and any contiguous rows of the table having the same group label is clustered together using that label to identify the group.
• Notice that the exportRenderer, searchFunc, and groupFunc functions are all defined on the renderer function, XyzCorp.pieChart.renderer. This naming convention is required for those functions to take effect. If you try to name your functions differently or use a different prefix, they are ignored.
|
Add an Entry to the Enumeration of Graphical Representations
Just like the simple “Rain or Shine” example above, use the Type and Attribute Management utility to add an entry to the enumeration of graphical representations. The enumeration is named “Graphical Attribute Representations” and is in the system-level organizer named “PTC Enumerations”. Remember the internal name of your entry. It will be used in the next step. For this example, assume “pieChart” as the internal name of the new entry.
Create the xconf mapping for the component
Add an xconf entry that maps the internal name of your graphical representation’s enumeration entry to the custom component class you created above. For our example pie chart the new entry might look like this. Note the selector must match the internal name of the enumeration entry in the previous section and the serviceClass must match the fully-qualified name of your custom component class.
<Service name=
"com.ptc.core.lwc.common.graphicalatts.GraphicalAttRepresentationHandler">
<Option serviceClass="com.XyzCorp.PieChartComponent" requestor="null"
selector="pieChart" />
</Service>
Again, once you have updated the xconf file, you need to propagate it to the property file it by running XConfManager in a Windchill shell: “xconfmanager -pF” and then restarting the Method Server.
Parent topic