DEV Community

loading...
Cover image for Building a Software Survey using Blazor - Part 12 - Putting it all together

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

redfolder profile image Mark Taylor Originally published at red-folder.com ・5 min read

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:

Pre-Render Page

During the Server app start up, it will:

  • Get the IP address of the user - as discussed in part 5
  • Create the users state - as discussed in part 2
  • Sets up the SignalR connection - as discussed in part 7

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Injected as:

    [Inject]
    private Models.Demographic Model { get; set; }
Enter fullscreen mode Exit fullscreen mode

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Injected as:

    [Inject]
    private Models.SoftwareTypes Model { get; set; }
Enter fullscreen mode Exit fullscreen mode

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; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Injected as:

    [Inject]
    private Models.Experiences Model { get; set; }
Enter fullscreen mode Exit fullscreen mode

One Change page:

One Change Page

State defined as:

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

Injected as:

    [Inject]
    private Models.OneChange Model { get; set; }
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Injected as:

    [Inject]
    private Models.Contact Model { get; set; }
Enter fullscreen mode Exit fullscreen mode

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):

  • bUnit for Unit testing - see Part 8
  • End to End testing - see Part 9
  • Continous Integration, Delivery and Deployment - see Part 11

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.

Discussion (0)

pic
Editor guide