How to use an Umbraco data type to populate an MVC Drop Down List

Posted written by Paul Seal on April 05, 2017 Umbraco

What is this post about?

If you use Umbraco and you create forms using MVC and razor, perhaps a contact form, you might want to populate a dropdownlist with data controlled by the Umbraco backoffice. This post shows you how to do that.

You can now watch the video of me implementing this

Watch on YouTube

1. Create an Umbraco Data type

In the Developer tab of Umbraco, click on the dots next to Data Types and create a new data type. Enter a name and choose dropdown list as the property editor.

Add the values you want to appear in the dropdown list. Then click on Save.

After you have pressed save, make a note of the id at the end of the url.

2. Add this helper class to your project

using System;
using System.Collections.Generic;
using System.Xml.XPath;
using System.Web.Mvc;
using System.Web.Configuration;

namespace CodeShare.Library.Helpers
{
    public class PreValueHelper
    {
        public List<SelectListItem> GetPreValuesFromDataTypeId(int dataTypeId)
        {
            List<SelectListItem> preValueSelectorList = new List<SelectListItem>();

            XPathNodeIterator iterator = umbraco.library.GetPreValues(dataTypeId);
            iterator.MoveNext();
            XPathNodeIterator preValues = iterator.Current.SelectChildren("preValue", "");

            while (preValues.MoveNext())
            {
                string preValueIdAsString = preValues.Current.GetAttribute("id", "");
                int preValueId = 0;
                int.TryParse(preValueIdAsString, out preValueId);
                string preValue = preValues.Current.Value;
                preValueSelectorList.Add(new SelectListItem { Value = preValue, Text = preValue }); //you could use the preValueId for the value here if you want the value to be a number
            }

            return preValueSelectorList;
        }
    }
}

3. Include some extra methods for retrieving your data type id from app settings.

You could save the id of the data type in your web.config app settings. These methods will help you with that

using System;
using System.Collections.Generic;
using System.Xml.XPath;
using System.Web.Mvc;
using System.Web.Configuration;

namespace CodeShare.Library.Helpers
{
    public class PreValueHelper
    {
        private const string APP_SETTING_ERROR_MESSAGE = "Invalid or missing appSetting, ";

        public List<SelectListItem> GetPreValuesFromDataTypeId(int dataTypeId)
        {
            List<SelectListItem> preValueSelectorList = new List<SelectListItem>();

            XPathNodeIterator iterator = umbraco.library.GetPreValues(dataTypeId);
            iterator.MoveNext();
            XPathNodeIterator preValues = iterator.Current.SelectChildren("preValue", "");

            while (preValues.MoveNext())
            {
                string preValueIdAsString = preValues.Current.GetAttribute("id", "");
                int preValueId = 0;
                int.TryParse(preValueIdAsString, out preValueId);
                string preValue = preValues.Current.Value;
                preValueSelectorList.Add(new SelectListItem { Value = preValue, Text = preValue });
            }

            return preValueSelectorList;
        }

        public List<SelectListItem> GetPreValuesFromAppSettingName(string appSettingName)
        {
            int dataTypeId = GetIntFromAppSetting(appSettingName);
            List<SelectListItem> preValues = GetPreValuesFromDataTypeId(dataTypeId);
            return preValues;
        }

        private int GetIntFromAppSetting(string appSettingName)
        {
            int intValue = 0;
            string setting = GetStringFromAppSetting(appSettingName);
            if (!int.TryParse(setting, out intValue))
            {
                throw new Exception(APP_SETTING_ERROR_MESSAGE + appSettingName);
            }
            return intValue;
        }

        private string GetStringFromAppSetting(string appSettingName)
        {
            string setting = WebConfigurationManager.AppSettings[appSettingName] as string;
            if (String.IsNullOrEmpty(setting))
            {
                throw new Exception(APP_SETTING_ERROR_MESSAGE + appSettingName);
            }
            return setting;
        }

    }
}

4. Add a property in your model, using the type List<SelectListItem>

In this example, my model has a list of titles.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace CodeShare.Library.Models
{
    public class ContactModel
    {

        [Display(Name = "Title:")]
        public string Title { get; set; }

        public List<SelectListItem> Titles { get; set; }
    }
}

5. Use the helper in the controller to load the values from the data type.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Web.Mvc;
using CodeShare.Library.Models;


namespace CodeShare.Web.Controllers
{
    public class ContactController : SurfaceController
    {

        private PreValueHelper preValueHelper => new PreValueHelper();

        private string GetViewPath(string name)
        {
            return $"~/Views/Partials/Apply/{name}.cshtml";
        }

        [HttpGet]
        public ActionResult RenderContactForm()
        {
            ContactModel model = new ContactModel();
            SetModelData(model);
            return PartialView(GetViewPath("_ApplicantDetails"), model);
        }

        [HttpPost]
        public ActionResult RenderContactForm(ContactModel model)
        {
            SetModelData(model);
            return PartialView(GetViewPath("_ContactForm"), model);
        }

        [HttpPost]
        public ActionResult SubmitContactForm(ContactModel model)
        {
            if(ModelState.IsValid)
            {
                //Do something
                return RedirectToCurrentUmbracoPage();
            }
            return CurrentUmbracoPage();
        }

        private void SetModelData(ContactModel model)
        {
            model.Titles = preValueHelper.GetPreValuesFromAppSettingName("TitlesDataTypeId");
        }
    }
}

6. Render the dropdown list in your razor partial view in the form

@using CodeShare.Library.Models
@model ContactForm

@using (Html.BeginUmbracoForm("SubmitContactForm", "Apply"))
{
    @Html.AntiForgeryToken()

    @Html.LabelFor(m => m.Title)
    @Html.DropDownListFor(m => m.Title, Model.Titles, "Please Select...")
    @Html.ValidationMessageFor(m => m.Title)

    <button>Submit</button>
}

That's it, all done