Building a Software Survey using Blazor - Part 12 - Putting it all together

I've decided to write a small survey site using Blazor. Part of this is an excuse to learn Blazor.

As I learn with Blazor I will blog about it as a series of articles.

This series of articles is not intended to be a training course for Blazor - rather my thought process as I go through learning to use the product.

All articles in this series:


In this article I wanted to recap the previous articles in the series and how they fit together to produce the final Software Survey application.

Starting the survey

A number of things occur simply by the user visiting https://software-survey.red-folder.com/

Firstly, the static HTML is provided by the application. The PreRenderLoadingMessage component I described in part 4 allows me to provide a loading page while the Blazor Server app starts up:

prerender loading page

During the Server app start up, it will:

The app is then ready to use and the PreRenderLoadingMessage will swap over from the loading page to the starting page.

The survey pages

As the user works through the survey, each page will use Dependency Injection to get the relevant part of the state.

Demographics page:

demographics page

State defined as:


namespace SoftwareSurvey.Models
{
    public class Demographic
    {
        [Required(ErrorMessage = "Please provide company size")]
        [DisplayName("Company Size")]
        [JsonProperty(PropertyName = "companySize")]
        public string CompanySize { get; set; }

        [Required(ErrorMessage = "Please provide your job seniority")]
        [DisplayName("Job Seniority")]
        [JsonProperty(PropertyName = "jobSeniority")]
        public string JobSeniority { get; set; }

        [DisplayName("Job Title (optional)")]
        [JsonProperty(PropertyName = "jobTitle")]
        public string JobTitle { get; set; }

        [Required(ErrorMessage = "Please provide if your business is UK Based")]
        [DisplayName("Is your business UK based?")]
        [JsonProperty(PropertyName = "ukBased")]
        public string UKBased { get; set; }
    }
}

Injected as:


    [Inject]
    private Models.Demographic Model { get; set; }

Software Types page:

software types page

State defined as:


namespace SoftwareSurvey.Models
{
    public class SoftwareTypes
    {
        [DisplayName("eCommerce")]
        [Description("Website(s) selling products or services")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "eCommerce")]
        public int? ECommerce { get; set; }

        [DisplayName("Information Website")]
        [Description("Website(s) providing information (portfolio, blog, etc)")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "informationWebsite")]
        public int? InformationWebsite { get; set; }

        [DisplayName("Mobile App")]
        [Description("Application(s) for install on mobile or tablet")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "mobileApps")]
        public int? MobileApps { get; set; }

        [DisplayName("Line Of Business")]
        [Description("Application(s) to support specific business process")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "lineOfBusiness")]
        public int? LineOfBusiness { get; set; }

        [DisplayName("SaaS")]
        [Description("Software as a Service - sold as a product (normally subscription)")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "softwareAsAService")]
        public int? SoftwareAsAService { get; set; }

        [DisplayName("Other")]
        [Description("Any other type of software not listed above")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "other")]
        public int? Other { get; set; }
    }
}

Injected as:


    [Inject]
    private Models.SoftwareTypes Model { get; set; }

Your Experiences page:

your experiences page

State defined as:


namespace SoftwareSurvey.Models
{
    public class Experiences
    {
        [DisplayName("ROI")]
        [Description("Is it providing Return On Investment?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "returnOnInvestment")]
        public int? ReturnOnInvestment { get; set; }

        [DisplayName("Keeping pace")]
        [Description("Is it keeping pace with the business needs?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "keepingPace")]
        public int? KeepingPace { get; set; }

        [DisplayName("Recruitment")]
        [Description("It is easy to recruit the developers you want?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "recruitment")]
        public int? Recruitment { get; set; }

        [DisplayName("Retention")]
        [Description("Is it easy to retain your developers?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "retention")]
        public int? Retention { get; set; }

        [DisplayName("Quality")]
        [Description("Is the software of a high quality?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "quality")]
        public int? Quality { get; set; }

        [DisplayName("Predicability")]
        [Description("Is the software development predicatable?")]
        [Required(ErrorMessage = "Please select a value")]
        [JsonProperty(PropertyName = "predicability")]
        public int? Predicability { get; set; }
    }
}

Injected as:


    [Inject]
    private Models.Experiences Model { get; set; }

One Change page:

one change page

State defined as:


namespace SoftwareSurvey.Models
{
    public class OneChange
    {
        [JsonProperty(PropertyName = "text")]
        public string Text { get; set; }
    }
}

Injected as:


    [Inject]
    private Models.OneChange Model { get; set; }

Further Contact page:

further contact page

State defined as:


namespace SoftwareSurvey.Models
{
    public class Contact : IValidatableObject
    {
        [DisplayName("With the results of the survery?")]
        [JsonProperty(PropertyName = "resultsOfTheSurvey")]
        public bool SurveyResults { get; set; }

        [DisplayName("For any follow up questions?")]
        [JsonProperty(PropertyName = "followUpQuestions")]
        public bool FollowUpQuestions { get; set; }

        [DisplayName("For future surveys?")]
        [JsonProperty(PropertyName = "furtherSuvey")]
        public bool FurtherSurveys { get; set; }

        [DisplayName("Email Address")]
        [EmailAddress]
        [JsonProperty(PropertyName = "email")]
        public string Email { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            return IsEmailValid ? null : new ValidationResult[] { new ValidationResult("Please provided email", new string[] { "Email" }) };
        }

        private bool IsEmailValid => !RequiresEmailAddress || !string.IsNullOrEmpty(Email);
        private bool RequiresEmailAddress => SurveyResults || FollowUpQuestions || FurtherSurveys;
    }
}

Injected as:


    [Inject]
    private Models.Contact Model { get; set; }

Saving the results

On hitting the final page, the application saves the full state object for the user to Cosmos DB - as discussed in part 6

The survey is then complete for the user.

Supporting cast

While not directly related to the application itself, its obvious right to draw attention to the supporting functions.

The following allowed me work quickly by providing a safety net (mainly to avoid regression):

And that's it

The full source code (including tests, Bicep/ ARM templates and Azure DevOps pipeline YAML) can be found in this repo.

There is however one further article to the series. I take a look at the different upgrading to .Net 5 makes to my Blazor Server application.

About the author:

Mark Taylor is an experience IT Consultant passionate about helping his clients get better ROI from their Software Development.

He has over 20 years Software Development experience - over 15 of those leading teams. He has experience in a wide variety of technologies and holds certification in Microsoft Development and Scrum.

He operates through Red Folder Consultancy Ltd.