frameworks

Angular, Blazor, React: A Quick Look

Bradley Hicken Angular, Blazor, Development Technologies, JavaScript, React Leave a Comment

Attention: The following article was published over 2 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

In the world of technology and development, there are many different types of frontend frameworks that are used. In this post, we’ll discuss two of the biggest ones, Angular and React, and one of the newer ones, Blazor.

With all of these frameworks, you would have to spend a huge amount of time getting to know all of them to really understand what it takes to use them. The goal today is to showcase all three doing the same task, so you can directly compare and contrast their capabilities.

Hopefully, this blog will give you the quick facts you need to know to make the best decision for your application.

Component Styles

Angular, React, Blazor, and almost all other frontend frameworks, for that matter, have some type of concept of a component that gets loaded by the web application. So, component styles will be the first aspect we will compare.

Let’s start by looking at what the structure of each style of a component looks like. To do this, I created an API project for all of the 3 different applications to use, an Angular application, a React Application, and a Blazor application created using standard templates that are out there. You may recognize this as a default fetch-data example that you get out of the box from Microsoft.

A single component folder for each application type looks like this:
Angular:

Angular

Blazor:

Blazor

React:

React

We have one or more files that define HTML templates and code that drives or runs those templates in each of these.

Angular is built on HTML templates with a TypeScript file that drives the page. Blazor has a Razor view and in this case, a .cs file that drives the page. It is important to note that, by default, the code for the page can live in the .razor file. It’s just my preference to separate them out. Lastly, the React project in this example is using a TypeScript file using JSX syntax.

Alright, let’s start with Angular and take a look at what really matters in the files.

Angular Files

Fetch-data.component.html:

<h1 id="tableLabel">Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

<p *ngIf="!forecasts"><em>Loading...</em></p>

<table class='table table-striped' aria-labelledby="tableLabel" *ngIf="forecasts">
  <thead>
    <tr>
      <th>Date</th>
      <th>Temp. (C)</th>
      <th>Temp. (F)</th>
      <th>Summary</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let forecast of forecasts">
      <td>{{ forecast.date }}</td>
      <td>{{ forecast.temperatureC }}</td>
      <td>{{ forecast.temperatureF }}</td>
      <td>{{ forecast.summary }}</td>
    </tr>
  </tbody>
</table>
!

Fetch-data.component.html is fairly straightforward. There is a header line and a small explanation of what the component does, followed by a table that gets generated by doing a for loop on a list of items that will be loaded in the next file.

Fetch-data.components.ts:

import { Component, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-fetch-data',
  templateUrl: './fetch-data.component.html'
})
export class FetchDataComponent {
  public forecasts: WeatherForecast[];

  constructor(http: HttpClient, @Inject('API_URL') baseUrl: string) {
    http.get<WeatherForecast[]>(baseUrl + 'weatherforecast').subscribe(result => {
      this.forecasts = result;
    }, error => console.error(error));
  }
}

In the TypeScript file, we have a couple of imports that bring in some of the required parts needed by Angular components.

Then on line 12, we make a call to our API to get a list of weatherforcasts that our component will display in the grid defined above.

Blazor Files

FetchData.razor:

@page "/fetchdata"
@using Microsoft.Extensions.Configuration;
@using TeamTomato.Shared.Models
@inject HttpClient Http
@inject IConfiguration Configuration

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

Blazor Files

In some ways, the razor view and the Angular HTML file look very similar. They are both using a simple for each loop to go over an array of objects that were returned from a data call to the API.

Let’s look at what that call looks like in Blazor.

FetchData.Razor.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using TeamTomato.Shared.Models;

namespace TeamTomato.Blazor.Pages
{
    public partial class FetchData
    {
        private WeatherForecast[] forecasts;
        private IConfiguration _config;

        protected override async Task OnInitializedAsync()
        {
            forecasts = 
                await Http.GetFromJsonAsync<WeatherForecast[]>($"https://localhost:44366/weatherforecast");
        }
    }
}

The same type of things going on here. We make a call to the API request, and it gets assigned to the array that is going to be used by the view to generate the table.

The real difference that I see here between Angular and Blazor is that in Blazor I did not have to write any JS or TypeScript to make it work. That’s one of the things that Blazor likes to brag about: you do not have to write in either of those two languages.

React File

The React file is bigger as it has the HTML and code in the same file. I will break it down into smaller parts and will do my best not to cut anything important out.

FetchData.tsx:

