To build an organistional webapp, there are a number of standard steps that you need to go through to ensure the project and outcome (as designed) are as expected.
Now that you have your design complete and agreement to move ahead with the build, the following will guide you through some standard steps that will help deliver the webapp as expected.
In this guide we will use the same example organisation as used in the designing quick start.
Collaboration
Set up conversation in console.mydigitalstructure.cloud to manage all the team conversations and links to key documents etc.
Use GSuite to manage documents, including those with client - get them to use comments.
If working in team the divide the work up by User Role.
Use project check-in meetings (in-person or online) to communicate progress, discuss design and an issues.
Example Project Check-In Document
Example Testing Tracking Sheet
Planning
Use Planning template (link below) to capture all the tasks required to complete the build based on the design document.
Take each of the user role processes and classifying by effort (Skill): Basic (1 hour), Standard (4 hours), Complex (8 hours) and units of work required.
This will result in a total hours for the project.
Example Planning Sheet
Agreement
Get agreement with the client on cost, timeframes and work required by them.
Example Agreement
The Build
Zones;
Using console.mydigitalstructure.cloud;
- Create a lab space
- Create User Roles to match design
- Create a User for each of the User Roles
Create the form foundation of the webapp;
Using the project costing sheet as a reference (which was based on the design).
- In the layout file (1901.layout...) set up the side menu.
-
Create the form (HTML) file for each of the user-roles.
Use the filename format [siteid].[user-role]-[version number]-url_[user-role*].html
eg 1901.customer-1.0.0-url_customer.html
* if the role is more than one word then use a hyphen eg customer-service
The url_ part makes it easier to update the webapp pages, which is covered later.
- In each of the files create the base form using the process in the design and comment in the details for each process
You now have the form foundation done!
Create the function foundation of the webapp;
Again using the project costing sheet as a reference (which was based on the design);
-
Create a file for each of the user-role process groups (ie as per the menu items in side layout)
Use the filename format [siteid].[user-role].[process-group]-[version number].js
eg 1901.customer.view.menu-1.0.0.js
-
In each of the files add code to add the controllers to the webapp as per the processes above.
[show image of example]
You now have the function foundation done!
Set up the app foundation;
In "app.js".
Item |
Description |
Standard controllers |
app-init |
Initialise the app; invoke mydigitalstructure.init with options:
-
viewStarting: (formerly viewStarted) Controller that is invoked before authetication to mydigitalstructure.cloud.
-
viewStart: Controller that will be invoked once authetication to mydigitalstructure.cloud complete - typically 'app-start'.
-
viewUpdate: Controller that is invoked with a message based on mydigitalstructure SDK activity. See View update messages section below for more details.
-
viewNavigation: Controller that is invoked when user navigates to new view.
-
views: Collection of view definitions.
-
viewAssemblySupport: Add standard controllers to the app namespace. Default true.
|
app-starting |
Invoked before authentication on mydigitalstructure.cloud |
app-start |
Invoked after authenication on mydigitalstructure.cloud, isLoggedOn: is passed to the controller. |
app-updated |
Invoked with messages from the mydigitalstructure SDK (see below) |
app-navigation |
Invoked when user navigates to new view can then update navigation elements based on data ie badges or system data like user roles ie update menu options. |
|
options |
httpsOnly: |
If request is http, webapp will automatically redirect to https. Optional.
|
container: |
The main container for the webapp. Used with views.
|
registerDocument: |
The ID of a public document that will be used as email template sent to a user when they register. Optional.
Use mydigitalstructure.cloud template tags [[TEMPLATE_LOGONNAME]] & [[TEMPLATE_INITIALPASSWORD]] to send logonname and password.
|
startURI: |
eg /app
|
startURIContext: |
eg #dashboard
|
authURI: |
eg /auth
|
authURIContext: |
eg /#auth
|
authURIIsHome: |
If URI is / then act as though the authURI setting. Typically set to true. Optional.
|
spinner: |
HTML for spinner eg '/site/1789/1789.working.gif'. Optional.
|
working: |
eg. '<div class="m-a-md text-center"><img src="/site/1789/1789.working.gif" style="height:25px;"></div>' Optional.
|
rows: |
eg 50
|
namespace: |
eg 'app'
|
logonSuffix: |
To ensure logon names are unique across the mydigitalstructure.cloud platform. Typically '@[site]', eg '@1234'.
To logon name is stored as say jane@smith.com@1234, but the user enters as jane@smith.com mydigitalstructure then manages the adding and removal of the logonSuffix.
|
styles: |
{ button: , buttonDefault: }
|
password: |
eg { minimumLength: 7 }. It should manage the setting on mydigitalstructure.cloud.
|
routing: |
Routing rules that control which views (uri + uriContext) are available when page refreshed etc. See below for more details.
|
editor: |
Options to use with rich text editors eg tinyMCE.
|
dateFormats: |
Set of date formats for use by momentjs. If not set it defaults to ['DD MMM YYYY', 'D MMM YYYY', 'D/MM/YYYY', 'DD/MM/YYYY', 'DD MMM YYYY HH:mm:ss']
|
|
Routing rules |
Item
|
Description
|
toURI
|
A set of rules that determine if webapp can route (navigate) to a uri (view)
-
uri: eg '/website'
-
uriContext: Can be many eg ['#website-dashboard']
-
onlyApplyIfURIDataContextNotEmpty: Rule is only applied if data context (passed with URI) is set ie /website/#website-dashboard/1234 where 1234 is he data context.
-
applyEvenIfReload: Rule is only applied if reload ie user has clicked the web-browser refresh button.
|
toStart
|
A set of rules that determine if webapp routes back to the start uri & uri context, as set in options when mydigitalstructure.init invoked.
-
uri: eg '*'
-
uriContext: Can be many eg ['*']
|
|
View update messages |
From
|
Status
|
Message
|
myds-reset
|
error
|
New passwords do not match
|
myds-reset
|
error
|
New password can not be blank
|
myds-retrieve
|
error-internal
|
No object
|
myds-init
|
start
|
|
myds-init
|
end
|
|
myds-init
|
uri-changed
|
# of the new location eg #app-actions
|
myds-logon-init
|
start
|
|
myds-logon-init
|
end
|
|
myds-logon-init
|
need-code
|
|
myds-logon-init
|
code-sent
|
|
myds-logon-init
|
error
|
There is an issue with your user account ([Error Notes])
|
myds-logon-init
|
error
|
There is an issue with your user account ([Error Notes])
|
myds-logon-send
|
start
|
|
myds-logon-send
|
request-start
|
|
myds-logon-send
|
request-start
|
|
myds-logon-send
|
end
|
|
myds-logon-send
|
password-expired
|
|
myds-send
|
start
|
|
myds-send
|
end
|
|
myds-send
|
error
|
Varies
|
myds-send
|
notify
|
Varies
|
myds-auth
|
error
|
not-authenticated
|
myds-view-access
|
context-changed
|
{hide: elements, show: elementsShow}
|
myds-register-space
|
start
|
|
myds-register-space
|
end
|
|
myds-register-templates
|
initialised
|
|
myds-register-app
|
start
|
|
myds-register-app
|
end
|
|
myds-user-password
|
start
|
|
myds-user-password
|
end
|
|
myds-util-location
|
end
|
|
myds-util-attachments-upload
|
start
|
|
myds-util-attachments-upload
|
end
|
|
myds-util-attachments-upload
|
error
|
mydigitalstructure.cloud error message
|
myds-auth
|
error
|
not-authenticated
|
myds-core
|
error
|
There is an error with this app
|
myds-core
|
error
|
mydigitalstructure.cloud error message
|
|
User Interface/Experience;
Item |
Description |
Design Template |
|
Icons |
Fontawesome
|
Dates/time |
Use momentjs to work with dates and times.
|
Numerals |
|
Utility functions |
Use lodash to add useful utility functions. eg _.each() to interate through and array.
|
PDF Creation |
Use pdfmake to create PDFs within the browser.
|
Text editor tips |
As per the quick start guide suggest using sublime, but can use what every you prefer eg VSCode.
Suggest using two vertical windows;
-
One for form (HTML) coding
-
One for function (JS) coding.
|
mydigitalstructure Util SDK;
Item |
Description |
Data caching |
app.set |
Store data in the local app data store at three logical levels; scope, context (optional) & name (optional).
eg app.set({scope: 'myscope', context: 'mycontext', name: 'myname', value: 'myvalue'});
|
app.get |
Retrieve data stored in the local app data store at three logical levels; scope, context (optional) & name (optional).
eg var myvalue = app.get({scope: 'myscope', context: 'mycontext', name: 'myname''});
|
app.find |
Find data in local cache.
var data = app.find(
{
dataController: 'certification-body-suppliers',
dataContext: 'all',
controller: 'certification-body-supplier-edit',
context: 'id'
});
|
|
View updating |
app.refresh |
Merge form (HTML stored with id = "_[scope]" and class="d-none myds-view-template") template with data, hide/show elements, initiate date pickers.
eg app.refresh({scope: 'myscope', data: mydata, hide: '#myelement'});
|
app.show |
Dynamical create form (HTML) within existing form (HTML).
eg app.show('#myelement', 'Some HTML');
|
|
View updating using the view queue (app.vq)
|
Overview |
Use the view queue to manage more sophisticated changes to the view (ie form changes).
You can set up a template (via code or of as HTML (Form) templates) and merge it with data (JSON). ie if need to display rows of the same data.
|
Example Uses |
-
No template;
-
Initialise queue with;
app.vq.init('#mycontainerelement', {working: true, disableSelector: '#mybuttonelement'})
-
Add content;
app.vq.add('<div>Hello</div>');
-
Add content;
app.vq.add('<div>World!</div>');
-
Render content;
app.vq.render('#mycontainerelement', enableSelector: '#mybuttonelement');
-
With template & single data object;
-
Initialise queue with;
app.vq.init('#mycontainerelement', {working: true, disableSelector: '#mybuttonelement'})
-
Add content;
app.vq.add('<div>Hello</div>');
-
Add template;
app.vq.add('<div>{{name}}!</div>', {type: 'template'});
-
Add content;
app.vq.add({useTemplate: true}, {"name": "Jane"});
-
Render content;
app.vq.render('#mycontainerelement', enableSelector: '#mybuttonelement');
-
With template & array of data objects;
-
Initialise queue with;
app.vq.init('#mycontainerelement', {working: true, disableSelector: '#mybuttonelement'})
-
Add content;
app.vq.add('<div>Hello</div>');
-
Add template;
app.vq.add('<div>{{name}}!</div>', {type: 'template'});
-
Data;
var rows = [ {"name": "Jane"}, {"name": "John"} ]
-
Loop through data & add content;
_.each(rows, function (row) {app.vq.add({useTemplate: true}, row)});
-
Render content;
app.vq.render('#mycontainerelement', enableSelector: '#mybuttonelement');
|
app.vq.init(selector, options) |
Initialise the queue ready to update the form of the view.
options:
-
clear: clear the view queue
-
disableSelector: elements that match the selector will have the disabled class added to them.
-
enableSelector: elements that match the selector will have the disabled class removed from them.
-
queue: which queue is it, can be left blank and default queue will be used. Use if building up multi-view form changes at the same time or sub view queues that will get merged into a main view queue.
-
setDefault: set this queue as the default queue.
-
selector: which elements to change the form for.
-
working: show the working image in elements that match the selector
eg app.vq.init({...});
|
app.vq.reset(options) |
Reset the view queue include the default queue.
eg app.vq.reset({...});
|
app.vq.clear |
Clear the view queue include the default queue.
-
type: 'content' or 'template'.
-
queue: which queue is it, can be left blank and default queue will be used. Use if building up multi-view form changes at the same time or sub view queues that will get merged into a main view queue.
-
clearDefault: clear the default queue setting.
-
preserve: default is false, set to true if just want to clear the default setting.
eg app.vq.clear({...});
|
app.vq.add(content, options) |
Add to the view queue.
options:
-
type: 'content' or 'template'.
-
queue: which queue is it, can be left blank and default queue will be used. Use if building up multi-view form changes at the same time or sub view queues that will get merged into a main view queue.
-
clear: clear the queue before adding the content (default false). Adding a template type content always clears the queue of templates, as there can only be one template. If need another template then need another queue.
-
useTemplate: when adding content to the queue merge it with the queues template. ie when going through the an array of data returned from mydigitalstructure.cloud. Template uses {{ }} as placeholders for the field eg 'Name: {{name}}.' In this case the content is the JSON data.
-
ifNoneContent: set content to this, if it is undefined
-
selector: when adding template content, if content is blank then use the HTML based on this selector.
eg app.vq.add({...});
|
app.vq.render |
Render a template.
-
type: 'content' or 'template'.
-
queue: which queue is it, can be left blank and default queue will be used. Use if building up multi-view form changes at the same time or sub view queues that will get merged into a main view queue.
-
template: when adding content to the queue merge it with the queues template. ie when going through the an array of data returned from mydigitalstructure.cloud. Template uses {{ }} as placeholders for the field eg 'Name: {{name}}.'
-
data: JSON data to be merged into the template.
-
selector: element(s) to have their form (HTML) updated.
eg app.vq.templateRender({...});
|
app.vq
.templateRender
Helper function for simple one step template render.
|
Render a template.
-
type: 'content' or 'template'.
-
queue: which queue is it, can be left blank and default queue will be used. Use if building up multi-view form changes at the same time or sub view queues that will get merged into a main view queue.
-
template: when adding content to the queue merge it with the queues template. ie when going through the an array of data returned from mydigitalstructure.cloud. Template uses {{ }} as placeholders for the field eg 'Name: {{name}}.'
-
data: JSON data to be merged into the template.
-
selector: element(s) to have their form (HTML) updated.
eg app.vq.templateRender({...});
|
app.vq.focus |
Set focus on a form element.
-
selector: container to set focus onto first input element or element with class myds-setfocus.
eg app.vq.render([selector]);
|
|
Handlers
Based on class or id
The handlers code is in mydigitalstructure.util.
|
#myds-logon |
Logs on to mydigitalstructure.cloud. It uses input elements with ids #myds-logonname, #myds-logoncode & #myds-logonpassword to get values. Then enter key also initiaties log on. |
#myds-register |
Registers a useron to mydigitalstructure.cloud. It uses input elements with ids #myds-spacename, #myds-firstname, #myds-surname, #myds-email & #myds-notes. |
myds-click |
Invoke a controller based on click. Set data-controller. |
myds-navigate |
.myds-router; span.dropdown-text (BS4), set the URL hash based on scope (controller) or target |
myds-text-select |
|
myds-select |
change; controller, scope, context, clean; invoke controller; |
myds-dropdown |
On dropdown list item; invokes the controller and sets the parent text on click of item in list; bootstrap dropdown |
myds-show, myds-invoke |
|
myds-close |
Close a pop over |
myds-export |
Export a table, created using controller util-view-table; context, filename, container, scope |
myds-list |
On item; when clicked set it to Active and others not. Invoke controller. |
myds-check |
On item; when clicked set it to Active and others not. Invoke controller. |
myds-text |
Key up; scope, context, enter="stop", clean; invoke controller if no key up for 500ms. |
myds-date |
change and clear date; enter="stop", scope, context; invoke controller if no key up for 500ms. |
myds-text-select |
focusout, change; set data and element data-id. |
myds-focus |
focusout; scope, controller, context, clean; invoke controller. |
myds-change |
change; scope, controller, context, clean; invoke controller. |
myds-sort |
click; sort, sortDirection, scope, controller, context, clean; invoke controller. |
myds-validate |
focusout, keyup; scope, validate-controller; invoke validate-controller. See Validation section |
myds-menu |
a.click; metisMenu; set active; ; invoke controller; |
myds-tab |
a.click, show, shown;bootstrap tab & pills; set active; invoke controller |
myds-modal |
show, shown; bootstrap modal; set active; invoke controller |
myds-collapse |
show, shown; bootstrap collapse; set active; invoke controller |
myds-popover |
show, shown; bootstrap popover; set active; invoke controller |
myds-carousel |
slide, slid; bootstrap carousel; set active; invoke controller |
myds-more |
click; show more rows for table built using controller util-view-table. |
myds-page |
click; show a page for table built using controller util-view-table. |
|
Object schema |
learn.mydigitalstructure.cloud/schema
|
Debugging |
Use core_debug_log and console.mydigitalstructure.cloud > DEBUG LOGS |
Object structures |
You can add fields to existing objects or create your object structure. To access your own (private) fields start with any underscore eg _myfield. Use console.mydigitalstructure.cloud > STRUCTURES.
|
Validation |
mydigitalstructure has some inbuilt validation functionality.
Assuming element already has data-scope & data-context set.
The first thing is to add the myds-validate class to the element.
Then add attributes to the element;
data-validate-mandatory |
Add to element if want error if no value
|
data-validate-minimum-length |
Set a validation error when length of text is below this value. eg data-validate-minimum-length="10"
|
data-validate-maximum-length="[number]" |
Set a validation error when length of text is above this value. eg data-validate-maximum-length="50"
|
data-validate-numeral |
Set a validation error if not a number. eg data-validate-numeral
|
data-validate-numeral-minimum="[number]" |
Set a validation error if number and is below this value. eg data-validate-numeral-minimum="20"
|
data-validate-numeral-maximum |
Set a validation error if number and is below this value. eg data-validate-numeral-maximum="100"
|
data-validate-date |
Set a validation error if not a valid date. eg data-validate-date
|
data-validate-email |
Set a validation error if not a valid email. eg data-validate-email
|
Set data-validate-controller to have it invoked on keyup & focusout. Validation status is passed to controller.
And use app.get[{scope; [scope]}] to get the latest validation status
Class myds-validate-error is added to the element mydigitalstructure.util-x.x.x.css has this styled as a red border for the element. You can set your own styling for this class. You can also use the validation controller to add other visual elements/
You can also manually invoke a validation check using the util-validate-check controller. ie before saving of a new record.
eg app.invoke('util-validate-check', {scope: 'contact-edit-'});
Options:
- If you invoke it with no options it will check any visible element with class myds-validate
- If want to restrict the selection then:
selector: ie \u0091#contact-edit-surname
scope: and/or context: - this will automatically include the myds-validate class in the selector
|
Utility controllers |
If mydigitalstructure.init option viewAssemblySupport: is set to true some useful controllers will be add to your app eg util-view-table.
util-view-table |
Create a table based on data stored on mydigitalstructure.cloud using the options:
-
object: mydigitalstructure.cloud data object.
-
container: ID of HTML element to form the table.
-
context: Unique context of the table, eg action-dashboard.
-
onComplete: Controller to invoke when table has been formed.
-
filters: mydigitalstructure.cloud query language filter.
-
customOptions: mydigitalstructure.cloud query language custom options.
-
options: noDataText:, rows:, orientation:, progressive:, class:, deleteConfirm: { text: 'Are you sure you want to delete this auditor?', position: 'left' }
-
format: row: { class:, data:, controller: }, fields: { caption: field:/name:, sortBy:,defaultSortDirection:, class:, data:, exportName:, fieldsList:, html: }.
|
util-view-select |
Create an advanced select element using select2 component.
|
util-on-complete |
Invokes the controller named in parameters object as onCompleteController: .
eg app.invoke('my-controller', {onCompleteController: 'my-other-controller'});
If onCompleteController: is not present and onCompleteControllerWhenCan: is, then it will be invoked.
|
util-view-reset |
Resets input elements in a view with classes; myds-text, myds-check, myds-data, myds-data-view.
-
If
scope: of the container that contains the input elements.
-
If
data: is true then data scope is also cleared.
|
util-attachment-check |
Check attachment file sizes before uploading to mydigitalstructure.cloud.
|
util-user-switches |
Show list of spaces that user can switch into.
|
util-user-switch-to |
Switch into a space.
|
|
Drop downs |
Use class myds-dropdown. Can also use autocomplete.
|
Creating form dynamically |
Function creating form - working with static form driving function
|
Importing |
controller: util-import-upload-attach
|
Deleting |
Use popover as a confirm.
|
Saving |
Use data scope; it only updates when it changes.
If new data, then need to set defaults.
|
bootstrap 4;
Item |
Description |
Classes |
Common classes used in webapps:
m- |
Used to set margins ie space around an element
|
p- |
Used to set padding ie space within an element
|
|
Next stage of building;
Item |
Description |
Sequence |
-
Do form (HTML) then function (JavaScript).
-
For form do the dashboards first
-
Do a first parse of form & function and use
//TODOs as placeholders.
|
Use form templates |
_[template name] and the .refresh() method to blend with data.
app.view.refresh(
{
scope: 'certification-body-supplier-edit',
selector: '#certification-body-supplier-edit',
data: data
});
|
Data getting |
Is it in the first get and can be merged with template {{}} or in a secondary get and set later |
Mindset tip |
Where is the form, where is the function? |
Updating the form (HTML) |
If you include the -url_ directive in the name of your file the page you create using console.mydigitalstructure.cloud with the matching url will be automatically updated eg:
-
If you create a page with url
/app .
-
And then set the file name as say [site].app-x.x.x-url_[url].html, eg 1234.app.1.0.0-url_app.html, the page with url /app will be updated.
|
Developing locally |
You can develop on your device, i.e. without having to upload the HTML, CSS & JS to the cloud, by using mydigitalstructure-local.
|
Testing
Testing to ensure the webapp is as per the design.
Item |
Description |
Initialise |
Use the lab space to imitialise data and then give access to the client.
|
Types of testing |
There two types of testing:
-
By you as the builder; as part of the building process and alignment with the design documentation.
-
By the client; to ensure it meets their expectaitons in regards to user experience ie the form is usable and it functions as expected. Based on the design document they need to work through the webapp and feedback into the issues sheet. Typically this is referred to as User Acceptance Testing (UAT)
|
Testing by you as the builder |
Use the Developer Tools in Chrome (or equivalent in your preferred browser). mydigitalstructure SDK sends messages to the console ie if it can't find a controller that it has been requested to be invoked.
Issues can also been logged on mydigitalstructure.cloud and access used console.mydigitalstructure.cloud > LOGS.
|
Testing by the client |
The client will use user accounts for each ofthe different user-roles to test form & functionality eg the user experience.
|
Testing tracking |
|
Go Live
The webapp (or part of it) is now as per the design and client has agreed for it go live!
Item |
Description |
Concepts |
In addition to the lab site that has it's own space to contain it's test data there are two more sites set up in the production space:
- Next; The next site is a production ready user interface based on the tested lab version. It uses the production data and is subject the same security zoning. It allows the client testers to experience the site before it goes live. A hard to guess URL should be used for this site. console.mydigitalstructure.cloud can help with this.
- Live; The live site is the one that the general users access. eg https://clientabcapp.mydigitalstructure.cloud
This type of set up is referred to "Blue / Green" - it allows for seamless upgrading and continous incremental improvement, as it is a matter of simply switching the live URL and next URLs between the two sites. It results in zero downtime for updates, meaning they can be more frequent as the risk is reduced significantly.
|
Set up |
Create the production space and set up security etc
Set up the two sites (as a copy of site lab), import system data and user data.
Set up URLs.
|
Access URLs |
You can use a mydigitalstructure.cloud URL or use your own domain with SSL which support@mydigitalstructure.cloud will help with setting up for you.
|
Life after Go Live
Once a site "Goes Live", there will be incremental improves using continous incremental improvement - following the same process as the lead up to "Go Live":
- Continue build & test work in the lab space.
- Once tested OK and client test OK, move the lab code to the next site.
- Once tested OK and client test OK in the next and client agrees, switch the live URL to this site and the next site URL to what was the live site.
- Do not update the next site until you are sure the general users are operating OK on the new live, just in case you need to roll-back. If you want to preserve this site, you can simply create another site as the new next site with a unique URL.
Done!
What next?
Need help with any part of the build or costing it, then email us @ hello@mydigitalstructure.cloud