You can build data-dependent HTML and SVG bar charts in a ViewModel by generating markup from your Car and BrandOfCar data; this chapter is for Bootcamp learners who have completed Training:Bootcamp:Chapter 12.
This is Chapter 13 of the Bootcamp. Start at Chapter 1 if you are new to the course, or return to Chapter 12 to complete the previous exercise.
Goal
Create two global views:
- CarsAndBrands, which renders an HTML/CSS bar chart showing the percentage of Cars for each BrandOfCar.
- CarsAndBrandsSVG, which renders the same data as an SVG vertical bar chart.
The charts are generated by OCL expressions. The expression produces HTML or SVG as a string, and the ViewModel column is configured to render that string as HTML.
Before you start
- Save your model.
- Check that the model has no errors, shown as red dots. Resolve all errors before continuing.
- Run and save the web application as you work. Each stage below has a small result that you can verify before proceeding.
Create the CarsAndBrands ViewModel
- Create a new ViewModel named CarsAndBrands.
- Set its class to Car. Leave the ViewModel not rooted.
- Under Actions leading here, add a Global Action of type show for this ViewModel.
- Add a grid whose source expression is
Car.allInstances. - Add these columns to the grid:
- Select
asString : self.asString, choose Add column, select BrandOfCar, and selectBrandOfCar_Name : self.BrandOfCar.Name. - Select
asString : self.asString, choose Add column, select RegistrationNumber, and selectRegistrationNumber : self.RegistrationNumber. - Delete the original
asString : self.asStringcolumn by right-clicking it and choosing Delete column.
- Select
- Save and test the web application. The grid should list each car with its brand and registration number.
Add an HTML output column
- Select the grid source column, AllCar : Car.allInstances.
- Choose Add column and then Add generic column.
- Name the new column ThisIsHtml.
- Drag the new widget into the grid layout and resize the AllCar widget so there is room for it.
- In the top menu, select Tagged values, then select Refresh From Wiki.
- Under Add from Value Store, add the tagged value
DataIsHtml=trueto the ThisIsHtml column by selecting Add This. Set the tag value toTrue.
What DataIsHtml does: The column expression still returns a string. With
DataIsHtmlset toTrue, the application renders that string as HTML instead of displaying the markup as text.
Verify HTML rendering
Set the expression for ThisIsHtml to:
'<div style="background:yellow;">hello</div>'
Save and run the application. You should see hello with a yellow background, not the literal <div> markup.
Generate one element per car
Replace the ThisIsHtml expression with:
Car.allInstances->collect(c|
'<div style="background:yellow;">'+c.RegistrationNumber+'</div>'
)->asSeparatedList( '' )
Save and test again. The column should render a yellow element for every Car.
String rule: In OCL, HTML is a string constant, so the overall markup uses single quotes. If markup needs a single quote, escape it with a backslash, or use double quotes in the HTML or CSS attribute instead.
Build the HTML/CSS bar chart
The chart groups cars by brand, calculates each group’s percentage of all cars, and injects one HTML bar for each group.
Understand the grouping result
Start by changing the grid source expression, AllCar : Car.allInstances, to:
Car.allInstances->groupby(c|c.BrandOfCar)
After this change, each row represents a brand group rather than a Car. A group has:
| Value | Meaning | Example |
|---|---|---|
self.BrandOfCar
|
The BrandOfCar used as the group key. | The brand for this row. |
self.List
|
The collection of Car objects in that brand group. | All Cars with that brand. |
The existing columns still expect a Car, so Designer can report that the referring column has type Car when a BrandOfCar group is expected. Correct the columns as follows:
- Scroll to the Car : Car column and change its class to Derive type from referring column.
- Select the BrandOfCar column and set its expression to
self.BrandOfCar.Name. - Select the RegistrationNumber column and set its expression to
self.List->size. - Rename RegistrationNumber to NumberOfCarsOfBrand.
- Save and test. Each row should now show a brand name and the number of cars in its group.
Calculate the percentage for each brand
Set the NumberOfCarsOfBrand expression to:
(100*self.List->size/Car.allInstances->size).Round(0).asString+'%'
For example, if 3 of 10 cars have the same brand, the expression returns 30%.
You can also prepare the brand name and percentage as a two-part tuple directly from the Cars collection:
Car.allInstances->groupby(c|c.BrandOfCar)->collect(tuple|
tuple.BrandOfCar.Name,
(100*tuple.List->size/Car.allInstances->size).Round(0).asString+'%'
)
When you use that expression as the grid source, adjust the columns for the tuple result:
- Set the BrandOfCar name column expression to
self.Name. - Set the percentage column expression to
self.Part2.
Render the chart
Use the complete expression in Chapter 13 – Code Snippet for step 396/399 as the ThisIsHtml expression. It contains the CSS, chart container, grouped data expression, and generated bar elements.
The expression creates a bar similar to this for each grouped brand:
<div class="bar" style="--bar-value:30%;" data-name="Example brand" title="Example brand 30%"></div>
The CSS custom property --bar-value supplies the calculated percentage, while data-name supplies the visible brand label.
If you start from the external HTML bar-chart example used in the original exercise, convert its LESS CSS to browser-ready CSS before adding it to the OCL string. Follow the HTML bar-chart example and use the LESS-to-CSS converter when needed.
Verify that the chart uses live data
- Save and test the HTML chart with the existing data.
- Open a new application window.
- Create a Car with a new BrandOfCar.
- Return to the chart and verify that the new brand is included and that the percentages update.
Clean up the HTML chart view
Once the chart works, remove the helper data grid and leave the chart as the view output.
- Delete the AllCar column.
- Scroll down and delete the Car column.
- Resize the ThisIsHtml widget to fill the intended chart area.
- Delete
<Name>from the Presentation box. - Select the CarsAndBrands ViewModel and set HideSidebar to hide the left-side actions.
- Save and test the web application.
Create the SVG chart
SVG (Scalable Vector Graphics) describes graphics as markup. In this exercise, the ViewModel generates SVG markup with the same grouped Car data.
- Make a copy of the CarsAndBrands ViewModel and name it CarsAndBrandsSVG.
- Add a global show action for CarsAndBrandsSVG.
- Save and verify that you can open the new view.
- Use the SVG vertical bar-chart example to first test the static CSS and HTML/SVG markup in your HTML output column.
- Save and verify that the static example renders before making it data-dependent.
- Replace the static values with the complete step 406 expression from Chapter 13 – Code Snippet.
The SVG expression uses let cars=Car.allInstances in so the same Cars collection is used for each calculation. It groups the cars by BrandOfCar and generates:
- X-axis tick labels from each brand name.
- A bar position based on the brand index.
- A bar height based on
450*tuple.List->size/cars->size.
For example, a group containing half of all Cars produces a calculated bar height of approximately 225 when the SVG chart height is 450.
Debug SVG output in the browser
If the SVG chart does not render as expected, use the Chrome Debugger to inspect what the browser received.
- Open the chart page in Chrome.
- Open Chrome Developer Tools and inspect the generated HTML and SVG elements.
- Check the values inserted into attributes such as
x,y, andheight. - Compare the generated markup with the static version that rendered correctly.
- Correct the OCL string expression, save the model, and reload the page.
This inspection distinguishes a markup or CSS issue from an OCL value-generation issue.
Completion check
You have completed this chapter when:
- The CarsAndBrands global action opens an HTML/CSS chart based on current Car and BrandOfCar data.
- Adding a Car with a new brand changes the HTML chart output.
- The CarsAndBrandsSVG global action opens an SVG chart based on the same data.
- The chart views do not show the helper grid columns or the sidebar.
The completed training model is available as ModelAfterChapter13.zip.
Next chapter
Continue with Chapter 14: Introducing the MDrivenServer.
