🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
MDriven Movie Theatre Part 2
This page was created by Alexandra on 2017-01-28. Last edited by Wikiadmin on 2026-07-29.

You can replace the generated ticket-selection page in an MDriven Turnkey application with a data-aware AngularJS seat map; this walkthrough is for developers continuing the movie-theatre example from Documentation:MDriven Movie Theatre Part 1.

What you build

Part 1 creates the movie-theatre model and a generated ticket-buying ViewModel. A user can choose show seats and submit a reservation request. The request is processed on the server so that two users cannot overbook the same seat.

In this part, you replace the generated BuyTickets page with an external view override. The override renders the show information and seats as a seat map while keeping the seat state and reservation logic in the model.

The completed page can:

  • show the movie image and show-seat information;
  • render seats in rows and columns from ViewModel data;
  • apply CSS classes for available, unavailable, and selected seats;
  • call a ViewModel action when the user clicks a seat; and
  • show a busy indicator while the reservation request is being processed.

Before you start

Complete Documentation:MDriven Movie Theatre Part 1 first. In particular, the ticket-buying ViewModel must expose the show, its seats, and the actions that select seats and submit the reservation request.

Keep reservation decisions on the server. In the example, requests in the RequestProcess state are selected by the server-side ViewModel expression:

SeatRequest.Allinstances->select(sr|sr.State="RequestProcess")

The server checks whether the requested seats are free and grants or declines the request. The browser seat map is presentation and interaction; it must not be the authority for availability.

Create an external view override

An external view override is a page file that replaces the generated page for one ViewModel.

  1. In the external override pages folder, create a text file whose name matches the ViewModel name.
  2. For the movie-theatre ticket page, create BuyTickets.cshtml when the ViewModel is named BuyTickets.
  3. Save the file and reload the application.

An empty override replaces the generated content with an empty page. This confirms that the file is active. You can temporarily add text to the file to verify that the application is loading the override.

To return to the generated page, delete the override file. Turnkey then reverts to its default generated content for that ViewModel.

Start from generated markup

You do not need to author the entire page from scratch.

  1. Copy the generated Angular page dynamic template into the override file.
  2. Use the development page to inspect the ViewModel and the Angular widgets available for it.
  3. Keep the widgets you need and remove or replace the generated layout.

For example, retain the image widget for the movie image and a data-grid widget for a show-seat list while you develop the new page. This gives you a working view before you replace the list with the seat-map markup.

Use a seat-map component as CSS, not as data logic

You can use a third-party seat-reservation sample as a source of HTML structure and CSS styles.

  1. Find a seat-reservation sample whose license permits your intended use.
  2. Download the CSS and JavaScript files you need.
  3. Place them in the web application's content folder.
  4. Reference the CSS and any required assets from the override page.
  5. Inspect the sample's rendered HTML to identify its row, seat, and state classes.

Many downloaded seat-map scripts generate their own seats in a document-ready handler and maintain a separate client-side seat state. That script does not know the Turnkey ViewModel data. Do not let it create or own the seat state.

Instead, reuse the component's CSS classes and reproduce the HTML structure with AngularJS repeats. The CSS then renders the ViewModel data with the appearance of the sample component, while the model remains the source of truth.

Shape the ViewModel data for the seat map

Add derived data to the ticket-buying ViewModel rather than reconstructing seating relationships in browser JavaScript.

A row is a ViewModel item that represents one physical seat row. In the example, the first seat in each row identifies the available rows:

Rows: self.ShowSeat->select(ss|ss.Seat.SeatInRow=1)

ShowSeat is the link object between a show and a physical seat. Each row item must also expose the seats in that row as a collection used by the page, such as Cols.

For every seat item rendered by the page, expose data for:

ViewModel value Purpose in the page
Id Identifies the clicked seat.
jsstate Adds the CSS state suffix, for example selected, available, or unavailable.
Cols Provides the seats to render within a row.
ShowBusyAnimation Controls whether the busy indicator is visible.

Define jsstate from the state values expected by the CSS. For example, if the stylesheet defines separate styling for available, unavailable, and selected seats, make the ViewModel produce the matching state suffix. This approach keeps the display based on model data instead of a second set of JavaScript rules.

Render rows and seats with AngularJS

Use nested ng-repeat directives: the outer repeat renders rows, and the inner repeat renders seats in each row. Adapt the class names to the CSS you imported.

<div style="display:inline-block">
  <div ng-repeat="row in root.Rows" class="seatCharts-row">
    <div ng-repeat="col in row.Cols"
         tabindex="-1"
         class="seatCharts-seat seatCharts-cell front-seat seat{{col.jsstate}}"
         id="{{col.Id}}"
         onclick="ToggleSeat(this)">
    </div>
  </div>
</div>

This markup does not ask a downloaded seat-map script to inject seats. AngularJS renders the seats supplied by root.Rows and row.Cols. The CSS classes provide the visual layout and state styling.

Send a seat click to the ViewModel

A click must pass the selected seat identity to the ViewModel and execute its toggle action. Add a page script that obtains the AngularJS scope from the clicked element, assigns its identifier to the ViewModel value, and posts the action.

<script>
function ToggleSeat(theelement) {
  var thescope = angular.element(theelement).scope();
  thescope.root.jsClickedSeat = theelement.id;
  thescope.$apply(function () {
    thescope.root.VM.Execute_Post('BuyTickets2', 'jsToggleSeat');
  });
}
</script>

The example uses these names:

Name Role
jsClickedSeat A ViewModel value that receives the clicked element's Id.
jsToggleSeat The ViewModel action that uses the clicked seat value to toggle selection.
BuyTickets2 The ViewModel identifier passed to Execute_Post in this example.

Use the names configured in your ViewModel. The value assigned in the page and the value read by the action must be the same. After the action completes, the returned ViewModel state updates the CSS state for the affected seat.

Add a busy indicator

A busy indicator gives the user feedback while a seat request is in progress.

  1. Add the busy-animation asset to the web application's content folder.
  2. Add an image or container to the override page.
  3. Bind its visibility to the ViewModel value root.ShowBusyAnimation with ng-show.
<div ng-show="root.ShowBusyAnimation">
  <img src="../content/filename" alt="Processing seat request" />
</div>

Add ShowBusyAnimation to the ViewModel. In this example, it is true while the current seat request is in the RequestProcess state:

vThisSeatRequest.State='RequestProcess'

Replace filename with the asset you added. Verify the relative path from the overridden page in your application.

Test the override

  1. Reload the ticket-buying page and verify that the movie image and show information still render.
  2. Verify that every seat appears in its intended row and column.
  3. Check each CSS state: available, selected, and unavailable.
  4. Click a seat and confirm that the toggle action receives that seat's identifier and the seat display updates.
  5. Submit a reservation request and verify that the busy indicator appears for RequestProcess.
  6. Confirm that the server-side request processing grants or declines the request and that the final seat state is returned to the page.
  7. Delete the override file once as a rollback test; confirm that the generated page returns, then restore the override.

Design guidance

Keep data transformations close to the ViewModel. For example, deriving Rows from ShowSeat makes the required structure explicit and avoids fragile browser-side logic. You can change the visual component later without moving reservation rules or seat availability into client code.

MDriven Turnkey supports this division of responsibility: MDriven Designer defines information, actions, rules, and views, while an external override lets you take control of the front end where the generated UI is not the desired end-user experience.

See also