🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Overriding AngularJS MDriven Turnkey Views
This page was created by Alexandra on 2018-11-04. Last edited by Wikiadmin on 2026-07-29.

You can replace a generated AngularJS Turnkey view with your own .cshtml page when you need to control the layout and HTML for a ViewModel; this is for developers working with the AngularJS Turnkey client.

Choose the right override scope

Use a page override when you need to reorganize the complete view, such as moving actions into a sidebar, replacing the generated content area, or adding page-specific JavaScript. A page override replaces the built-in page renderer, so you are responsible for rendering the parts of the page that you still need.

If you only need to replace the control for one ViewModel column, use an EXT Component instead. EXT Components support column-level overrides through UIOverride and avoid copying an entire page layout. For example, use an EXT Component to render a text column with a rich-text editor; see Documentation:TinyMCE editor.

Need Use Example
Change the layout of a whole ViewModel page Page override Put generated actions in a left column and content in a right column.
Replace one generated input control EXT Component Render a specific text column with a custom editor.
Load a script before AngularJS compiles the application AppWideAngularScriptIncludes Load a script that registers an AngularJS directive.

Start from the generated bindings

Before writing the override, inspect the bindings suggested for the current ViewModel. Turnkey exposes them at https://<yoursite>/Turnkey/Development?view; the Portal also provides a link. See Bindings for angular.

The bindings reveal the names that the AngularJS page can use. Data available to your HTML is derived from the ViewModel. Do not expect arbitrary server-side domain objects to be available unless the ViewModel exposes them.

For a ViewModel with a column named RegistrationNumber, bind the input to root.RegistrationNumber:

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

The generated bindings also provide status expressions for ViewModel columns. Keep these expressions when your replacement control must follow the modeled enabled, visible, and style behavior:

<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, EditOneCar is the ViewModel class runtime name and RegistrationNumber is the column runtime name. Use the names from your generated bindings; do not copy these names unchanged into another model.

Create a page override

Follow the deployment and override procedure in Turnkey session 9: View Override. Start with a copy of the generic view and then change one area at a time.

  1. Create the override .cshtml file as described in View/Page override.
  2. Begin with the generic structure below.
  3. Confirm that actions, content, loading feedback, and constraint messages still appear.
  4. Replace generated content with your own AngularJS markup, using the binding names from /Turnkey/Development?view.
  5. Test both editable and non-editable states, hidden states, validation messages, and master-detail selection.
@using Eco.MVC
@model AngularModel
@{ Layout = null; }

<h3>{{root._ViewModelPresentation}}</h3>

<form>
  <!-- The form enables postback-style validation so constraint messages can appear. -->
  <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 example uses Bootstrap div layout classes rather than tables for the overall page layout. The table element remains appropriate for tabular data, such as a grid.

Preserve the generated helpers unless you replace their behavior

The generic view contains helpers that render generated UI and feedback:

Helper or expression Purpose in the generic view
@Html.AngualarUIActions(Model) Renders the generated action area.
@Html.AngualarUI(Model) Renders the generated ViewModel UI.
@Html.DisplayErrorsWarningsInfos(Model) Renders error, warning, and information constraints.
root._Admin.Loading() Controls the loading indicator.

For example, if you replace @Html.AngualarUI(Model) with hand-written controls, retain @Html.DisplayErrorsWarningsInfos(Model) unless you provide equivalent feedback yourself. Otherwise modeled constraints may no longer be shown on the page.

Bind common controls

Text input

Bind an editable ViewModel column with ng-model. Add the generated status bindings so the control follows the ViewModel's rules.

<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" />

Select control

A pick list can be rendered with ng-options. This example stores the selected object's object ID in root.Brand and displays each option's Name.

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

Apply the corresponding ng-disabled, ng-show, and ng-class expressions when the select must follow enabled, visible, and style rules.

Grid and current row

Use ng-repeat to render a collection. All ViewModel objects have a Boolean vCurrent property. Binding a checkbox to it selects the current object and sets the vCurrent_<ViewModelClass> variable 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>

Access data in a master-detail hierarchy

At the root ViewModel level, a property can be addressed directly. For example, root.RegistrationNumber works when the current EditOneCar object is represented at the root.

When the ViewModel class is deeper in a master-detail hierarchy, retrieve the current object for that ViewModel class through root._Admin.GetCurrentForVMClass(...):

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

This expression finds the object currently selected for EditOneCar and then accesses its RegistrationNumber. Use this form when a direct root.PropertyName binding does not refer to the intended current detail object.

Add page-specific JavaScript

A page override can contain a <script> block. To obtain the AngularJS scope from an element, use angular.element(this).scope(). For example, an ordinary HTML onclick handler can pass the scope to a function that invokes a ViewModel action after a delay:

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

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

A button can have both ng-click and onclick; both handlers are called. The action names in this example are model-specific. Replace them with the ViewModel class and action names from your application. For details, see View/Page override.

If a script must be available before AngularJS compiles the application, add it through AppWideAngularScriptIncludes or the applicable EXT Component mechanism rather than relying on a page-local script.

Test the override

Test the override after every model update and layout change.

  1. Open the overridden ViewModel page and verify that the expected presentation appears in Template:Root. ViewModelPresentation.
  2. Trigger each action that you keep or replace.
  3. Edit a value that has a constraint and verify that errors, warnings, and information messages appear.
  4. Verify disabled, hidden, and styled column states.
  5. In each grid, select a row and verify that detail bindings follow the selected vCurrent object.
  6. Verify that custom scripts and any required AngularJS directives load in the intended order.

See also