🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
A Trello like Board In MDrivenTurnkey
This page was created by Alexandra on 2017-03-05. Last edited by Wikiadmin on 2026-07-29.
  1. Build a Trello-like board in MDriven Turnkey

You can build a board that displays cards in lists, moves a card between lists, and opens a modal editor for a card; this guide is for MDriven Turnkey developers creating a custom AngularJS view override.

A board is a custom presentation of your domain data. In this example, a **Board** contains **BoardLists**, and each BoardList contains **BoardCards**. The custom client code handles pointer interaction, while MDriven actions on the server perform the model changes.

> **Example:** A project board can have the lists `To do`, `In progress`, and `Done`. Each card has a `Name` and `Text`. Dragging `Write release notes` from `To do` to `In progress` changes the card's associated BoardList.

For a ready-to-apply sample model, see Documentation:Example Gist. For general Turnkey documentation, see Documentation:Turnkey.

    1. What you will create

The implementation has four parts:

| Part | Purpose | Example | |---|---|---| | Domain model | Stores boards, lists, and cards. | A BoardCard belongs to one BoardList. | | ViewModels | Expose the board data and editing operations to Turnkey. | `DemoBoard` exposes `BoardLists`, and each list exposes `BoardCards`. | | View override | Replaces the standard Turnkey presentation with board markup. | `BoardDemo.cshtml` renders one table column per BoardList. | | AngularJS directive | Adds drag, drop, and double-click behavior. | `ph-card` tracks a drag and triggers a server-side move action. |

    1. Model the board data

Create the smallest domain model that represents a board:

1. Create a `Board` class. 2. Create a `BoardList` class and associate it with Board. 3. Create a `BoardCard` class and associate it with BoardList. 4. Add `Name` and `Text` attributes to BoardCard.

The video demonstration uses this minimum structure: a board has lists, each list has cards, and each card has a name and text.

> **Example data** > > - Board: `Release planning` > - BoardList: `To do` > - BoardCard: Name = `Write release notes`; Text = `Summarize changes for version 1.0.`

The move operation changes the BoardCard-to-BoardList association. It does not need to recreate the card or copy its text.

    1. Create the ViewModels and actions

Create a ViewModel named `DemoBoard` that presents:

- the board name or presentation on `root` - the board's lists as `root.BoardLists` - each list's name as `boardlist.Name` - each list's cards as `boardlist.BoardCards` - each card's name and text as `card.Name` and `card.Text`

Also create a ViewModel for editing one BoardCard. The board's double-click behavior opens this editor as a modal view.

      1. Add server-side actions

The client should set ViewModel attributes that identify the card to move and the list to move it to, then execute a move action. The move action assigns the card's BoardList association to the selected target list.

The demonstrated implementation also has an action that opens the current card in the card-editing ViewModel as a modal view.

| User interaction | Server-side responsibility | |---|---| | Drop a card on a list | Assign the card's BoardList to the target BoardList. | | Double-click a card | Set the card as current and execute an action that opens the edit-card ViewModel as a modal view. | | Save in the editor | Persist the changed card name or text and return the updated presentation. |

Keep the domain mutation in the action. The client identifies the card and destination, but the server updates the model and returns the updated screen state.

    1. Set up a local Turnkey development site

A local Turnkey site lets you edit overrides and refresh the browser to inspect the result. You can point the local web application at the MDrivenServer of a cloud Turnkey application when you need to use cloud data during development.

      1. Configure the MDrivenServer override

1. Download and import the MDriven Turnkey site into local IIS, or run it with IIS Express. 2. Open the web site in Visual Studio. 3. Configure SSL for the local host name or address that you use, and enable SSL for the site. 4. In the web application's `App_Data` folder, create `MDrivenServerOverride.xml`. 5. Set `MDrivenServerPWD` to the MDrivenServer password and set the element value to the cloud MDrivenServer URL.

