🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Turnkey session 9: View Override
This page was created by Alexandra on 2017-01-22. Last edited by Wikiadmin on 2026-07-29.

You can replace the generated page for a MDriven Turnkey ViewModel with your own HTML and AngularJS bindings when you need a layout or interaction that the generated UI does not provide.

What a view override does

A view override is a .cshtml page whose name matches a ViewModel. When Turnkey finds that page, it renders your page instead of the standard generated UI for that ViewModel.

For example, an override named EditOneCar.cshtml replaces the generated UI for the ViewModel named EditOneCar. Your HTML can bind only to data exposed by that ViewModel and to its generated status information.

Use an override when you want to:

  • Arrange fields in a custom HTML layout.
  • Replace generated editors with your own inputs, select controls, or grids.
  • Apply ViewModel-defined enabled, visible, and style expressions to custom controls.
  • Add browser-side HTML and JavaScript integrations. Session 9 demonstrates the context of adding a Google map and obtaining the device location; implement those integrations in your override according to the requirements of the service and browser you use.

Create an override

  1. Identify the ViewModel name. For example, the view name EditOneCar requires EditOneCar.cshtml.
  2. Create the .cshtml file in youmodels_AssetsTK/Views/EXT_OverridePages.
  3. Paste the generated baseline template from this page into the file.
  4. Replace or extend the generated UI markup with your custom HTML.
  5. Upload the model so that the asset is distributed to the application.
  6. Reload the application and open the ViewModel. The application now uses the override page.

The Session 9 walkthrough also shows creating an override for a view, saving and sending settings, restarting the application, and then refreshing the browser to verify that the replacement page is active. Watch the Session 9 walkthrough.

Start with the generated page

Start from the standard page rather than from an empty file. It retains the generated action area, UI area, loading indicator, and validation messages while you decide which parts to replace.

@using Eco.MVC
@model AngularModel
@{ Layout = null; }
<h3>{{root._ViewModelPresentation}}</h3>
<form> <!-- The form lets validation be triggered so error bubbles can show. -->
  <div id="floatingRectangle" ng-show="root._Admin.Loading()">
    <img src="~/Content/ajax-loader.gif" />
  </div>
  <div class="container-fluid">
    <div class="row">
      <div class="col-md-2" style="vertical-align:top;">
        @Html.AngualarUIActions(Model)
      </div>
      <div class="col-md-10" style="padding:10px;border-left: lightgray thin solid;">
        @Html.AngualarUI(Model)
      </div>
    </div>
    <div class="row">
      <div class="col-md-offset-2 col-md-10">
        @Html.DisplayErrorsWarningsInfos(Model)@* Error, warning, and info constraints *@
      </div>
    </div>
  </div>
</form>
@Html.Partial("_DeveloperInfoPartial")
Element Purpose
Template:Root. ViewModelPresentation Shows the presentation text of the root ViewModel.
@Html.AngualarUIActions(Model) Renders the generated action area.
@Html.AngualarUI(Model) Renders the generated body UI.
root._Admin.Loading() Controls whether the loading indicator is shown.
@Html.DisplayErrorsWarningsInfos(Model) Renders error, warning, and information messages, including constraint feedback.

The template uses Bootstrap div layout rather than tables so that the generated layout is responsive. You can retain any of these sections, replace them, or remove them. If you remove the generated body or actions, you are responsible for providing the replacement UI that your users need.

Bind controls to ViewModel data

The data available to an override comes from the ViewModel. AngularJS expressions use root for the root ViewModel.

If the ViewModel exposes RegistrationNumber and KilometersRun, bind them as root.RegistrationNumber and root.KilometersRun.

Text input

Bind an input with ng-model:

<input ng-model="root.RegistrationNumber" />

For example, editing this input updates the RegistrationNumber value exposed by the root ViewModel.

Enabled, visible, and style expressions

Turnkey exposes generated status expressions for ViewModel members under root.VM_Status. Use them so that a custom control follows the same enabled state, visibility, and style decisions as the generated control.

<input ng-model="root.RegistrationNumber"
       ng-disabled="!root.VM_Status.EditOneCar_RegistrationNumber_Enabled"
       ng-show="root.VM_Status.EditOneCar_RegistrationNumber_Visible"
       ng-class="root.VM_Status.EditOneCar_RegistrationNumber_Style" />

In this example:

  • ng-disabled disables the input when the generated Enabled expression is false.
  • ng-show shows the input when the generated Visible expression is true.
  • ng-class applies the generated style value.

Define the underlying behavior in the ViewModel and its expressions; see Documentation:Turnkey Session 11: More on View Override for a further override example and Documentation:Turnkey session 4: ViewModel validation for ViewModel validation.

Select control (combobox)

Use a pick list exposed by the ViewModel with ng-options. This example binds root.Brand to the selected object identifier and renders each option's Name:

<select ng-model="root.Brand"
        ng-options="opt.GetOId() as opt.Name for opt in root.BrandPickList">
</select>

You can apply the same ng-disabled, ng-show, and ng-class patterns to a select control.

Render a grid

Use ng-repeat to render a row for each object in a ViewModel collection. The following example renders the AllVideo collection:

<table>
  <thead>
    <tr>
      <th></th>
      <th>Name</th>
      <th>Length</th>
      <th>Genre</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="video in root.AllVideo">
      <td><input type="checkbox" name="input" ng-model="video.vCurrent"></td>
      <td>{{ video.Name }}</td>
      <td>{{ video.Length }}</td>
      <td>{{ video.Genre }}</td>
    </tr>
  </tbody>
</table>

Each ViewModel object has a Boolean vCurrent property. Setting it identifies the current object through the corresponding vCurrent_NAMEOFVMCLASS value. This is how currency is handled in a master-detail UI.

Bind to the current object in a master-detail hierarchy

When a member is directly on the root ViewModel, use a direct binding such as root.RegistrationNumber. When the member belongs to a ViewModel class farther down a master-detail hierarchy, get that class's current object through root._Admin.GetCurrentForVMClass.

<input ng-model="root._Admin.GetCurrentForVMClass('EditOneCar')['RegistrationNumber']" />

For EditOneCar at the root, this is equivalent to root.RegistrationNumber. For an EditOneCar instance deeper in the hierarchy, the GetCurrentForVMClass form targets the currently focused object instead of assuming that it is the root.

Verify and maintain the override

  1. Make one visible, harmless change first, such as static text, and deploy the asset.
  2. Refresh the application and confirm that the new text appears. This confirms that the file name and override location match the ViewModel.
  3. Add one bound input and confirm that it updates the ViewModel as expected.
  4. Add enabled, visible, and style bindings where the ViewModel defines them.
  5. Add collection rendering and current-object handling only after the basic bindings work.

Keep the override focused on rendering. Model behavior, validation, actions, and expressions remain defined in the ViewModel. For CSS styling, see Documentation:MDriven Turnkey Series. For local development and debugging of HTML bindings, see HowTos:Development in Visual Studio.

See also