The first thing you need to understand is the anatomy of a Dojo plugin, so lets have a look.
But once we have a Dijit module defined and ready to go we need to somehow hook it up to EPiServer and tell EPi which property we want to use our shiny module for. This isn’t very difficult either. We simply decorate any property on a ContentType with the ClientEditor attribute, like this:
And suddenly all strings will be handled by the ournamespace.ourawesomewidget module. Pretty neat huh?
But hey, wait a minute, where do we actually put our created Dijit module for EPiServer to find it? This is an excellent question and here we unfortunately need to mix in some site configuration. In the module.config you can define where the Dojo loader should look for files with a specific namespace. In the alloy templates site we find this:
Ok, so there’s a <dojo> tag where we can add <paths>, apparently this automatically maps to the ~/ClientResources/ root folder. Also worth noting is that the name of our added path (in this case alloy) must correspond exactly to the namespace of our modules for Dojo to understand where to look for them, this means that if your namespace alloy.somenamespace.widgetname with a modules config as the one above the path to your widget must be ~/ClientResources/Scripts/somenamespace/widgetname.js.
I hope all that made sense…
Anyway, enough with the background already, lets actually build something. Imagine our client wants to add some sort of property editor for creating polls to their site. Each poll has a title and a couple of possible answers.
Allright, lets start with modeling the data real quick. I’m gonna use ASP.NET Web Api as the back end for our polls.
Just a real simple API controller with an in memory representation of our data. We’re going to need an answers controller as well…
Ok, well that would of course work a little bit different if we actually had some sort of database or something to persist our polls and answers to, but we don’t, so lets just have a look at the Dijit now…
We start by configuring up a new namespace in the module.config (we could of course use the already existing alloy namespace, but where’s the fun in that?). Note how we can have two namespaces pointing to the same path without a problem.
I create a Dijit module PollCreation.js:
We then pass in our function which has an argument for each of our dependencies:
You could name these arguments anything you want, but it’s good practice to name them the same as the dependency.
Allright, moving on… we then from our function return our declared dijit module:
Note how we inherit all functionality from _Widget, _TemplatedMixin, _WidgetsInTemplateMixin etc. especially the _TemplatedMixin is interesting as it allows us to use a HTML template for our module. Remember "dojo/text!./templates/poll.html" from our dependencies? This is where we make use of it. We’ve put the value of the loaded poll.html into the template variable. The path to our poll.html is relative to our module which means that the complete path to the html template is ~/ClientResources/Scripts/Editors/templates/poll.html. All right, so we’ve put the html template into the templateString variable inside our module, which means our module can access that variable using this.templateString which we shall see in practice soon.
Here’s the poll.html template:
Yeah, it’s quite a mess but not really that complicated. It’s very important to note that any template that is to be used by a Dijit module must have exactly one root element. If you by mistake add two divs as root element for example the EPiServer forms editing mode will simply fail to load… silently. No errors! Nothing! This can be very frustrating, but as long as you remember to use only one root node you should be fine.
Like I said, not too complicated, but a lot of steps already and we haven’t even looked at the module in editmode yet!
Lets keep going through our module, next up are all the functions.
That’s it! But since we haven’t created the DeleteableTextBox and the PollDialog yet we still aren’t done. Lets have a look at the DeleteableTextBox real quick.
Now that’s a lot simpler. We just create a simple module that inherits from dijit.form.TextBox and add some functionality for deleting the associated answer. Here’s the html template.
All right, almost done. Lets have a look at the dialog.
Here’s the html template for the dialog.
And now we’re finally done with the plugin. We just need to setup a property on one of our ContentTypes like this.
Ok, lets have a look at the result.
Our property should now look something like this.
And if we select a poll:
Deleting a poll is as simple as clicking on the waste basket icon, and if we click the + we’ll see our dialog in action:
And that’s it! It took some effort and we could definitely have polished this a bit more, but I hope building custom properties for EPiServer now feels a bit more doable.
You might have noted that I do not have a POST method in my web api controller which means that we’ll get an error when we try to create a new poll. That could be easily remedied by adding a post method, something like this:
However this still won’t work as we do not persist any data. But if you had an underlying database or some other type of persistence it would work as expected.