Inspirational Quotes built in Blazor



I decided to build something a little different this time.  I came across a quote I really liked.  It made me stop and think for a minute.  Once I digested the thought, it made me smile.  This got me thinking about having a new quote each day.  I am sure there are apps already available, but I bet there are not any built with Blazor.

 Live Demo

Requirements

        1.       Source of the quotes must be free

        2.       The quote displayed must be random

        3.       There needs to be a background image

        4.       Background image needs to be random

        5.       Must be responsive

        6.       Must be free to host

        7.       Don’t have APIs calling API’s

APIs

I started with Client-side application.  The thinking was that I might want a PWA as well and this would give me a great start. 

Next step was to find a couple of free APis, quote and background image.  I referenced my post on “Holly freebies Batman” and I found free quote here Type Fit . What is interesting about this API, is they provide a “getAll” API.  This allowed me to get all the current quotes and keep them in a file within the application.  I create a json file and added an Id to each quote.  I needed up with a list of over 1600 quotes.  In addition, this allowed me to add some of my own, including the one that triggered the creation of this application.  This will allows us to change the quote dynamically in the PWA in an offline mode.

The background image API turned out to be a little more work.  I found several free ones, but most you had to register to get an API key.  Which is fair.   I did find once from Unsplash.  This one was free, no registration, but unlike the other APIs I looked at, it returned the actual image, not data about the image and then the URL to call.  This would save an additional call.  Downside is with out the attributes of the image, making sure the text of the quote did not bleed into the image would be a bit trickier.

UI

The UI I wanted was straight forward, full screen image, quote text on top of the image.  At the bottom, out of the view area, a little statement on the purpose of the application.

To implement this, I did the following:

        1.       In the MainLayout.Razor, I removed everything but @Body

            @inherits LayoutComponentBase
            @Body

        2.       Remove bootstrap CSS from index.html

        3.       Replace the app.CSS with the new CSS

a.       Note when you replace the app.css you must keep the styles for #blazor-error-ui and .dismiss.  if you don’t, the error with “Reload” consistently shows and it makes you think there was an error.  I wasted some time on that one.

b.       In the “container” of the indie razor page, you need to set the background image source to the Unsplash API

                                                               i.      background-image: url('https://source.unsplash.com/random');

To solve the quote text color issue, white and black both bleed into the background image, I had to find a color that could be seen with most background.  Since the backgrounds are random, this could have been challenging.  I thought about inspecting the image and select a contrasting color, but that was over kill for what I needed.  I ended up just reviewing several colors before selecting the copper/brown color.

This got us to the point of sowing the back ground image and the reason  for the application.

 

Implementation

 

First thing we needed was a Quote object to hold the quote.  This is a simple object: 

        public int Id { get; set; }

        public string Text { get; set; }

        public string Author { get; set; }

 

Since we wanted to get a new quote on each new page refresh, we used the HTTP client in the OnInitializedAsync method to get the collection of quotes from the server 

              quotes = await Http.GetFromJsonAsync<List<Quote>>("sample-data/quote2.json");

 

Next, we created a service to randomly select one of the quotes from the collection:

 public static async Task<Quote> Get(List<Quote> data)
        {
            Quote result = new Quote();

            if(data.Any())
            {
                Random r = new Random();
                int index = r.Next(1, data.Count);
                try
                {
                    var t = from a in data
                            where a.Id == index
                            select a;
                    if (t.Any())
                    {
                        result = t.FirstOrDefault();
                    }
                }
                catch
                {
                    result = data.ElementAt(0);
                }
                if (string.IsNullOrEmpty(result.Author))
                {
                    result.Author = "Unknown";
                }
            }
            await Task.CompletedTask;
            return result;
        }
    }

Since the first Id is 1, we set the random generator at 1 to the number in the quote collection.  We have some error checking just in case there is an exception when trying to get a quote.  Not all quotes have an author, in the last step we check that and set the author to unknown if the author is null.

 

Deployment

I deployed this to Azure as a static web site in a Data storage container.  See my post about Hosting Models for more information.

 

Let’s Make a Server-Side Version

After I deployed and show the application working, I decided to create a Sever Side version.  I did this mainly to track hosing cost.  Since the server version had to be deployed to an Azure app service, free tier would work at first, I and to compare any hidden cost.  I will create a post on my finding on this later.

 

Code differences between Client-Side and Server-Side.

For the UI, there was no difference than removing bootstrap from _Host.cshtml.  The main difference in the code was where and how I was getting the list of Quotes.  In the server side I did not need HTTP client, I could just read the file from the file system.  I did have to put the data file in the wwwroot folder under a data folder.  I had to read this file from the index.razor file to ensure I could access the data file.

Second change was to move the getting the quote to display from the OnInitializedAsync method to the OnAfterRenderAsync method.  This was do to the async calls to get the file and the background image loading.  In slower systems the quote would be display twice.  Once on the initial load and then it would change once the image was loaded.  Moving it to the OnAfterRender method, we now display the quote only after the background image it loaded.

 

To load the quote collection file:

    private string LoadTextFile()
    {
        string result = string.Empty;
        //make sure the file exists
        if (File.Exists("wwwroot/data/quote2.json"))
        {
                result = File.ReadAllText("wwwroot/data/quote2.json");
         }
        return result;
    }


Since we are returning a string of JSON from the file, we need to JsonSerializer it.  I was used to using NewtonSoft for this but it is include now:

    System.Text.Json.JsonSerializer.Deserialize<List<Quote>>(data);

Those were the changes needed.  It was a few more changes than I had thought would be needed, but looking at the changes, it makes sense.

Next Step

Next in mind for the application is to get a custom domain name for it and configure Azure to use it.  I will create a new post on how that goes.

Source Code
I have included both the Client-Side and Sever-Side code in the GitHub repro.


Comments

Popular posts from this blog

Yes, Blazor Server can scale!

Blazor new and improved Search Box

Blazor Wizard Step Component