Dialog Development for 7.0

Author:JonK
Last Updated:March 27, 2019 1:13 PM

Dialog implementations are made up the following code elements:

  • Controller 
  • Dialog Definition
    • Dialog Editor
    • Dialog View Model
    • Dialog Hydrator
  • Dialog View
    • Razor
    • JS

The Wkst project in our TitanBase solution uses the "Areas" scaffolding supported by Visual Studio.  Dialogs are a specific area in the project, and thus have their own Controllers, Models, and Views folders for organizing the code. 

Controller 

Our Dialogs are loaded through an Ajax call to a Dialog Controller.  The JS Namespace that supports opening dialogs is NWS.Wkst.Dialog

If creating a new Dialog, you will need a supporting controller to host the url route you intend to call via the NWS.Wkst.Dialog.Open.   

Dialog controllers are standard controllers.  I encourage you to decorate your controller classes with two annotation developed for Titan - AjaxAuthorize and HandleDialogError

[AjaxAuthorize]
[HandleDialogError]
public class CreateController : Controller
{
	// POST: Dialog/Create/Document?parentDocID=&editName=&documentTypeID=
	[HttpPost]
	public ActionResult Document(int parentDocID, string editName = null, string documentTypeID = null)
	{
		/* implementation details */

		var data = new XElement("Data", /* package incoming data, or retrieve config data from DB */);
		var editor = new CreatePage(data);
		return PartialView(editor.ViewPath, editor.Model);
	}
}

AjaxAuthorize attribute supports communicating to our Ajax javascript that an unauthorized request error occurred, so the js can redirect to the login page.

HandleDialogError attribute supports returning Error Message dialogs in the event something unexpected occurs while trying to execute your dialog code. 

 

Dialog Definition

Definition classes are responsible for defining the hydratable View Model and associated View for a particular editor interface in Titan.   Since most Editor, ViewModel, and Hydrator classes are very simple, I encourage you to write a single .cs file that contains the three classes that make up the definition.

public class CreatePage : XmlBasedEditor, ICreateDocument
{
	public CreatePage(XElement data) : base(new CreatePageViewModel { }, data) { }
	public override string ViewPath => "~/Areas/Dialog/Views/Create/CreatePage.cshtml";

	/* ICreateDocument implementation */
}

public class CreatePageViewModel : DialogViewModel<CreatePageHydrator>
{
	/* POCO made up of CmsControlBase properties */
}

public class CreatePageHydrator : XmlBasedModelHydrator<CreatePageViewModel>
{
	public CreatePageHydrator(CreatePageViewModel model, XElement data) : base(model, data, "") { }

	public override void HydrateModel()
	{
		base.HydrateModel();
		/* Hydrate the common Dialog-specific properties */
		/* Hydrate the rest of the ViewModel */
	}
}

 

XmlBasedEditor

Most editors in Titan are going to be driven off of XML data. To simplify the development I encourage you to write Editor classes that are derived from XmlBasedEditor. This makes it possible to create a new instance of your editor, passing in the XML that supports it, and the base classes automatically handle the hydration.  

The constructor for your Editor class needs to call the base constructor, establishing a new instance of your specific ViewModel.

The ViewPath for your Editor needs to be a path into "~/Areas/Dialog/Views" or your dialog won't render. 

 

DialogViewModel<THydrator>

All Dialogs in the Workstation have common properties for specifying Title, Buttons, and Js integration.   You gain access to these properties by writing a ViewModel object derrived from DialogViewModel.  

The DialogViewModel is a generic abstract class.  When inheriting from it you need to indicate the Type for the Hydrator class so the hydration steps can be handled automatically.

Otherwise, your ViewModel is a standard simple POCO.  As with all our ViewModels, I encourage you to create properties based on CmsControlBase objects so the js framework can automatically package the data for submission.  

 

XmlBasedModelHydrator<TModel>

Once again, since most interfaces in Titan are populated using XML formatted content, the hydrators for your dialogs will need to be derived from XmlBasedModelHydrator.  This base class is also generic, and requires you to specify the Type for the ViewModel it knows how to hydrate. 

For Dialog hydration, you need to populate the Dialog-specific properties defined in the IDialog interface (implemented on the base class DialogViewModel) along with the unique properties for your ViewModel. 

Model.Title = StaticText.CreateNewPage;
Model.ExtraCssClasses = "small";
Model.Buttons = DialogButtons.CancelDone;
Model.EditorJsClass = "NWS.Wkst.Dialog.CreatePage";

Note:

  • The Title for your dialog should come from the Localization file "StaticText".  
  • The ExtraCssClasses currently available for Dialogs constrain the width of the dialog only.  Widths are defined with percentages so the dialog will be responsive. 
    • "small" = 25%  - perfect for simple, one-field dialogs
    • "medium" = 33%  - good for simple forms with less than 7 fields.
    • "large" = 66% - good for complex forms and tabbed interfaces 
  • Buttons are implemented with a Titan class called DialogButton.  Since most dialogs in Titan have the same general buttons, a static class was created to define the commonly used button collections.
    • DialogButtons.Cancel - gives you a Cancel button. 
    • DialogButtons.CancelDone - gives you Cancel and Done buttons. The Cancel can be activated using the Escape key. The Done can be activated using the Enter key. 
    • DialogButtons.Done - gives you a Done button
    • DialogButtons.NoYes - gives you No and Yes buttons. The No can be activated using the Escape key. The Yes can be activated using the Enter key. 
  • EditorJsClass defines the JS class that contains the public functions that will be wired to the buttons. 

The XmlBasedModelHydrator base class provides a simple API for retrieving content from XML via XPath strings:

  • ExtractValue - returns the string content of an element matching an XPath query
  • ExtractElements - returns an enumerable of XElements matching an XPath query
  • Exists - boolean indicating whether an XPath query produced a node-set

 

Dialog View

At the path defined in your Editor, you need a standard Razor View that specifies as its Layout "~/Areas/Dialog/Views/Shared/EditorTemplates/DialogEditorBase.cshtml".  This layout provides the necessary HTML framework for the workstation to understand and wire-up your dialog. 

@using NorthwoodsSoftwareDevelopment.Cms.Wkst
@model CreatePageViewModel

@{
	Layout = "~/Areas/Dialog/Views/Shared/EditorTemplates/DialogEditorBase.cshtml";
}

@section CmsControlJs {
	@Html.RegisterJsForView("NWS.Wkst.Dialog.CreatePage.Init")
}

@section Footer {
}

@Html.EditorFor(model => model.ParentDocID)
@Html.EditorFor(model => model.EditName)
@Html.EditorFor(model => model.DocumentTypeID)
@Html.EditorFor(model => model.ParentName)

Two optional sections are available for your presentation code. 

  • CmsControlJs - Optional... but not really.  It is common in dialog views to also have a JS file that supports the functionality of the interface. You'll want a JS file with the same name as your Razor file, and then a line of code in the CmsControlJs section to call the Titan HtmlHelper method "RegisterJsForView". While technically optional (because you might chose to implement it differently), this is the way the system expects you to provide the js functions to wire-up the dialog buttons.
  • Footer - Truely optional. In some of our wireframes, we show dialogs that have a small footer area with contextual content. The Footer section supports the placement of that content.

 

top