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.
Watch this video introduction to view overrides:
<iframe src="https://www.youtube.com/embed/Wu9nP3x7cYM?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe>
Create View Override View Override HTML Fine Tuning Adding an input control Style Boxes or combo boxes Tables Removing the information Complex action: location HTML adding scripts VClientLatitude and vClientLongitude variables Mobile Device UI
Contents
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
- Identify the ViewModel name. For example, the view name
EditOneCarrequiresEditOneCar.cshtml. - Create the
.cshtmlfile inyoumodels_AssetsTK/Views/EXT_OverridePages. - Paste the generated baseline template from this page into the file.
- Replace or extend the generated UI markup with your custom HTML.
- Upload the model so that the asset is distributed to the application.
- 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.
Here is a typical generated .cshtml file structure: <html>
<span style="background: yellow; color: black">@</span><span style="background: white; color: blue">using </span><span style="background: white; color: black">Eco.MVC
</span><span style="background: yellow; color: black">@model </span><span style="background: white; color: #2b91af">AngularModel
@{ Layout = null; }</span>
Understanding the structure helps guide your customizations:
Take a closer look at the parts:
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.
Your ViewModel exposes data bindings accessible throughout the override:
If your ViewModel is defined like this:
root.RegistrationNumber, root.KilometersRun etc
To declare an input id that binds to RegistrationNumber, you would do this:
<input ng-model=âroot.RegistrationNumberâ/>
In the ViewModel, you also have the enable expression you probably want to use:
<input ng-model=âroot.RegistrationNumberâ ng-disabled=â!root.VM_Status.EditOneCar_RegistrationNumber_Enabledâ/>
Then, you have the visible expression:
<input ng-model=âroot.RegistrationNumberâ ng-disabled=â!root.VM_Status.EditOneCar_RegistrationNumber_Enabledâ ng-show="root.VM_Status.EditOneCar_RegistrationNumber_Visible" />
⌠do not forget the style:
<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"/>
This is how we define select controls (Combobox) for setting the Brand in the above ViewModel:
<select ng-model="root.Brand" ng-options="opt.GetOId() as opt.Name for opt in root.BrandPickList" ></select>
You may use disable, show, and style for this as well.
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.
For example, here is a text input binding:
<span style="background: white; color: #2b91af">
</span><span style="background: white; color: blue"><</span><span style="background: white; color: maroon">h3</span><span style="background: white; color: blue">></span><span style="background: white; color: black">{{</span><span style="background: white; color: purple">root._ViewModelPresentation</span><span style="background: white; color: black">}}</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">h3</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">form </span><span style="background: white; color: blue">> </span><span style="background: white; color: #006400"><!--The only reason for having a form is the ability to simulate a <br> postback so that validation is triggered and the error bubbles may show-->
<!--Body content-->
</span><span style="background: white; color: blue"><</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">id</span><span style="background: white; color: blue">="floatingRectangle" </span><span style="background: white; color: red">ng-show</span><span style="background: white; color: blue">='root._Admin.Loading()'><br> <</span><span style="background: white; color: maroon">img </span><span style="background: white; color: red">src</span><span style="background: white; color: blue">='~/Content/ajax-loader.gif' /></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="container-fluid">
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="row">
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="col-md-2" </span><span style="background: white; color: red">style</span><span style="background: white; color: blue">="</span><span style="background: white; color: red">vertical-align</span><span style="background: white; color: black">:</span><span style="background: white; color: blue">top</span><span style="background: white; color: black">;</span><span style="background: white; color: blue">">
</span><span style="background: yellow; color: black">@</span><span style="background: white; color: black">Html.AngualarUIActions(Model)
</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="col-md-10" </span><span style="background: white; color: red">style</span><span style="background: white; color: blue">="</span><span style="background: white; color: red">padding</span><span style="background: white; color: black">:</span><span style="background: white; color: blue">10px</span><span style="background: white; color: black">;</span><span style="background: white; color: red">border-left</span><span style="background: white; color: black">: </span><span style="background: white; color: blue">lightgray thin solid </span><span style="background: white; color: black">;</span><span style="background: white; color: blue">"><br> </span><span style="background: yellow; color: black">@</span><span style="background: white; color: black">Html.AngualarUI(Model)</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="row">
<</span><span style="background: white; color: maroon">div </span><span style="background: white; color: red">class</span><span style="background: white; color: blue">="col-md-offset-2 col-md-10"><br> </span><span style="background: yellow; color: black">@</span><span style="background: white; color: black">Html.DisplayErrorsWarningsInfos(Model)</span><span style="background: yellow; color: black">@*</span><span style="background: white; color: #006400">Error,Warning,Info constraints</span><span style="background: yellow; color: black">*@</span><span style="background: white; color: blue"></</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">div</span><span style="background: white; color: blue">>
</</span><span style="background: white; color: maroon">form</span><span style="background: white; color: blue">>
</span><span style="background: yellow; color: black">@</span><span style="background: white; color: black">Html.Partial(</span><span style="background: white; color: #a31515">"_DeveloperInfoPartial"</span><span style="background: white; color: black">)
</span>
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-disableddisables the input when the generatedEnabledexpression is false.ng-showshows the input when the generatedVisibleexpression is true.ng-classapplies 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.
Here is the complete HTML markup for the grid example: <html>
<span style="background: white; color: green;"> <table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Length</th>
<th>Genre</th>
</tr>
</thead>
<tbody>
<tr <span style="background-color: #ffff00;">ng-repeat="video in root.AllVideo"></span>
<td> <input type="checkbox" name="input" ng-model="video.<span style="background-color: #ffff00;">vCurrent</span>"></td>
<td> {{ video.Name }} </td>
<td> {{ video.Length }} </td>
<td> {{ video.Genre }} </td>
</tr>
</tbody>
</table>
</span>
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
- Make one visible, harmless change first, such as static text, and deploy the asset.
- Refresh the application and confirm that the new text appears. This confirms that the file name and override location match the ViewModel.
- Add one bound input and confirm that it updates the ViewModel as expected.
- Add enabled, visible, and style bindings where the ViewModel defines them.
- 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
- Documentation:Turnkey Session 11: More on View Override
- Documentation:MDriven Turnkey Series
- HowTos:Development in Visual Studio
- Documentation:Turnkey session 4: ViewModel validation
- Documentation:Turnkey session 3: Opting out actions
The video guide includes interactive navigation to key topics:
To make your experience smooth, we set the main tags mentioned in the video to the right bar menu of this mini-player. Choose an interesting subtitle on the list and immediately get to the exact theme navigation item place in the video. Now you can pick any topic to be instructed on without watching the whole video.
<iframe src="https://www.youtube.com/embed/Lxbg7P--5u4?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe>
Car Damages action Page content/Inject Content in Standart Page style section/image as a site asset Java Script section Multiple instances
