🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Bootcamp: Chapter 13 - Code Snippet
This page was created by Stephanie on 2024-03-18. Last edited by Wikiadmin on 2026-07-29.

You can use these expressions in the Chapter 13 ThisIsHtml column to render the current distribution of Car objects by brand as HTML or SVG; this page is for Bootcamp learners completing Training:Bootcamp:Chapter 13.

Before you paste a snippet

Complete the setup through the relevant step in Training:Bootcamp:Chapter 13 before using these expressions.

  1. Create the CarsAndBrands ViewModel for Car.
  2. Add the ThisIsHtml generic column.
  3. Set the column tagged value DataIsHtml to True. This allows the generated string to render as HTML rather than appear as text.
  4. Verify the basic rendering test first. For example, the following expression should display each registration number in a yellow block:
Car.allinstances->collect(c|
'<div style="background:yellow;">'+c.RegistrationNumber+'</div>'
)->asSeparatedList( '' )

The chart expressions below use OCL collection operations to create markup:

Expression element What it does in this exercise
Car.allInstances Gets the cars used as the chart data.
c.BrandOfCar) Creates one group for each car brand.
tuple.List->size Gets the number of cars in that brand's group.
(100*.../cars->size).Round(0).asstring+'%' Produces a whole-number percentage such as 25%.
collect(...)->asSeparatedList( ) Produces one markup string by joining the generated bars with no separator.

Use single quotes for OCL string constants. The embedded HTML and CSS use double quotes where appropriate, which avoids escaping single quotes in attributes.

Step 396 and 399: HTML bar chart

Paste the following complete expression into the Expression box for ThisIsHtml. It generates one horizontal bar per BrandOfCar; the bar width is that brand's percentage of all cars.

'<style>
       .chart-wrap {
         margin-left: 50px;
         font-family: sans-serif;
         height: 650px;
         width: 300px;
       }
       .chart-wrap .title {
         font-weight: bold;
         font-size: 1.62em;
         padding: 0.5em 0 1.8em 0;
         text-align: center;
         white-space: nowrap;
       }
       .chart-wrap.vertical .grid {
         transform: translateY(-175px) translateX(175px) rotate(-90deg);
       }
       .chart-wrap.vertical .grid .bar::after {
         transform: translateY(-50%) rotate(45deg);
         display: block;
       }
       .chart-wrap.vertical .grid::before,
       .chart-wrap.vertical .grid::after {
         transform: translateX(-0.2em) rotate(90deg);
       }
       .chart-wrap .grid {
         position: relative;
         padding: 5px 0 5px 0;
         height: 100%;
         width: 100%;
         border-left: 2px solid #aaa;
         background: repeating-linear-gradient(90deg,transparent,transparent 19.5%,rgba(170,170,170,0.7) 20%);
       }
       .chart-wrap .grid::before {
         font-size: 0.8em;
         font-weight: bold;
         content: "0%";
         position: absolute;
         left: -0.5em;
         top: -1.5em;
       }
       .chart-wrap .grid::after {
         font-size: 0.8em;
         font-weight: bold;
         content: "100%";
         position: absolute;
         right: -1.5em;
         top: -1.5em;
       }
       .chart-wrap .bar {
         width: var(--bar-value);
         height: 50px;
         margin: 30px 0;
         background-color: #F16335;
         border-radius: 0 3px 3px 0;
       }
       .chart-wrap .bar:hover {
         opacity: 0.7;
       }
       .chart-wrap .bar::after {
         content: attr(data-name);
         margin-left: 100%;
         padding: 10px;
         display: inline-block;
         white-space: nowrap;
       }
       </style>
'+
'
<div class="chart-wrap vertical">
         <h2 class="title">Bar Chart for Cars and Brands</h2>
  
  <div class="grid">
       '+
       Car.allInstances->groupby(c|c.BrandOfCar)->collect(tuple|tuple.BrandOfCar.Name,(100*tuple.List->size/Car.allinstances->size).Round(0).asstring+'%')
       ->collect(values|'<div class="bar" style="--bar-value:'+values.Part2+';" data-name="'+values.Name+'" title="'+values.Name+' '+values.Part2+'"></div>'

)
->asSeparatedList( '' )+
'
  </div>
</div>'

What the generated markup looks like

For a brand named Volvo representing 25% of the cars, the inner collection produces a bar equivalent to:

<div class="bar" style="--bar-value:25%;" data-name="Volvo" title="Volvo 25%"></div>

The CSS reads --bar-value as the bar width and reads data-name to display the label.

Verify the HTML chart

  1. Save the ViewModel and open the web application.
  2. Confirm that each brand has a bar and that its label and percentage are displayed.
  3. Open a second window, create a car with a new brand, and return to the chart. The new brand must appear in the chart data.
  4. When the chart works, remove the helper columns as described in step 401: delete AllCar, delete Car, resize ThisIsHtml, remove <Name> from the Presentation box, and set HideSidebar on CarsAndBrands.

Step 406: SVG vertical bar chart

Make a copy of the HTML ViewModel named CarsAndBrandsSVG, add the global action for that ViewModel, and first confirm that the static SVG example renders, as directed in Training:Bootcamp:Chapter 13. Then replace the ThisIsHtml expression with the following dynamic SVG expression.

'<style>
      h1 {
        font: 24px sans-serif;
      }
      .bar {
        fill: #d81c3f;
      }
      
      .bar:hover {
        fill: darkgray;
      }
      
      .axis {
        font: 10px sans-serif;
      }
      
      .axis path,
      .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
      }
      
      .x.axis path {
        display: none;
      }
      </style>
'+
'
<div class="chart-wrap vertical">
        <h2 class="title">Bar Chart for Cars and Brands</h2>
  
  <div class="grid">
      '+
      Car.allInstances->groupby(c|c.BrandOfCar)->collect(tuple|tuple.BrandOfCar.Name,(100*tuple.List->size/Car.allinstances->size).Round(0).asstring+'%')
      ->collect(values|'<div class="bar" style="--bar-value:'+values.Part2+';" data-name="'+values.Name+'" title="'+values.Name+' '+values.Part2+'"></div>'

)
->asSeparatedList( '' )+
'
  </div>
</div>'+

'<h1>SVG Vertical Bar Chart</h1>

<svg width="960" height="500">
	<g transform="translate(40,20)">
	<g class="x axis" transform="translate(0,450)">'+
let cars=Car.allInstances in
(
  cars->groupby(c|c.BrandOfCar)->collect(tuple|tuple.BrandOfCar.Name,(100*tuple.List->size/cars->size).Round(0).asstring+'%',BrandOfCar.allinstances->indexof(tuple.BrandOfCar)*35)
->collect(values|'
  		       <g class="tick" transform="translate('+values.Part3.asstring+',0)" style="opacity: 1;"><line y2="6" x2="0"></line>
			<text dy=".71em" y="9" x="0" style="text-anchor: middle;">'+values.Name+'</text>
		</g>

'

)
->asSeparatedList( '' )
)+

'
  		       
		<path class="domain" d="M0,6V0H900V6"></path>
	</g>
		<g class="y axis">
			<g class="tick" transform="translate(0,450)" style="opacity: 1;"><line x2="-6" y2="0"></line>
			<text dy=".32em" x="-9" y="0" style="text-anchor: end;">0%</text>
		</g>
			<g class="tick" transform="translate(0,212)" style="opacity: 1;"><line x2="-6" y2="0"></line>
			<text dy=".32em" x="-9" y="0" style="text-anchor: end;">50%</text>
		</g>
			<g class="tick" transform="translate(0,24.87009919697684)" style="opacity: 1;"><line x2="-6" y2="0"></line>
			<text dy=".32em" x="-9" y="0" style="text-anchor: end;">100%</text>
		</g>
		<path class="domain" d="M-6,0H0V450H-6"></path>
		<text transform="rotate(-90)" y="6" dy=".71em" style="text-anchor: end;">Frequency</text>
	</g>
'+
let cars=Car.allInstances in
(
  cars->groupby(c|c.BrandOfCar)->collect(tuple|tuple.BrandOfCar.Name,
                          (450*tuple.List->size/cars->size).Round(0),
                          BrandOfCar.allinstances->indexof(tuple.BrandOfCar)*35)
->collect(values|'
		<rect class="bar" x="'+values.Part3.asstring+'" width="31" y="'+values.Part2.asstring+'" height="'+(450-values.Part2).asstring+'"></rect>

'

)
->asSeparatedList( '' )
)+

'
</svg>'

How the SVG values are calculated

The SVG plot area is 450 units high.

  • The first SVG collection calculates a percentage label and an x-position. The x-position is the index of the brand multiplied by 35.
  • The second SVG collection calculates a bar value on the 450-unit scale: 450*tuple.List->size/cars->size.
  • Each generated rect uses that value for y and uses 450 - value for height.

For example, if a brand represents 20% of all cars, its calculated bar value is 90. The generated rectangle has y="90" and height="360".

= Diagnose a chart that does not render

  1. Confirm that DataIsHtml remains set to True for ThisIsHtml.
  2. Test the static HTML or SVG before adding OCL-generated values. This separates markup and CSS problems from data-expression problems.
  3. Use the Chrome Debugger to inspect what the browser received. Check whether generated rect elements and their numeric x, y, and height attributes are present.
  4. Check the generated data with a known set of cars. A new car under a new brand should add a new label and bar.
  5. If the ViewModel reports a type error after changing the grouping expression, follow step 398 in Training:Bootcamp:Chapter 13: set the Car column class to Derive type from referring column, then use the adjusted expressions for the grouped values.

See also