🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Fashion with tagexpander
This page was created by Hans.karlsen on 2021-04-30. Last edited by Wikiadmin on 2026-07-29.

You can use TagExpander to replace the generated MDriven Turnkey view for a specific ViewModel with your own HTML while retaining generated controls and ViewModel data; this page is for existing TagExpander solutions that you need to understand or maintain.

Do not start new TagExpander implementations. Use PlacingContainers for new advanced UI layouts. TagExpander remains documented here because existing applications may contain it.

When to use TagExpander

TagExpander is an older MDriven Turnkey view-customization mechanism. It lets you take over the HTML for one ViewModel and insert generated controls, raw data, or AngularJS expressions into that HTML.

Use it when you are maintaining an existing override that needs a signature page, such as a dashboard or a highly branded detail page. Keep the ordinary generated UI for routine editing and navigation. A custom override replaces the normal page layout, so you are responsible for the HTML structure and layout of that page.

For an older, fully manual AngularJS/Razor view-override approach, see Documentation:Overriding AngularJS MDriven Turnkey Views. For the wider set of web UI styling options, see Documentation:UI&Styling.

How TagExpander works

MDriven Turnkey looks for a TagExpander file for the ViewModel being displayed. The file must be located and named as follows:

<YourModelFileName>_AssetsTK/
  Views/
    EXT_OverridePages/
      <NameOfViewModel>.tagexpander.cshtml

For example, to override a ViewModel named OrderDashboard in a model named Sales, create:

Sales_AssetsTK/Views/EXT_OverridePages/OrderDashboard.tagexpander.cshtml

When Turnkey finds this file, it expands its tags and caches the resulting view. It also subscribes to changes in the file. When you save the file, Turnkey clears the cached expansion and signals open user interfaces to reconnect. If the displayed view is the changed TagExpander view, it reloads as though it had lost its server context. This short edit-save-refresh cycle is useful while you are refining HTML and CSS.

Create an override for an existing ViewModel

  1. Identify the exact name of the ViewModel whose generated page you want to replace. The file name must match this ViewModel name.
  2. Open the model asset folder, <YourModelFileName>_AssetsTK.
  3. Create the folders Views/EXT_OverridePages if they do not already exist.
  4. Create <NameOfViewModel>.tagexpander.cshtml in that folder.
  5. Add the HTML for the page and insert TagExpander tags where you need controls or data.
  6. Save the file and open or return to that ViewModel in Turnkey. The changed page reloads when the override is detected.

Start with a visible marker while confirming that the file name and location are correct:

<h1>Order dashboard override is active</h1>

If you still see the generated page, check the ViewModel name, the .tagexpander.cshtml suffix, and the folder path. A file named only <NameOfViewModel>.cshtml is a conventional view override, not a TagExpander file.

Insert ViewModel columns

TagExpander recognizes placeholders enclosed in percent signs. A placeholder starts with the name of a column on the root ViewModel and ends with the representation you want.

Tag pattern Result Example
%ColumnName.control% Renders the normal Turnkey control for the ViewModel column. %CustomerName.control%
%ColumnName.data% Inserts the data value rather than a generated control. %OrderNumber.data%
%ColumnName.expression% Inserts the AngularJS expression produced for the column. Use it when an AngularJS directive needs an expression. %AllOrderLines.expression%

Use generated controls when users edit data

Use .control for editable fields and other standard controls. The generated control preserves the behavior designed for that ViewModel column, including its type, visible expression, enable expression, style expression, action state, and access-group behavior.

For example, this custom card uses normal Turnkey controls inside custom HTML:

<div class="order-card">
  <h2>Order</h2>
  <div class="order-card__field">
    %OrderNumber.control%
  </div>
  <div class="order-card__field">
    %Customer.control%
  </div>
  <div class="order-card__field">
    %OrderDate.control%
  </div>
</div>

The surrounding markup is yours to style. The inserted controls remain governed by the ViewModel definitions in MDriven Designer.

Use data for read-only text

Use .data when you want the value without a generated input or display control.

<p class="order-summary">
  Order %OrderNumber.data% was created on %OrderDate.data%.
</p>

Do not use .data as a replacement for an editing control. It gives you data, not the generated control behavior.

Use expressions with AngularJS directives

Use .expression when an AngularJS directive needs the underlying ViewModel expression. This is useful for repeating over a multi-row ViewModel column.

For a root ViewModel column named AllClass1, this markup renders a bullet list:

<ul>
  <li ng-repeat="row in %AllClass1.expression%">
    {{row.InTheGridAttribute1}}
  </li>
</ul>

In this example, AllClass1 is the collection exposed by the root ViewModel. Each row is an item in that collection, and InTheGridAttribute1 is read from that item. The property names used inside Template:... must match the data exposed by the ViewModel.

Built-in tags

TagExpander also provides the following built-in tags.

Tag What it does Typical use
%Save.control% Renders a Save button. It is shown only when there is something to save. Put it in the footer of a custom edit page.
%Cancel.control% Renders a Cancel button. It is shown only when there is something to cancel. Put it next to Save.
%NoMenu.control% Hides the Turnkey main menu. Use it for a focused, full-page custom experience.
%Import(<anyurl>).control% Retrieves HTML or text from the supplied URL and expands it into the TagExpander file. TagExpander tags in the imported content are expanded as well. Reuse an externally maintained HTML design that contains TagExpander placeholders.

For example, a custom edit-page action area can contain:

<div class="order-actions">
  %Save.control%
  %Cancel.control%
</div>

Import external markup carefully

You can use %Import(<anyurl>).control% to bring HTML or text into the override. This can support a workflow where a design is created outside Turnkey and then connected to real ViewModel data by adding TagExpander placeholders.

For example:

%Import(https://example.invalid/order-dashboard.html).control%

The imported content becomes part of the override, and its TagExpander tags are expanded. You remain responsible for the availability and contents of the imported resource. Test the complete page after changes to imported markup.

Layout and styling considerations

A TagExpander override gives you control over the page HTML. You can use your own CSS classes and layout techniques, including CSS Grid. Generated controls retain the style information defined for their ViewModel columns.

If you place generated controls directly in a CSS Grid container, their existing placement styling can still affect the result. Wrap a generated control in another element when you want the custom layout container to determine its placement:

<div class="order-grid">
  <div class="order-grid__item">
    %Customer.control%
  </div>
  <div class="order-grid__item">
    %OrderDate.control%
  </div>
</div>

Keep business rules in the ViewModel, OCL, and EAL rather than duplicating them in page markup. The override should define presentation; generated controls allow the existing ViewModel behavior to continue applying.

Limits and maintenance guidance

  • TagExpander applies to the root ViewModel named by the file. Its placeholders refer to columns available on that root ViewModel.
  • The exact ViewModel name in the file name matters. A mismatch means Turnkey does not select the override.
  • A custom override replaces the generated page structure. Include the controls and actions your users need; they are not added automatically around your custom HTML.
  • Use .control whenever you need the standard Turnkey behavior for a column. Hand-written AngularJS bindings require you to handle presentation concerns that generated controls otherwise retain.
  • Keep overrides small and focused. Use the generated UI for standard pages, and reserve a custom page for the part of the application that genuinely needs its own design.
  • Prefer PlacingContainers for all new advanced UI work.

See also