A Trello like Board In MDrivenTurnkey
No edit summary
(Adding page to Category:TOC because it contains a TOC.)
 
(34 intermediate revisions by 4 users not shown)
Line 1: Line 1:
=== Video tutorial ===
 
<html>
<html>
<h4> Boards are really popular and useful. In order to do more advanced UI controls in MDriven Turnkey AngularJS you can study how this trello-like-board is constructed. In this video we show how to add one to your MDriven Turnkey application and fill it with any info that is important in your domain model. </h4>
<h4> Boards are really popular and useful. To create more advanced UI controls in MDriven Turnkey AngularJS, you can study
<p class="warn">  
  how this Trello-like board is constructed. In this video, we show you how to add a board to your MDriven Turnkey application and
<em>To make your experience more comfortable, we set the main tags mentioned in the video to the right bar menu of this mini player. Choose the interesting subtitle on the list and immediately get to the exact theme timeplace in the video. Now you can pick any topic to be instructed without watching the whole video.</em>
  fill it with any important info in your domain model. </h4>
<style type="text/css">
<p class="video-warn">
p.warn {
  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. </p>
opacity: 0.7;
text-align: justify;
width: 90%
}
</style>
</p.class="warn">


<style>
<div class="video">
#video12 {
   <div class="video__wrapper">
  position: relative;
    <iframe src="https://www.youtube.com/embed/jfmASHr_IXM?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe>
  height: 500px;
   </div>
  width:560px;
  <div class="video__navigation">
  padding-bottom: 10px;
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="55" tabindex="0"> View Model for Trello like board </span>
}
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="81" tabindex="0"> Board View Overriding </span>
#video12 iframe {
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="265" tabindex="0"> Using Development Side </span>
  position: absolute;
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="420" tabindex="0"> Putting Styling Information </span>
  min-height: auto;
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="514" tabindex="0"> Directive Scripts </span>
  min-width: auto;
    <span class="navigation-item" data-video="jfmASHr_IXM" data-start="708" tabindex="0"> Mouse Down Moves Implementation </span>
}
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="825" tabindex="0"> Mouse Up Moves (Moving a Card)</span>
#video12 div {
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="949" tabindex="0"> MoveAction in Details </span>
  position: absolute;
     <span class="navigation-item" data-video="jfmASHr_IXM" data-start="1060" tabindex="0"> Edit Card Action </span>
  top: 0;
  </div>
  left:760px;
  width: 260px;
  height: 100%;
  padding-left: 10px;
  overflow-y: auto;
}
span {
    font-size: 18;
    display:block;
    padding: 2px 10px 0.5px 10px;
    padding-bottom: 0.5;
    padding-top: 0.5;
  opacity: 0.7;
}
span:hover {
  color: #0000FF;
   cursor: pointer;
}
span:focus {
  color: blue;
</style>
<br>
<div id="video12">
<iframe width="740" height="500" src="https://www.youtube.com/embed/WALLlxcdIpM?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe>
   <div>
     <span data-video="WALLlxcdIpM" data-start="50" tabindex="0"> View Model for Trello like board </span>
     <span data-video="WALLlxcdIpM" data-start="76" tabindex="0"> Board View Overriding </span>
     <span data-video="WALLlxcdIpM" data-start="260" tabindex="0"> Using Development Side </span>
     <span data-video="WALLlxcdIpM" data-start="415" tabindex="0"> putting styling information </span>
     <span data-video="WALLlxcdIpM" data-start="509" tabindex="0"> Directive scripts </span>  
    <ul>  <span data-video="WALLlxcdIpM" data-start="703" tabindex="0"> mouse down moves implementation  </span>  
     <span data-video="WALLlxcdIpM" data-start="820" tabindex="0"> mouseup moves ( moving a card )</span>  
     <span data-video="WALLlxcdIpM" data-start="944" tabindex="0"> MoveAction in details </span> </ul>
     <span data-video="WALLlxcdIpM" data-start="1055" tabindex="0"> edit card action </span>
</div>
</div>
</div>


<script>
var IMG = document.querySelectorAll('#video12 span'),
    IFRAME = document.querySelector('#video12 iframe');
for (var i = 0; i < IMG.length; i++) {
  IMG[i].onclick = function() {
    IFRAME.src = 'http://www.youtube.com/embed/' + this.dataset.video + '?rel=0&autoplay=1';
    if(this.dataset.end) IFRAME.src = IFRAME.src.replace(/([\s\S]*)/g, '$1&end=' + this.dataset.end);
    if(this.dataset.start) IFRAME.src = IFRAME.src.replace(/([\s\S]*)/g, '$1&start=' + this.dataset.start);
    this.style.backgroundColor='rgba(0,0,0,.2)';
  }
}
</script>
</html>
</html>


=== Installation ===
== '''Installation''' ==
In order to develop something like this it really helps to install [[Installing TurnKey as an Azure WebApp|MDriven Turnkey locally]]. This way you can change and see the effect without ftp’ing a lot of files back and forth.
To develop something like this, it is beneficial to install [[Installing TurnKey as an Azure WebApp|MDriven Turnkey locally]]. This way, you can change and see the effect without flipping a lot of files back and forth.


One important ability you may want to use is to continue to use the data in the cloud – but still have the Turnkey web app locally. I use this ability all the time to debug and develop turnkey. And you can use it to when developing page overrides and advanced controls: MDrivenServerOverride.xml
One important ability you may want to maintain is to continue to use the data in the cloud – but still have the Turnkey web app locally. I use this ability all the time to debug and develop Turnkey. You can use it when you develop page overrides and advanced controls: MDrivenServerOverride.xml


The file MDrivenServerOverride.xml is placed in your App_Data folder and has this format:
The file MDrivenServerOverride.xml is placed in your App_Data folder and has this format:
Line 90: Line 37:
  <MDrivenServerOverride MDrivenServerPWD="----yoursecretpwd---"><nowiki>https://YourMDrivenTurnkeyApp.azurewebsites.net/__MDrivenServer</nowiki></MDrivenServerOverride>  
  <MDrivenServerOverride MDrivenServerPWD="----yoursecretpwd---"><nowiki>https://YourMDrivenTurnkeyApp.azurewebsites.net/__MDrivenServer</nowiki></MDrivenServerOverride>  
  </root>
  </root>
Adding a file like this allows you to move where MDriven Turnkey should go to find its MDriven Server. Normally the MDriven Server is placed in a sub-application to the Turnkey-application in a folder named __MDrivenServer (double underscores). But this way I can use a local Turnkey app – and point out the MDrivenServer for the cloud app.
Adding a file like this allows you to move where MDriven Turnkey should go to find its MDriven Server. Normally, the MDriven Server is placed in a sub-application to the Turnkey-application in a folder named __MDrivenServer (double underscores). But this way, I can use a local Turnkey app – and point out the MDrivenServer for the cloud app.


== Running the MDriven Turnkey in Visual Studio ==
== '''Running the MDriven Turnkey in Visual Studio''' ==
[[File:Web application.png|thumb|376x376px]]
[[File:Web application.png|thumb|376x376px]]
Download and import the MDriven Turnkey in your local IIS – or use IIS Express. Then open the website in Visual Studio.  
Download and import the MDriven Turnkey in your local IIS – or use IIS Express. Then, open the website in Visual Studio.  
* Make sure you have the hang of SSL certificate for your localhost or whatever address you will be using.
* Make sure you have the hang of the SSL certificate for your local host or whatever address you will be using.
* Enable SSL on site.
* Enable SSL on site.
* Add the MDrivenServerOverride file to your App_Data.
* Add the MDrivenServerOverride file to your App_Data.
In the WebSite you will find a folder EXT_Scripts. This is where we want to keep our custom scripts.  
On the website, you will find a folder named EXT_Scripts. This is where we want to keep our custom scripts.  


Also please notice the “AppWideAngularScriptIncludes – NotInEffect.html” file. Whenever you see these “- NotInEffect” files it means that you create a copy of that file and remove the “- NotInEffect”  and it will have some meaning. The meaning of file is documented in the file you copied. 
Also, please notice the “AppWideAngularScriptIncludes – NotInEffect.html” file. Whenever you see these “- NotInEffect” files, it means that you should create a copy of that file and remove the “- NotInEffect”  and it will have some meaning. The meaning of the file is documented in the file you copied. 


This file has this content:
This file has this content:
Line 112: Line 59:
   
   
   -----------------
   -----------------
   Intension for this file: To enable you to inject scripts that executes before angular compile - thus making it possible to add directives
   Intension for this file: To enable you to inject scripts that execute before Angular compiles - thus making it possible to add directives
   
   
   Suggested usage in this file:
   Suggested usage in this file:
   
   
     -->
     -->
In this article we are going to add this to the AppWideAngularScriptIncludes file:
In this article, we are going to add this to the AppWideAngularScriptIncludes file:
  <script src="/EXT_Scripts/Board.js"></script>|
  <script src="/EXT_Scripts/Board.js"></script>|
   
   
  The application will expect to find a Board.js file in EXT_Scripts and we will get to this soon.
  The application will expect to find a Board.js file in EXT_Scripts and we will get to this soon.


  We will also need some markup to replace the Turnkey standard UI. This override markup we put in the EXT_View Folder.
  We will also need some markup to replace the Turnkey standard UI. We put this override markup in the EXT_View Folder.
[[File:Web application2.png|thumb|none]]So for things to work in my view that I will name BoardDemo I will need to add a BoardDemo.cshtml in that folder.
[[File:Web application2.png|thumb|none]]For things to work in my view that I will name BoardDemo, I will need to add a BoardDemo.cshtml in that folder.


In order to get these files to end up in the correct place but still separating them from the MDrivenTurnkey website I will create a separate project. Open a new instances of Visual Studio and create TypeScript-project (TypeScript is important since Javascript will probably make you explode of frustration):
To get these files in the correct place but still separate them from the MDrivenTurnkey website, I will create a separate project. Open a new instance of Visual Studio and create a TypeScript-project (TypeScript is important since Javascript will probably make you explode out of frustration):


[[File:Typescript.png|none|thumb]]
[[File:Typescript.png|none|thumb]]


In this project I will set up a post build event that copies the resulting BoardDemo.cshtml and Board.js files to the correct places in my Turnkey webapp:
In this project, I will set up a post-build event that copies the resulting BoardDemo.cshtml and Board.js files to the correct places in my Turnkey web app:
[[File:Webapp.png|none|thumb|663x663px]]
<blockquote>[[File:Webapp.png|374x374px]]</blockquote><blockquote>xcopy  /Y   “$(ProjectDir)BoardDemo.cshtml” “C:\CapableObjectsWush\source\StreamingApp\WebApplication2\Views\EXT_OverridePages\”</blockquote><blockquote>xcopy /E /Y  /I “$(ProjectDir)EXT_Scripts” “C:\CapableObjectsWush\source\StreamingApp\WebApplication2\EXT_Scripts”</blockquote>This way, you can separate your control development and have it in SVN or Git on its own.
<blockquote>xcopy  /Y   “$(ProjectDir)BoardDemo.cshtml” “C:\CapableObjectsWush\source\StreamingApp\WebApplication2\Views\EXT_OverridePages\”</blockquote><blockquote>xcopy /E /Y  /I “$(ProjectDir)EXT_Scripts” “C:\CapableObjectsWush\source\StreamingApp\WebApplication2\EXT_Scripts”</blockquote>This way you separate your control development and can have it in svn or git by its own.


== The model ==
== '''The Model''' ==


[[File:Image5model.png|frameless]]
[[File:Image5model.png|frameless]]


And a viewModel – called '''DemoBoard''':
And a ViewModel – called '''DemoBoard''':
[[File:Model boardlist.png|none|thumb|669x669px]]
 
[[File:Model_boardlist.png|frameless]]
 
We also add a ViewModel that we will use for editing a '''BoardCard''':
We also add a ViewModel that we will use for editing a '''BoardCard''':
[[File:BoardCard.png|none|thumb|668x668px]]


== The markup override ==
[[File:BoardCard.png|frameless|310x310px]]
== '''The Markup Override''' ==
  @{ Layout = null; }
  @{ Layout = null; }
   
   
Line 148: Line 96:
   
   
  <nowiki><style>
  <nowiki><style>
     
                 
            .boardlist {
                      .boardlist {
          vertical-align: top;
                      vertical-align: top;
        }
                    }
     
                 
        .boardheader {
                    .boardheader {
          font-weight: bold;
                      font-weight: bold;
          font-size: 1.20rem;
                      font-size: 1.20rem;
          background-color: seagreen;
                      background-color: seagreen;
          text-align:center;
                      text-align:center;
          color:white;
                      color:white;
        }
                    }
     
                 
          .card {
                    .card {
          vertical-align: top;
                      vertical-align: top;
          background-color: mediumpurple;
                      background-color: mediumpurple;
          margin: 2px;
                      margin: 2px;
          border-radius: 12px;
                      border-radius: 12px;
          border: 2px solid purple;
                      border: 2px solid purple;
          padding: 6px;
                      padding: 6px;
        }
                    }
     
                 
          .cardname {
                    .cardname {
          font-weight: bold;
                      font-weight: bold;
        }
                    }
     
                 
        .cardtext {
                    .cardtext {
          font-size: 0.90rem;
                      font-size: 0.90rem;
          word-wrap: break-word;
                      word-wrap: break-word;
        }
                    }
     
                 
        .rotateWhileMove {
                  .rotateWhileMove {
          -webkit-transform: rotate(7deg);
                      -webkit-transform: rotate(7deg);
          -moz-transform: rotate(7deg);
                      -moz-transform: rotate(7deg);
          -ms-transform: rotate(7deg);
                      -ms-transform: rotate(7deg);
          -o-transform: rotate(7deg);
                      -o-transform: rotate(7deg);
          z-index:1000;
                      z-index:1000;
        }
                    }
     
                 
        .myHorizontalbut {
                    .myHorizontalbut {
          height: 24px;
                      height: 24px;
          width: 90px;
                      width: 90px;
          position: relative;
                      position: relative;
          padding: 2px;
                      padding: 2px;
          display: inline-block;
                      display: inline-block;
          margin: 2px;
                      margin: 2px;
        }
                    }
     
                 
          </style></nowiki>  
                    </style></nowiki>  
  <nowiki><div id="theDivForTheBoard" style="height:300px;" ph-board    vmclassid="{{root.VMClassId}}">  
  <nowiki><div id="theDivForTheBoard" style="height:300px;" ph-board    vmclassid="{{root.VMClassId}}">  
        <table border="1" align ="center" style="height:100%; width:100%;">
                  <table border="1" align ="center" style="height:100%; width:100%;">
          <tr></nowiki>  
                    <tr></nowiki>  
  <nowiki> </nowiki>    <nowiki><td ng-repeat="boardlist in root.BoardLists" height="20px" width="200px"></nowiki>
  <nowiki> </nowiki>    <nowiki><td ng-repeat="boardlist in root.BoardLists" height="20px" width="200px"></nowiki>
  <nowiki> </nowiki>      <nowiki><div class="boardheader">{{boardlist.Name}}</nowiki><nowiki></div></nowiki>
  <nowiki> </nowiki>      <nowiki><div class="boardheader">{{boardlist.Name}}</nowiki><nowiki></div></nowiki>
Line 207: Line 155:
  <nowiki> </nowiki>      <nowiki><td ng-repeat="boardlist in root.BoardLists" class="boardlist" ph-boardlist vmclassid="{{boardlist.VMClassId}}"></nowiki>
  <nowiki> </nowiki>      <nowiki><td ng-repeat="boardlist in root.BoardLists" class="boardlist" ph-boardlist vmclassid="{{boardlist.VMClassId}}"></nowiki>
  <nowiki> </nowiki>        <nowiki><div ng-repeat="card in boardlist.BoardCards" class="card" ph-card>
  <nowiki> </nowiki>        <nowiki><div ng-repeat="card in boardlist.BoardCards" class="card" ph-card>
                <div class="cardname">{{card.Name}}</nowiki><nowiki></div></nowiki>
                            <div class="cardname">{{card.Name}}</nowiki><nowiki></div></nowiki>
  <nowiki> </nowiki>          <nowiki><div class="cardtext">{{card.Text}}</nowiki><nowiki></div></nowiki>
  <nowiki> </nowiki>          <nowiki><div class="cardtext">{{card.Text}}</nowiki><nowiki></div></nowiki>
  <nowiki> </nowiki>        <nowiki></div></nowiki>
  <nowiki> </nowiki>        <nowiki></div></nowiki>
Line 216: Line 164:
   
   
       <nowiki><button ng-repeat="oneaction in root.VM.StateActions() | orderBy:'+SortKey'"
       <nowiki><button ng-repeat="oneaction in root.VM.StateActions() | orderBy:'+SortKey'"
              ng-class="oneaction.Class" ng-click="oneaction.Execute(); hidemenu();"
                          ng-class="oneaction.Class" ng-click="oneaction.Execute(); hidemenu();"
              ng-disabled="!oneaction.Enable" class="myHorizontalbut">
                          ng-disabled="!oneaction.Enable" class="myHorizontalbut">
        {{oneaction.Presentation}}</nowiki>
                    {{oneaction.Presentation}}</nowiki>
  <nowiki></button></nowiki>  
  <nowiki></button></nowiki>  
   
   
  <nowiki> </nowiki>      <nowiki><img id="loadingAnimation" src="/Content/loadingAnimation.gif" scroll-position="scroll" style="margin-top: 0px;" ng-show="root.VM.Loading()" class="ng-hide"></nowiki>  
  <nowiki> </nowiki>      <nowiki><img id="loadingAnimation" src="/Content/loadingAnimation.gif" scroll-position="scroll" style="margin-top: 0px;" ng-show="root.VM.Loading()" class="ng-hide"></nowiki>  


You can just look at the page source before  overriding it  – or use <mysite>/MDriven/Development to get documentation on how we do the standard UI:
You can look at the page source before  overriding it  – or use <mysite>/MDriven/Development to get documentation on how we do the standard UI:
[[File:UI.png|left|thumb|589x589px]]
[[File:UI.png|left|487x487px|frameless]]
The markup gives this '''UI''' on data from my model:
The markup gives this '''UI''' on data from my model:
[[File:UI 2.png|center|thumb|498x498px]]  
[[File:UI 2.png|center|408x408px|frameless]]  


While working with this I can have the Visual Studio with MDriven Turnkey running  
While working on this, I can have the Visual Studio with MDriven Turnkey running and:


able to watch the result in a browser
I can watch the result in a browser.


☛ use my second Visual Studio to change the override markup  
I can use my second Visual Studio to change the override markup.


☛ compile (this will trigger my post build action), so that the files are copied into the folders of the running turnkey site
I can compile (this will trigger my post-build action) so that the files are copied into the folders of the running turnkey site.


☛ hit refresh in the browser to see the effect of my changes
I can hit refresh in the browser to see the effect of my changes.


== The script ==
== '''The Script''' ==
To get the desired behavior to drag a card from one column to another we need to add javascript. Luckily TypeScript is much easier to work with and turns into javascript upon each save. So use TypeScript do not hack javascript directly.  
To get the desired behavior to drag a card from one column to another, we need to add Javascript. Luckily, TypeScript is much easier to work with and turns into Javascript upon each save. Use TypeScript - do not hack Javascript directly.  


This is the TypeScript source for the Board.ts that will turn into Board.js on each save:
This is the TypeScript source for the Board.ts that will turn into Board.js on each save:
Line 347: Line 295:
  console.trace("this file loaded");
  console.trace("this file loaded");
  InstallTheDirective(angular.module('MDrivenAngularApp'));
  InstallTheDirective(angular.module('MDrivenAngularApp'));
The script defines an angularJS directive called “phCard” and that is used in the markup – but in markup you must write “ph-card” (this is the way of angular to ).
The script defines an angularJS directive called “phCard” that is used in the markup. In the markup, you must write “ph-card” (this is the way of angular to).
 
In short, the script hooks mouse events to allow you to move the card. Once you drop the card, the script ends up in mouse up – and here we find the list you were over on drop. We set a couple of variables and then execute the action “MoveAction”. On the server, the MoveAction-action changes the owner of the card, and the Turnkey will be notified about this change which will end up as the rendering of the card in the new column.
[[Category:MDriven Turnkey]]
[[Category:MDriven Designer]]
[[Category:Content Override]]
[[Category:Bootstrap]]
[[Category:AngularJS]]
[[Category:CSS]]
[[Category:Visual Studio]]
[[Category:MVC]]
[[Category:Case]]
{{Edited|July|12|2024}}


In short, the script hooks mouse events to allow you to move the card. Once you drop the card the script ends up in mouseup – and here we find out the list you were over on drop – we set a couple of variables – then execute the action “MoveAction”. On the server the MoveAction-action change the owner of the card and Turnkey will be notified about this change – and that will end up as rendering of the card in the new column.
[[Category:TOC]]

Latest revision as of 13:45, 26 March 2024

Boards are really popular and useful. To create more advanced UI controls in MDriven Turnkey AngularJS, you can study how this Trello-like board is constructed. In this video, we show you how to add a board to your MDriven Turnkey application and fill it with any important info in your domain model.

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.

View Model for Trello like board Board View Overriding Using Development Side Putting Styling Information Directive Scripts Mouse Down Moves Implementation Mouse Up Moves (Moving a Card) MoveAction in Details Edit Card Action

Installation

To develop something like this, it is beneficial to install MDriven Turnkey locally. This way, you can change and see the effect without flipping a lot of files back and forth.

One important ability you may want to maintain is to continue to use the data in the cloud – but still have the Turnkey web app locally. I use this ability all the time to debug and develop Turnkey. You can use it when you develop page overrides and advanced controls: MDrivenServerOverride.xml

The file MDrivenServerOverride.xml is placed in your App_Data folder and has this format:

Mdriven Server Override.png
<?xml version="1.0" encoding="utf-8"?> 
<root>  
<MDrivenServerOverride MDrivenServerPWD="----yoursecretpwd---">https://YourMDrivenTurnkeyApp.azurewebsites.net/__MDrivenServer</MDrivenServerOverride> 
</root>

Adding a file like this allows you to move where MDriven Turnkey should go to find its MDriven Server. Normally, the MDriven Server is placed in a sub-application to the Turnkey-application in a folder named __MDrivenServer (double underscores). But this way, I can use a local Turnkey app – and point out the MDrivenServer for the cloud app.

Running the MDriven Turnkey in Visual Studio

Web application.png

Download and import the MDriven Turnkey in your local IIS – or use IIS Express. Then, open the website in Visual Studio.

  • Make sure you have the hang of the SSL certificate for your local host or whatever address you will be using.
  • Enable SSL on site.
  • Add the MDrivenServerOverride file to your App_Data.

On the website, you will find a folder named EXT_Scripts. This is where we want to keep our custom scripts.

Also, please notice the “AppWideAngularScriptIncludes – NotInEffect.html” file. Whenever you see these “- NotInEffect” files, it means that you should create a copy of that file and remove the “- NotInEffect”  and it will have some meaning. The meaning of the file is documented in the file you copied. 

This file has this content: In this article, we are going to add this to the AppWideAngularScriptIncludes file:

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

The application will expect to find a Board.js file in EXT_Scripts and we will get to this soon.
We will also need some markup to replace the Turnkey standard UI. We put this override markup in the EXT_View Folder.
Web application2.png

For things to work in my view that I will name BoardDemo, I will need to add a BoardDemo.cshtml in that folder.

To get these files in the correct place but still separate them from the MDrivenTurnkey website, I will create a separate project. Open a new instance of Visual Studio and create a TypeScript-project (TypeScript is important since Javascript will probably make you explode out of frustration):

Typescript.png

In this project, I will set up a post-build event that copies the resulting BoardDemo.cshtml and Board.js files to the correct places in my Turnkey web app:

Webapp.png

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”

This way, you can separate your control development and have it in SVN or Git on its own.

The Model

Image5model.png

And a ViewModel – called DemoBoard:

Model boardlist.png

We also add a ViewModel that we will use for editing a BoardCard:

BoardCard.png

The Markup Override

@{ Layout = null; }

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

<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> 
<div id="theDivForTheBoard" style="height:300px;" ph-board    vmclassid="{{root.VMClassId}}"> 
                   <table border="1" align ="center" style="height:100%; width:100%;">
                     <tr> 
     <td ng-repeat="boardlist in root.BoardLists" height="20px" width="200px">
        <div class="boardheader">{{boardlist.Name}}</div>
    </tr>

     <tr> 
       <td ng-repeat="boardlist in root.BoardLists" class="boardlist" ph-boardlist vmclassid="{{boardlist.VMClassId}}">
         <div ng-repeat="card in boardlist.BoardCards" class="card" ph-card>
                            <div class="cardname">{{card.Name}}</div>
           <div class="cardtext">{{card.Text}}</div>
          </div>
       </td>
     </tr>
   </table>
 </div>  

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

You can look at the page source before overriding it – or use <mysite>/MDriven/Development to get documentation on how we do the standard UI:

UI.png

The markup gives this UI on data from my model:

UI 2.png

While working on this, I can have the Visual Studio with MDriven Turnkey running and:

☛ I can watch the result in a browser.

☛ I can use my second Visual Studio to change the override markup.

☛ I can compile (this will trigger my post-build action) so that the files are copied into the folders of the running turnkey site.

☛ I can hit refresh in the browser to see the effect of my changes.

The Script

To get the desired behavior to drag a card from one column to another, we need to add Javascript. Luckily, TypeScript is much easier to work with and turns into Javascript upon each save. Use TypeScript - do not hack Javascript directly.

This is the TypeScript source for the Board.ts that will turn into Board.js on each save:

//# sourceURL=EXT_Scripts/Board.js
 /// <reference path="../typings/jquery/jquery.d.ts" />


 function InstallTheDirective(MDrivenAngularApp) {
  console.trace("InstallTheDirective" + MDrivenAngularApp.toString());


  MDrivenAngularApp.directive('phCard', ['$document', function ($document) {
    return {
      link: function (scope, element, attr) {
        var startX = 0, startY = 0, x = 0, y = 0;

        element.attr("vmclassid", scope.card.VMClassId);
        let bl = scope.$parent.boardlist;

        element.css({
          position: 'relative',
          cursor: 'pointer'
        });

        var clicks = 0;
        element.on('mousedown', function (event) {
          // Prevent default dragging of selected content
          event.preventDefault(); 
          x = 0;
          y = 0;
          startX = event.pageX - x;
          startY = event.pageY - y;
          clicks++;
          if (clicks == 1) {
            setTimeout(function () {
              if (clicks == 1) {
                $document.on('mousemove', mousemove);
                $document.on('mouseup', mouseup);
                element.addClass("rotateWhileMove");

              } else {
                // double click - execute card action
                scope.card.vCurrent = true;
                var theobjectfortheboard = scope.card.VMClassParent.VMClassParent;
                scope.$root.MDrivenViewModel.Execute(theobjectfortheboard.VMClassType(), "EditCurrentCardAction");

              }
              clicks = 0;
            }, 300);
          }
        });

        function mousemove(event) {
          y = event.pageY - startY;
          x = event.pageX - startX;
          element.css({
            top: y + 'px',
            left: x + 'px'
          });
        }

        function mouseup(event) {

          $document.off('mousemove', mousemove);
          $document.off('mouseup', mouseup);
          y = event.clientY;
          x = event.clientX;
          element.removeClass("rotateWhileMove");
          let actiondone: boolean = false;

          var cardid: string = scope.card.VMClassId;
          var theobjectfortheboard = scope.card.VMClassParent.VMClassParent;
          var theboarddiv = $("[vmclassid='" + theobjectfortheboard.VMClassId + "']");
          if (theboarddiv && scope.card && scope.card.VMClassParent) {
            // find all phBoardList elements under this board in DOM
            var alltheboardlists = $(theboarddiv).find("[ph-boardlist]");
            for (let elem of alltheboardlists.get()) {
              var r = (<HTMLElement>elem).getBoundingClientRect();
              if (r.top < y && r.bottom > y && r.left < x && r.right > x) {
                let thelistWeWereDroppedIn: HTMLElement = elem;
                let vmclassid = thelistWeWereDroppedIn.getAttribute("vmclassid");
                let theobjectforthelist = scope.$root.MDrivenViewModel.GetFromVMClassId(vmclassid);
                if (theobjectforthelist !== scope.card.VMClassParent) {
                  theobjectfortheboard.MoveActionTargetList_AsExternalId = theobjectforthelist.VMClassAsExternalId();
                  theobjectfortheboard.MoveActionTargetCard_AsExternalId = scope.card.VMClassAsExternalId();
                  scope.$root.MDrivenViewModel.CallServerAction(theobjectfortheboard.VMClassType(), "MoveAction");


                  actiondone = true;
                }
              }
            }
          }

          if (!actiondone) {
            element.css({
              top: 'auto',
              left: 'auto'
            });
          }
        }
      }
    };
  }]);
 } 

console.trace("this file loaded");
InstallTheDirective(angular.module('MDrivenAngularApp'));

The script defines an angularJS directive called “phCard” that is used in the markup. In the markup, you must write “ph-card” (this is the way of angular to).

In short, the script hooks mouse events to allow you to move the card. Once you drop the card, the script ends up in mouse up – and here we find the list you were over on drop. We set a couple of variables and then execute the action “MoveAction”. On the server, the MoveAction-action changes the owner of the card, and the Turnkey will be notified about this change which will end up as the rendering of the card in the new column.

This page was edited 48 days ago on 03/26/2024. What links here