import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router';
import { Link } from 'react-router-dom';
import { ApplicationState } from '../store';
import * as WeatherForecastsStore from '../store/WeatherForecasts';

// At runtime, Redux will merge together...
type WeatherForecastProps =
  WeatherForecastsStore.WeatherForecastsState // ... state we've requested from the Redux store
  & typeof WeatherForecastsStore.actionCreators // ... plus action creators we've requested
  & RouteComponentProps<{ startDateIndex: string }>; // ... plus incoming routing parameters


class FetchData extends React.PureComponent<WeatherForecastProps> {
  // This method is called when the component is first added to the document
  public componentDidMount() {
    this.ensureDataFetched();
  }

  // This method is called when the route parameters change
  public componentDidUpdate() {
    this.ensureDataFetched();
  }

  public render() {
    return (
      <React.Fragment>
        <h1 id="tabelLabel">Weather forecast</h1>
        <p>This component demonstrates fetching data from the server and working with URL parameters.</p>
        {this.renderForecastsTable()}
        {this.renderPagination()}
      </React.Fragment>
    );
  }

  private ensureDataFetched() {
    const startDateIndex = parseInt(this.props.match.params.startDateIndex, 10) || 0;
    this.props.requestWeatherForecasts(startDateIndex);
  }

  private renderForecastsTable() {
    return (
      <table className='table table-striped' aria-labelledby="tabelLabel">
        <thead>
          <tr>
            <th>Date</th>
            <th>Temp. (C)</th>
            <th>Temp. (F)</th>
            <th>Summary</th>
          </tr>
        </thead>
        <tbody>
          {this.props.forecasts.map((forecast: WeatherForecastsStore.WeatherForecast) =>
            <tr key={forecast.date}>
              <td>{forecast.date}</td>
              <td>{forecast.temperatureC}</td>
              <td>{forecast.temperatureF}</td>
              <td>{forecast.summary}</td>
            </tr>
          )}
        </tbody>
      </table>
    );
  }

  private renderPagination() {
    const prevStartDateIndex = (this.props.startDateIndex || 0) - 5;
    const nextStartDateIndex = (this.props.startDateIndex || 0) + 5;

    return (
      <div className="d-flex justify-content-between">
        <Link className='btn btn-outline-secondary btn-sm' to={`/fetch-data/${prevStartDateIndex}`}>Previous</Link>
        {this.props.isLoading && <span>Loading...</span>}
        <Link className='btn btn-outline-secondary btn-sm' to={`/fetch-data/${nextStartDateIndex}`}>Next</Link>
      </div>
    );
  }

In this listing, we see that the control is going to do something that is similar to our other two examples. It will map an array to the elements in the table, and, like the other two examples, this one also just fetches the data when the page loads on line 39.

This call is actually in another file as well.

WeatherForcasts.ts:

export const actionCreators = {
    requestWeatherForecasts: (startDateIndex: number): AppThunkAction<KnownAction> => (dispatch, getState) => {
        // Only load data if it's something we don't already have (and are not already loading)
        const appState = getState();
        if (appState && appState.weatherForecasts && startDateIndex !== appState.weatherForecasts.startDateIndex) {
            fetch( `${Configuration.apiUrl}weatherforecast`)
                .then(response => response.json() as Promise<WeatherForecast[]>)
                .then(data => {
                    dispatch({ type: 'RECEIVE_WEATHER_FORECASTS', startDateIndex: startDateIndex, forecasts: data });
                });

            dispatch({ type: 'REQUEST_WEATHER_FORECASTS', startDateIndex: startDateIndex });
        }
    }
};

This section of code is what makes the call to the data API and returns that back to our component.

In Conclusion

In the end, what I have shown is just a simple example that accomplishes the same thing in 3 different frameworks. My goal is not to convince you one way or another, but to get you interested in looking at different ways of doing things.

I can tell you that I have worked with Angular in the past, and to me, Blazor seems easy to pick up. I have not worked with React on a daily basis for any period of time, but it seems fairly straightforward also.

In the end, when it comes to picking a front-end framework, I think it is always best to look at the skills of the team that is using it and the requirements of the project itself. If the best solution is React, then go with that; if it’s Angular, choose that one; if Blazor is right, go in that direction.

The bottom line is that as individuals that work with technology all the time, we have to be open to considering all the possible solutions. That involves researching (like this post does), testing, and evaluating based on your unique project and team.

If you want to generate the examples we discussed above, you can open Visual Studio and use the default templates to generate the base projects.

Thank you for reading this blog! If you would like to read our other content, click here to check out our dev blog.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments