Create a dropdown menu of years

Umbraco and C# coding

Create a dropdown menu of years.

First of all, I will show you want I mean when I say a dropdown menu of years and then I'll show you how I did it but at the same time, explain why I did it this way.

This code example is being used on an Umbraco 8 project but it's not Umbraco specific.

The functionality of this dropdown is to allow users to filter search results by a year. The year will be added to a query string and then I'll search Examine for the published date of the content that matches the year. That's maybe for another blog.

Just now, here is what the dropdown menu looks like:

A dropdown list showing years

I could have created a list within HTML with hardcoded years in it e.g.

<ul>
<li>2019</li>
<li>2020</li>
<li>2021</li>
</ul>

This is just for example as the actual dropdown has checkboxes in it.

But what happens next year? Does the client need to call us up and say, can you please add another year to the drop down or what if they want to add 2018 to the list, again, do they need to call up to get it added? That just seemed crazy.

I started to implement a solution by creating a document type in the backoffice which is a category and the client can add years and then they use a category picker on the search results node where they can pick which years to display out to a list on the front end.

It worked but I ran in to a couple of issues when rendering out the value to the front end. I was trying to use .ToSafeAlias() because this was to catch any client mistakes e.g. they might put in a space by accident but I found when I tried to place the Alias in to a querystring, the output was empty! If I took .ToSafeAlias off the value, the it worked. Not ideal.

Each day at 3:30pm I have a call with the rest of the .Net Team at Spindogs, where I work, and I mentioned this issue and one solution was to add a numeric field in to the backoffice with a Start Year which can only accept positive whole numbers e.g. you cant enter -2021 or 20.21.

Once I have this in Umbraco, I can then extend the current page model so that I check the value and if the value is less than the current year, go in to a loop and increment the value until it matches the current year. These values are then added to a list which I can iterate over on the razor page.

using System;
using System.Collections.Generic;

namespace Umbraco.Web.PublishedModels
{
    public partial class SearchResults
    {
        public List<string> Year()
        {
            List<string> listOfYears = new List<String>();
            DateTime now = DateTime.Now;

            if (StartYear <= 0 || StartYear > now.Year)
            {
                listOfYears.Add(now.Year.ToString());
                return listOfYears;
            }
            else
            {
                if (StartYear < now.Year)
                {
                    for(int year = StartYear; year <= now.Year; year++)
                    {
                        listOfYears.Add(year.ToString());
                    }
                }
                return listOfYears;
            }
        }
    }
}

I've added a couple of 'safety' checks in the model too.

If the editor doesn't enter a value or adds a year in the future we only add the current year to the list.

So if the user enters in the year 2019, the years 2019, 2020 and 2021 will be added to the list which is then returned to the view.

  @if (Model.Year().Any())
                        {
                            <div class="field_wrap __checkbox __dropdown __date">
                                <div class="label_wrap">
                                    <label>Year</label>
                                </div>
                                <div class="input_wrap">
                                    @foreach (var year in Model.Year())
                                    {
                                    var isChecked = filtersYear != null && filtersYear.Contains(year);
                                    <label class="@( isChecked ? "__checked" : string.Empty)"><input type="checkbox" class="filter" name="Year" @( isChecked ? "checked" : string.Empty) value="@year">@year</label>
                                    }
                                </div>
                            </div>

                        }

And this is the code that I use to create the dropdown menu.

There is some extra stuff added in here which checks the querystring value and checks the box on page reload but hopefully you can see how all I do is loop over Model.Year() and then output the values being returned.

The nice thing about this is that next year, the user doesn't need to remember to add a new year document type, the dropdown menu will just automatically see that the current year is now 2022 and add a new checkbox to the dropdown.

I hope this is useful to someone and if you have any comments or questions, feel free to drop me a message on Twitter @scottishcoder

EDIT: 

After posting the blog, I got a message from Matt Wise with an alternative approach. Love this collaboration from the community. 

Here is his approach:

using System;
using System.Collections.Generic;
using System.Linq;
					
public class Program
{
	public static void Main()
	{
		var startYear = 2022;
		var years = Years(startYear);
		foreach(var y in years)
		{
			Console.WriteLine(y);
		}
	}
	
	public static List<int> Years(int startYear)
	{
		var rangeLength = DateTime.Now.Year - startYear + 1;
		if(rangeLength > 0 && startYear > 0)
		{
			return Enumerable.Range(startYear, rangeLength).ToList();			
		}
		else
		{
			return new List<int> { DateTime.Now.Year };
		}
	}

 

Thanks for sharing Matt #h5yr!

Published on: 03 September 2021