Tab Development for 7.0

Author:JonK
Last Updated:April 12, 2019 3:15 PM

Tab implementations are made up the following code elements:

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

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

Controller 

Tabs are loaded through an Ajax call made by the Workstation JS framework when a Tab is selected.  The JS Namespace that supports Tabs is NWS.Wkst.Tabs

If creating a new Tab, you will need a supporting controller with the Index method to host the url route you intend to call.  For all Tabs, the parameter "string[] items" will be passed and contains the list of DocID values that were checked in the Workstation.

Tab controllers are standard controllers.  I encourage you to decorate your controller classes with AjaxAuthorize, which supports communicating to our Ajax javascript that an unauthorized request error occurred, so the js can redirect to the login page.

[AjaxAuthorize]
public class PropertiesController : Controller
{
	// POST: Tab/Properties
	[HttpPost]
	public ActionResult Index(string[] items)
	{
		/* implementation details */

		var data = /* retrieve data from DB */;
		var editor = new PropertiesEditor(data);
		return PartialView(editor.ViewPath, editor.Model);
	}
}

 

Tab Definitions = Editor, ViewModel, Hydrator

Defining a Tab for the workstation requires three C# classes.  As is common in MVC, you need a simple ViewModel class that defines properties used by a View.  Additionally, we follow a hydrator pattern, and as a result you need to write a Hydrator class. 

The third class is a unifying object that we call an Editor. It encapsulates the creation of the correct ViewModel object and the Hydration of that model from provided data.

If possible I encourage you to write a single .cs file that contains the three classes that make up the definition.  In some cases however, the Tab ViewModel and hydrator are lengthy code files, and it will be helpful to break them up into separate .cs files.

public class PropertiesEditor : XmlBasedEditor
{

	public PropertiesEditor(XElement data) : base(new PropertiesViewModel(), data) { }
	public override string ViewPath => "Properties";
}

public class PropertiesViewModel : TabViewModel<PropertiesHydrator>, IHasCustomControls
{
	/* POCO made up of CmsControlBase properties */
}

public class PropertiesHydrator : TabModelHydrator<PropertiesViewModel>
{
	public PropertiesHydrator(PropertiesViewModel model, XElement xmlDoc) : base(model, xmlDoc, "/Properties") { }

	public override void HydrateModel()
	{
		base.HydrateModel();
		/* Hydrate the common Tab-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/Tab/Views" or your Tab won't render. 

 

TabViewModel<THydrator>

All Tabs in the Workstation have common properties for specifying whether the Tab should render for a "muti-edit" scenario (IsMultiEdit) and any extra CSS classes to add to the tab container div (ExtraCssClasses).   You gain access to these properties by writing a ViewModel object derrived from TabViewModel.  

The TabViewModel 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.  

 

TabModelHydrator<TModel>

As is usual in Titan, we're populating with XML.  Tabs are slightly unique and require you to derive from TabModelHydrator.  Under the covers this is still an XmlBasedModelHydrator, and you have access to the same helpful extractor methods. 

For Tab hydration, the base class handles setting IsMultiEdit by reading the XML data,  and looking for the TotalDocs attribute. you need to populate the Tab-specific property defined in the ITabEditor interface (implemented on the base class TabViewModel) along with the unique properties for your ViewModel. 

// From TabModelHydrator.cs the override of HydrateModel reads the incoming xml data

Model.IsMultiEdit = Exists("/@TotalDocs[.>1]");

 

The TabModelHydrator class derives from XmlBasedModelHydrator and 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

 

Tab View

Views for Tabs follow the MVC View location and naming conventions.  For example, by specifying a ViewPath of "Properties", the system will probe the common view locations and locate "~/Areas/Tab/Views/Properties/Properties.cshtml". 

@using NorthwoodsSoftwareDevelopment.Cms.Wkst
@model PropertiesViewModel

@Html.DisplayFor(model => model.GeneralPropertiesHeader)
@Html.EditorFor(model => model.NavDisplayName)
@Html.EditorFor(model => model.LinkParameter)
@Html.EditorFor(model => model.UrlAliases)
@Html.EditorFor(model => model.StartDate)
@Html.EditorFor(model => model.EndDate)
@Html.EditorFor(model => model.IsHiddenFlag)

...
top