🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
View/Page override
This page was created by Lars.olofsson on 2022-04-26. Last edited by Wikiadmin on 2026-07-29.

You can replace the generated MDriven Turnkey page for a specific ViewModel with your own .cshtml markup when you need a layout or interaction that the standard renderer does not provide.

What a page override does

A page override is a complete replacement for the generated page renderer for one ViewModel. MDriven Turnkey still supplies the ViewModel data to AngularJS; your markup decides how to present and bind that data.

Use a page override for a page that needs a substantially different layout, such as a mobile-oriented booking screen, a calendar, or a seat map. Keep the standard generated UI for pages that do not need a custom layout.

A full page override is different from overriding an individual ViewModel attribute with UIOverride. Use UIOverride when you only need custom rendering for a part of the generated UI. For examples that redesign complete pages, see Documentation:MDriven Movie Theatre Part 2 and Documentation:Turnkey Session 11: More on View Override.

Create an override

  1. Identify the ViewModel that the page uses. The override file name must match the ViewModel name.
  2. In your model assets, create a .cshtml file in yourmodels_AssetsTK/Views/EXT_OverridePages.
  3. Name the file <ViewModel name>.cshtml. For example, the override for a ViewModel named buy tickets is buy tickets.cshtml.
  4. Add the HTML, AngularJS bindings, and any required script or style to the file.
  5. Upload the model. When Turnkey reloads the model, the application uses the override for that ViewModel.
  6. Refresh the application page and test the page's data binding, actions, validation messages, and responsive layout.

To return to the standard generated page, delete the override file and upload the model again.

Start from the generated page

Start with the generated page structure rather than building a renderer from scratch. The standard page demonstrates the AngularJS bindings and MDriven helpers that the ViewModel expects.

@using Eco.MVC
@model AngularModel
@{ Layout = null; }

<h3>{{root._ViewModelPresentation}}</h3>
<form>
  <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)
      </div>
    </div>
  </div>
</form>
@Html.Partial("_DeveloperInfoPartial")

The form allows validation to be triggered so that errors can be displayed. @Html.AngualarUIActions(Model) renders generated actions, @Html.AngualarUI(Model) renders generated UI, and @Html.DisplayErrorsWarningsInfos(Model) renders error, warning, and information constraints. You can retain these helpers where they fit, replace them with custom markup, or combine generated and custom regions.

Bind ViewModel data

The data available to your page is derived from its ViewModel and is exposed through AngularJS. Fields on the root ViewModel class are addressed as root.<attribute>.

For example, a ViewModel with RegistrationNumber and KilometersRun provides bindings such as root.RegistrationNumber and root.KilometersRun.

<label for="registrationNumber">Registration number</label>
<input id="registrationNumber" ng-model="root.RegistrationNumber" />

Use the generated status values to preserve the ViewModel's enablement, visibility, and style logic. This example binds a field and applies the status for the EditOneCar ViewModel class:

<input id="registrationNumber"
       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" />

Pick lists

Bind a select control to the selected object's object identifier and populate it from its pick-list collection.

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

Apply the corresponding disabled, visible, and style expressions when the ViewModel defines them.

Grids and current row

Use ng-repeat for a collection. Every object has a Boolean vCurrent property. Binding it establishes the current object, which is used for master-detail currency.

<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" ng-model="video.vCurrent"></td>
      <td>{{ video.Name }}</td>
      <td>{{ video.Length }}</td>
      <td>{{ video.Genre }}</td>
    </tr>
  </tbody>
</table>

When an attribute belongs to a ViewModel class further down a master-detail chain, retrieve the current object for that class instead of assuming the attribute is on root:

root._Admin.GetCurrentForVMClass('EditOneCar')['RegistrationNumber']

For the root-level example above, this is equivalent to root.RegistrationNumber.

Inspect the data while developing

To discover the data sent for a page, add debug to that page URL. The debug view exposes the JSON data sent by the server, including current values. Use it to verify attribute and collection names before writing bindings.

You can also use browser developer tools to inspect the rendered page and experiment with CSS. After you have finished an override, copy the completed markup, styles, and scripts into the override file, upload the model, and refresh the application. Watch the rendering and overrides walkthrough for an example of inspecting bindings and iterating on an override.

Call AngularJS scope from ordinary JavaScript

Prefer AngularJS directives such as ng-click for page interactions. If you must call JavaScript from an ordinary HTML onclick handler, obtain the current AngularJS scope from the element.

angular.element(this).scope()

From browser developer tools, $0 refers to the currently selected element:

angular.element($0).scope()

A button can have both ng-click and onclick; both handlers run. The following example calls a function in the override page and passes the button's scope:

<button onclick="DelayedSave(angular.element(this).scope())">Save later</button>

The script calls the LazySave action on the JournalSeeker ViewModel after two seconds:

<script>
  function DelayedSave(scope) {
    setTimeout(function () {
      scope.StreamingViewModelClient.CallServerAction('JournalSeeker', 'LazySave');
    }, 2000);
  }
</script>

This pattern is for JavaScript that must run outside AngularJS directive handling. Ensure that the ViewModel name and action name passed to CallServerAction match your model.

Use external assets deliberately

A custom page can use CSS and JavaScript assets stored with the web application content. For example, a seat-reservation template may supply presentation CSS and client-side behavior, while the ViewModel remains the source of seat rows, availability, and selection state.

Do not rely on a downloaded UI script to infer or own application state. Map the ViewModel data to the states the CSS requires, such as available, unavailable, or selected, and let AngularJS render from that data. Check the license of any external template before including it.

Choose the right customization method

Need Use
Replace the layout and markup for one ViewModel page A full page override in EXT_OverridePages
Change rendering for a specific generated ViewModel attribute UIOverride
Customize the login, registration, or account-management experience Documentation:Customizing login and other account UI MVC

See also