Stranger Forms

The Upside Down of customising Umbraco forms.

Story by Rebecca King, Published 21 Nov, 2017
8 minutes to read

Why call this the Upside Down of forms you ask? Read on and you shall discover.

Recently I was asked to modify an existing form for a client. Ok, so I haven’t had too much experience with forms, so naturally, I thought, this will be a quick and easy job… Boy, was I wrong!

The mission was as follows:

Fix the form so that the user could select a sub category within a category and when the form was approved, the details entered in the form would be posted under the selected sub category. Simple right??

form showing category with sub category

Much like Will, I was lost and stumbling around in scary and unfamiliar surroundings, not really knowing where to start. I did some research at the Upside-Down town library (aka Google) and picked up a few good snippets of what I could do, but nothing that really outlined the process, no documentation that really was of any help at all.

I had managed to work out the basic objectives to complete my mission.

Objective 1: Create a custom Pre-Value Data Source that would allow the client to select the Node to start at, the Category document type and a Sub Category document type.

Objective 2: Create a custom view that would display the selected Pre-Values from the data source.

Objective 3: Create a custom Data Type for use on the form.

Objective 4: Working out a way to use the selected sub category as the publishing node id.

Objective 5: Creating a custom Work Flow to allow for the data to be displayed correctly both in the backoffice form entries and in the email.

Let's start at the top:

Objective 1 – Custom Pre-Value Source

Ok, I thought, this can’t be hard, I have access to, I’ll just have a look at the documentation there to see how to create one…

prevalue source types

WHAT??? That can’t be it surely??? I needed a view master, not an etcha sketch!!! Ok, surely someone else will have had to create a custom Pre-Value Source. Skip forward several wasted hours searching through the Upside Down library, getting a cup of tea and thinking about chocolate.

Hang on, the current Pre-Values are created already in code, what if I used Dot Peek to disassemble the Umbraco Forms classes to see how it is all done. (Yes, I could have gone to git, looked at all of the code there, but let’s be honest, Dot Peek is great for searching and navigating through assemblies). And hence the Upside Down of forms was unleashed.

Back to our objective. Now I knew that in order to create my own custom Pre-Value Source, I needed a class that inherited Umbraco.Forms.Core.FieldPreValueSourceType. Ok, that was a big leap forward, now what?? My source is going to need the category root node, the document type for the main categories, the document type for the sub category. Surely this won’t be difficult?

Yep, it was. What do these Attribute.Settings need to be? What view do I use, do I have to create my own? Will the existing views work? How to I reference them?

I got there in the end… It turns out you can use the pre-existing views or you can create your own. These views are located in App_Plugins/UmbracoForms/Backoffice/Common/SettingTypes. I’m providing this for you now because it took me ages to track them down.

form setting types

Now, I need to create the constructor for the class and override some methods to make this work.

constructor for the categories from document class

So far, so good.

validation settings method

Wait, I think I might have this!!!! Now all I have to do is override the GetPreValues method and I’m home sweet, just like Will in the last episode of season 1.

Ok, at this point I’m thinking great, I need to just get the children of the root node where the document type alias is of the main category document type, save it as the Id of the PreValue type and then the Value can be an IEnumerable of type PreValue. Wrong!!!! The PreValue definition is

 prevalue class definition

Back to the etcha sketch… Ok, so Json is a string, I’ll just put the subcategory in the value as a Json string. Problem solved.

Get prevalues method with Json string

Objective 1 is complete.

Objective 2: Create a custom view to make the data source values look good.

 I can taste that sweet, sweet victory!! Compile and run and looking good. Just when I feel like Eleven breaking free of her “imprisonment”, the rendering looks awful!!

Render view of subcategory 2 values

However, buried in the App_Plugins/UmbracoForms/Backoffice/PreValueSource/edit.html file is the rendering for this page and not wanting to customise this in any way because of future upgrades, the decision was to leave it the way it was. It’s still readable. Ok, so I failed this objective of the mission, but it wasn’t a complete loss as I did work out how to do it, and chose not to for the future of the world.

Objective 3: Creating a custom field that utilises the Pre-Value source.

After my previous loss, I was hopeful that the creation of the field might prove to be an easy task. So back to Dot Peek I went scouring the code for clues as to how to create a field. I was able to find some better information on for the field types, so I was now feeling like I could do this.

Firstly, I needed to create a new class that inherited from Umbraco.Forms.Core.FieldType

forms field type class

Objective 3 nearly complete, all I have left to do is to ensure that the categories render for the backoffice user to see. In order to do this a html file needed to be created in the App_Plugins/UmbracoForms/Backoffice/Common/FieldTypes named after the class – singleChoiceWithSubheading.html, add some angularjs (not my specialty, but I can manage it, so I thought) and away we go.

After a full day trying to get the Json string to convert (Yes, I tried EVERYTHING. Yes, I had another dev look at it also, if you can solve it, please let me know). I ended up just doing the following.

Field type view with angular code

field type render in form

You will also need to create a file in Views/Partials/Forms/FieldTypes/FieldType.SingleChoiceWithSubheadings.cshtml this will be the view of the field that the front-end user will see when the form is rendered.

field type front end render view

Feeling a little disheartened after not fully completing my last 2 missions, I tentatively stepped into Objective 4.

Objective 4: Set up the mapping in the workflow and set the publishing node to whatever category the user selected on the form.

I did a lot of searching for this one!!! So to save you the time and headache, here it is in two simple words – Magic Strings. Jump onto and search for it. In a nutshell, you use the field alias inside curly braces { } In the workflow, you can add the field name like so

forms magic strings example

Objective 5: Creating a custom work flow to enable correct display of the data in both the backoffice and for emails.

Well after my success with the last objective, finally my confidence was returning. Now for the last step. I know I could do it as I was getting a far better understanding of how this was all hanging together. I had made many, many iterations and trials of different ways of doing things. Anyway, let’s get to it.

Having learned many things from objective 1, I knew that creating a custom email workflow wouldn’t be too hard, in fact I really had to just copy an existing one with only a slight modification that would grab the node id from the custom field created above, look up the name of the content based on the id and insert it into the outgoing email. Here is what I came up with, without much trouble. It only took a couple of goes to get the xpath syntax correct.

custom workflow type class
workflow type class
workflow type class

With that all working a treat, the final step was to get the category node id to display with the category name in the backoffice in the entry like so…

backoffice category submission view

Remember, all that is in the database is the node id. This took a little working out to do, but I finally worked it out. Here is what I had to do. Create a file in App_Settings/UmbracoForms/Backoffice/Common/RenderTypes/categories.html

In my field type, I set the RenderView to “categories” and this is how the magic works. My categories file looks like this.

categories render view

Now, I was feeling REALLY confident and looking at the existing file structures, I assumed that the controller would go where the file.controller.js file was located (in RenderTypes in the Controller folder). DO NOT be fooled by this like Dustin was fooled, the controller code must go in the umbraco.forms.js file!

categories angular controller

And there you have it, mission complete and the client was as happy as Dustin eating his 3 Muskateers Nougat.

So to summarise the steps required to complete this

  1. Create a Pre-Value source class inheriting from FieldPreValueSourceType
  2. Create a field type class inheriting from FieldType
  3. Create the partial views for the field types in 3 different places
    1. Backoffice/common/fieldtypes, backoffice/common/rendertypes and views/partials/forms/fieldtypes
  4. Add the field to the form in the back office
  5. Create a custom workflow class for sending out the email (or anything really) that inherits from WorkflowType
  6. Add this custom workflow to your form
  7. Create any necessary rendering in the RenderTypes and umbraco.forms.js

Good luck.