JustNik

Extending the Umbraco Sys Variables for use in Angular

Umbracologoblue05

Ever wanted to access a setting from Web.Config, or a common Web API URL in your Angular property editor? What about your Custom Dashboard? Or even both? Well you can, and it's reasonably straight forward once you know how.

I'm going to start this little blog by letting you, the reader, know that this has only been tested in Umbraco v7. I have recently helped a friend do something similar in their v8 beta, but things seem to be ever so slightly different.

So, as with a lot of the amazing Umbraco features there is actually some documentation that goes with this, although to be honest I did only find it once I started writing this post. You can find the documentation here: https://our.umbraco.com/documentation/extending/version7-assets.

In the documentation it talks about how you can hook into an event and add your own settings, it's great. So why might you want to do this?

Well, as the title suggests you might want to access some of your settings from Web.Config (or another config file) in your Angular. For me however, I wanted to share an API endpoint across multiple different backoffice customisations. I didn't want to hard code the base API URL in case I refactored it and changed it, although I was happy enough to hard code the final segment into my controllers. This meant, and still means, that if I wanted to change the name of my controller or the Plugin area it sits under I can and I don't have to go and hunt for its usage thought my Angular code.

Loading your variables.

So what's first? Well, the as the documentation says, we need to hook into an event in order to inject our own values. As with other events, we need to do this via some custom code using a class that inherits from ApplicationEventHandler.

In this, we want to add an event handler to the Parsing event on ServerVariablesParser.

public class StartupHandler : ApplicationEventHandler
{
    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
         base.ApplicationStarted(umbracoApplication, applicationContext);
        ServerVariablesParser.Parsing += ServerVariablesParser_Parsing;

    }

    private void ServerVariablesParser_Parsing(object sender, Dictionary<string, object> e)
    {
        //We need some code here
    }
}

Once we've set up our event handler, we then need to add some code to it. This code, for the most part, could do anything but it is important to note that if you need to access something such as a UrlHelper, or an UmbracoHelper instance, you will need to create your own (The documentation loosely touches on this here).

In this code you could read settings from Web.Config, retrieve information from a database, parse a Controller to get it's URL. Frankly, just about anything to retrieve the settings/values you want to make available in the Umbraco.Sys.Variables object.

In this example, I'm going to retrieve the base URL for a custom API controller.

private void ServerVariablesParser_Parsing(object sender, Dictionary<string, object> e)
{
    if (HttpContext.Current == null)
    {
        throw new InvalidOperationException("HttpContext is null");
    }

    //Create a .NET MVC URL Helper
    var urlHelper =
        new UrlHelper(
            new RequestContext(
                new HttpContextWrapper(
                    HttpContext.Current), 
                new RouteData()));

    if (!e.ContainsKey("MySysVariablesSection"))
        e.Add("MySysVariablesSection", new Dictionary<string, object>
            {
                {
                    "SettingsApiUrl",
                    urlHelper.GetUmbracoApiServiceBaseUrl<MyCustomApiController>(
                        controller => controller.GetMyValues())
                }
            });
}

So what is this code doing? Well, firstly it's checking to see if we have a HttpContext, if we don't there might be a bit of an issue! Maybe throwing an exception here isn't the right thing to do, but for demo purposes it will work. Replace that with you own approach to handling the situation!

Once we've done that we go about creating ourselves an instance of a UrlHelper so we can get our base URL for our controller.

Finally we add this to the variable helpfully named "e". This is one of the arguments that is passed into our event handling method and is the dictionary that Umbraco uses to populate Umbraco.Sys.Variables internally. At the point we get access to it, it has already been populated with Umbraco's own settings and we can now add our own.

I've added in a little check to make sure only one instance of our "Section" key is present, you could edit this so that if it already exists your custom setting updates or is added to the existing dictionary.

Because the values in this will get parsed to Json eventually it's nice to keep things as simple as possible in my opinion.

If the section key doesn't exist, we add it along with any settings we want to include. In this case we are adding a "SettingsApiUrl" property with the value the urlHelper gives us as our Base Url for our API end point.

Accessing your variables

So essentially that is it. Once you've configured your C# code bits and pieces you should have values available to you in your Angular. But how do you access them?

Well, in theory it's just a single line of code as the bulk of the lifting it performed by Umbraco internally. How wonderful is that!

The following code is a very basic AngularJS controller for umbraco. It does nothing essentially but on it's load it will write our custom value to the browsers development console. #Loveabitofconsole.log

(function () {
    'use strict';

    function myCustomController($scope, $http) {
        var apiUrl = Umbraco.Sys.ServerVariables["MySysVariablesSection"]["SettingsApiUrl"];

        console.log(apiUrl);
    }
    angular.module('umbraco').controller('myCustomController', myCustomController);

}

As you can see it's quite straight forward to access these values in your Angular, you don't even need to pass anything into your controller. This is because Umbraco defines the Umbraco.Sys.ServerVariables as a global variable. It's great! It's just there!

So Why?

Well why would you do this. As I mentioned earlier you might have a setting that is stored in a database, or a config file you want to access. If this setting isn't going to change (or at least not regularly) why not load it once and forget about it? That's what this allows you to do.

Now if you have a setting that can change based on editor options for example, you might want to go down the route of loading settings via an API call, as seems to quite a common approach to things.

My use case was for 3 different sections of a site (a dashboard, a complete custom tree section, and a property editor) all wanting to call the same API (but use different endpoints). Rather than load this value each time by hard coding my base API URL, I took advantage of this to save the base URL into Umbraco.Sys.ServerVariables and then in my various controllers/services I could just append the different endpoint slugs where need. It made things easier, especially when I had to change the name of my Plugin area (PluginController attribute) due to a naming conflict. I made the change, recompiled the website and my Angular just carried on working.

My advice, give it a go. It's not for everyone and not for every use-case but it's useful knowledge to have under your tool belt!

Note regarding v8-beta:

So, a friend of mine (Paul from codeshare.co.uk) recently gave this approach a go while creating a content app for the upcoming release of v8 and he found that a few things had changed. The biggest being that v8 doesn't have ApplicationEventHandler available any more. Instead this excellent post from Warren explains the new approach.

Paul also mentioned, and this might be possible in v7 as well, that you can also access your custom variable entries in JavaScript without using the string approach I've demoed above but this alternative below.

var apiUrl = Umbraco.Sys.ServerVariables.MySysVariablesSection.SettingsApiUrl;
JustNik
Connect with me
Twitter GitHub LinkedIn
© JustNik 2024