```xml <?xml version="1.0" encoding="utf-8"?> <root>

 <MDrivenServerOverride MDrivenServerPWD="----yoursecretpwd---">https://YourMDrivenTurnkeyApp.azurewebsites.net/__MDrivenServer</MDrivenServerOverride>

</root> ```

Normally, MDrivenServer is a sub-application of the Turnkey application in `__MDrivenServer` (two underscore characters). With this override, the locally running Turnkey web application uses the MDrivenServer at the specified cloud URL instead.

> **Gotcha:** Treat `MDrivenServerPWD` as a secret. Do not commit a real password to source control.

    1. Register the board script

Put custom scripts in the Turnkey web application's `EXT_Scripts` folder.

Turnkey includes files with `- NotInEffect` in the name. To activate one of these extension points, copy the file and remove `- NotInEffect` from the copy's name. Read the copied file for its own instructions.

To load the board script application-wide:

1. Locate `AppWideAngularScriptIncludes – NotInEffect.html`. 2. Copy it and remove `- NotInEffect` from the new file name. 3. Add the Board script include to the active file.

```html <script src="/EXT_Scripts/Board.js"></script>| ```

Turnkey will then look for `Board.js` in `EXT_Scripts`.

    1. Add the view override

Create an override named for the ViewModel presentation. For the `DemoBoard` ViewModel, the demonstrated override file is `BoardDemo.cshtml`.

The copy command in the original implementation places this file in:

```text Views\EXT_OverridePages\ ```

Use the override location used by your local Turnkey web application. The important point is that `BoardDemo.cshtml` replaces the standard presentation for this view.

      1. Board markup

The following markup renders each BoardList as a column and each BoardCard as a card. It also adds directive markers that `Board.js` uses for interaction.

```cshtml @{ Layout = null; }

Template:Root. ViewModelPresentation

<style>

 .boardlist {
   vertical-align: top;
 }
 .boardheader {
   font-weight: bold;
   font-size: 1.20rem;
   background-color: seagreen;
   text-align: center;
   color: white;
 }
 .card {
   vertical-align: top;
   background-color: mediumpurple;
   margin: 2px;
   border-radius: 12px;
   border: 2px solid purple;
   padding: 6px;
 }
 .cardname {
   font-weight: bold;
 }
 .cardtext {
   font-size: 0.90rem;
   word-wrap: break-word;
 }
 .rotateWhileMove {
   -webkit-transform: rotate(7deg);
   -moz-transform: rotate(7deg);
   -ms-transform: rotate(7deg);
   -o-transform: rotate(7deg);
   z-index: 1000;
 }
 .myHorizontalbut {
   height: 24px;
   width: 90px;
   position: relative;
   padding: 2px;
   display: inline-block;
   margin: 2px;
 }

</style>

<button ng-repeat="oneaction in root.VM.StateActions() | orderBy:'+SortKey'"

       ng-class="oneaction.Class"
       ng-click="oneaction.Execute(); hidemenu();"
       ng-disabled="!oneaction.Enable"
       class="myHorizontalbut">
 Template:Oneaction.Presentation

</button>

<img id="loadingAnimation"

    src="/Content/loadingAnimation.gif"
    scroll-position="scroll"
    style="margin-top: 0px;"
    ng-show="root.VM.Loading()"
    class="ng-hide">

```

      1. How the bindings work

AngularJS `ng-repeat` repeats markup for each item in a collection.

| Binding | Result | |---|---| | `ng-repeat="boardlist in root.BoardLists"` | Creates one board column for every exposed list. | | `Template:Boardlist.Name` | Shows the list name in the column header. | | `ng-repeat="card in boardlist.BoardCards"` | Creates one card element for every card in that list. | | `Template:Card.Name` and `Template:Card.Text` | Show the card content. | | `vmclassid="Template:...VMClassId"` | Supplies the ViewModel class identifier used by the directive behavior. |

> **Example:** If you add a fourth BoardList in the standard form and save it, `root.BoardLists` contains the new list. The `ng-repeat` binding creates a fourth board column when Turnkey updates the view.

    1. Implement drag, drop, and double-click behavior

`Board.js` installs AngularJS directives for the board, lists, and cards. The card directive sets the card element's `vmclassid`, makes the element relatively positioned, and listens for pointer events.

The original implementation uses TypeScript as the source and generates `Board.js` from it. Keep the TypeScript source in your development project and deploy the generated JavaScript to `EXT_Scripts`.

      1. Required interaction flow

Implement the following flow in your directives:

1. On card mouse down, start tracking the pointer position. 2. While the pointer moves, move the card element visually and apply the `rotateWhileMove` class. 3. On mouse up, inspect the board-list elements to find whether the drop point is inside a list. 4. If the pointer is inside a list, identify the dragged card and target list. 5. Set the corresponding ViewModel attributes for the move action. 6. Execute the move action. 7. Let MDrivenServer update the BoardCard-to-BoardList association and return the refreshed board. 8. On a double-click, set the selected card as current and execute the action that opens the modal card editor.

The client-side code should not duplicate the association-update logic. Its role is limited to detecting the interaction and invoking the appropriate ViewModel action.

> **Example:** When the user drops `Write release notes` inside the `In progress` column, the directive sets the move target card and target list, then executes the move action. The action assigns `Write release notes` to `In progress`; Turnkey then receives the updated board state.

    1. Keep custom files separate from the Turnkey web project

You can develop the override and TypeScript in a separate Visual Studio project, then copy the generated files to the local Turnkey site after a build. This keeps custom control code separate and suitable for its own source-control repository.

The demonstrated post-build commands copy the override and scripts to the running web application:

```text xcopy /Y "$(ProjectDir)BoardDemo.cshtml" "C:\CapableObjectsWush\source\StreamingApp\WebApplication2\Views\EXT_OverridePages\" xcopy /E /Y /I "$(ProjectDir)EXT_Scripts" "C:\CapableObjectsWush\source\StreamingApp\WebApplication2\EXT_Scripts" ```

Replace the example destination paths with paths for your local Turnkey web application.

      1. Development loop

1. Run the local Turnkey site from Visual Studio. 2. Open the board in a browser. 3. Edit `BoardDemo.cshtml` or the TypeScript source in the separate project. 4. Build the separate project so its post-build step copies the files. 5. Refresh the browser to inspect the changed override.

    1. Inspect the data and standard presentation

Before writing an override, inspect the existing Turnkey output and the ViewModel data it exposes.

- Open `<your-site>/MDriven/Development` and select the ViewModel to inspect the standard UI documentation and available bindings. - Append `debug` to the board page URL to inspect a JSON representation of the current ViewModel data. - Use the browser debugger to inspect the live board object, lists, and cards.

These tools let you verify that the bindings in the override match the data exposed by your ViewModel.

> **Example:** If `root.BoardLists` is absent from the debug output, the markup cannot create columns. Update the `DemoBoard` ViewModel before troubleshooting the HTML.

    1. Verify the board

Test the feature in this order:

1. Open a board with at least three lists and one card. 2. Confirm that every list appears as a column. 3. Add a list through the available form and save; confirm that a new column appears after the update. 4. Confirm that each card displays its `Name` and `Text`. 5. Drag a card to another list; confirm that the association changes and the card appears in the destination column. 6. Double-click a card; confirm that the modal card editor opens. 7. Edit the card name or text, save, and confirm that the board shows the updated value.

    1. Limitations and implementation notes

- This example uses a table layout with a fixed board height of `300px`; adjust the presentation only after the data bindings work. - The board depends on the ViewModel names used in the markup: `root.BoardLists`, `boardlist.BoardCards`, `Name`, `Text`, and `VMClassId`. - The complete directive source is not included in the available page content. The essential behavior is the interaction flow described above: identify card and target list on mouse up, set ViewModel targets, and execute the server-side action. - Use the debug view and `/MDriven/Development` to confirm the actual properties and actions exposed by your implementation before changing the override.