Since the creation of the World Wide Web, the delivery of information to people has been transformed by web technologies such as web servers, web browsers, and web pages. The capabilities and uses of these technologies have evolved greatly over the decades. In particular, the evolution of the web page has greatly impacted users and developers alike. In the last decade, users and developers have seen the rise of Single-Page Applications (SPA). SPA user interfaces (UI) and their associated libraries and frameworks have become very popular and have transformed both the user and developer web page experience.
Gone are the days of server-side web applications where UIs were dynamically generated on the server and sent as HTML to the web browser. The constant need to serve a new page for every DOM (document object model) change did not provide a great user experience and it placed an unnecessary load on network and server resources.
To improve performance and user experience, we must shift UI rendering from the server to the web browser client. This reduces network and server load, avoids page reloading, and makes changes to the DOM tree through the browser's programming language: JavaScript.
Around 10 years ago, several improvements in web browser technology appeared. Web browser APIs became more standardized. JavaScript began to mature with ES5 and a plethora of JavaScript UI frameworks came into common usage. With the improvements in browser technology, developers began to leave the comfort of statically-typed and strongly-typed languages such as Java and C# for JavaScript in the web browser. Why JavaScript? It is the only language supported in all web browsers.
In the early days of this transition from server to client UI rendering, libraries such as jQuery, Backbone.js, Angular.js, and Dojo ruled the development world. While they worked, the resulting code was often difficult to maintain. Furthermore, the efficiency of DOM updates was limited to the skill of the individual developer.
To make things better, component-based libraries entered the scene. These new component-based libraries, such as React and Angular, provided two huge benefits.
First, they provided a good structure for creating reusable UI parts and they provided good patterns and practices for composing those UI parts to build a complete web page. Second, these component-based systems were declarative. Declarative components meant the developer did not write the code to update the DOM tree directly. DOM tree updates would be handled by the component library. One such approach to a library updating the DOM is React's "Virtual DOM" mechanism.
Even with the rise of component-based JavaScript libraries/frameworks, one problem remained for many developers, code had to be written in JavaScript. To help mediate this problem, Microsoft created TypeScript. TypeScript is a superset of the JavaScript language that adds static typing and strong typing to JavaScript's dynamic typing and loose typing. With the proper type definition packages, TypeScript can be used with any JavaScript library to write statically and strongly typed code. The usage of TypeScript is a real help for enterprise-class projects. Typically, enterprise-class projects have many developers, each of whom works on specific parts of the application. Static typing helps developers understand the overall structure of the project and ensures they work correctly with parts of the project they did not code. The downside to TypeScript is that there are incompatibilities between versions (including minor release versions) and it results in a lot of extra code, some of which can be hard to write.
For libraries such as React, both JavaScript and TypeScript can be used. While JavaScript is more common, TypeScript has a strong following in the React community. For modern Angular (not to be confused with the older Angular.js), TypeScript is a requirement. Angular leverages the additional capabilities of TypeScript as part of its framework. A good example of Angular's use of TypeScript features is the use of decorators to attach metadata to class definitions. The metadata is used to describe the role of a class within the Angular application such as a class being a component or pipe.
For many years, these JavaScript/TypeScript libraries and frameworks have dominated the web browser UI development landscape. Nevertheless, the long-sought desire of developers has been the ability to write statically-typed, strongly-typed, browser-based applications without using JavaScript. In the early days, this was accomplished with Java Applets, followed by Flash, and for a brief period, Silverlight. Each of these browser-based technologies was proprietary and usually required an extension to be installed in the browser. The problem was the many browsers did not equally support the technologies. Often there were security issues with the technologies and large corporate enterprises would not let the extensions be installed. Additionally, private consumers did not know how to install the extensions, either. In the end, hardware/software vendors such as Apple would not support them on mobile devices.
WebAssembly achieves part of the goal sought by technologies such as Java Applets, Flash, and Silverlight: running statically-typed, strongly-typed binary code directly in a web browser. WebAssembly is a standard and has been supported in all major browsers in 2017. Today, 94% of all browsers support it directly and for those older browsers that do not, it can be poly-filled with a library named "asm.js". Using languages such as C, C++, Rust, GoLang, and many others, code can be compiled to a WebAssembly binary, downloaded into the browser, and executed. Sounds awesome right? Yes, it is.
But what is the part of the goal that was not achieved? Unlike Java Applets, Flash and Silverlight, WebAssembly cannot directly interact with the UI, in this case, the DOM tree. It can interact with JavaScript, but not the DOM tree directly. So to use WebAssembly, there needs to be JavaScript code to marshal changes to the DOM on behalf of WebAssembly.
Sure, writing WebAssembly code in C or C++ is cool, but who wants to write a web application with C? Can anyone remember the mid-90's and writing CGI programs in C? Even then, developers preferred Perl or PHP for writing web page code in C. Ideally, business application developers want to avoid JavaScript and write the code in a language they know, namely languages such as Java or C#. So it is possible to write C# code that runs as WebAssembly in the web browser? The answer is 'yes', it's called Blazor WebAssembly. Blazor is a .NET and C# based framework for writing SPA web applications. It comes in two flavors (hosting models): Server and WebAssembly. The focus for this blog post is WebAssembly.
Like React and Angular, Blazor WebAssembly uses a component-based pattern to promote reusability and good UI design patterns. It is declarative, the developer is not writing code to directly modify the DOM tree. The difference is that JavaScript is not used. Instead, a special version of .NET created with Mono runs as WebAssembly in the web browser. Mono is a cross platform, open-source .NET framework sponsored by Microsoft. This version of .NET downloads the DLLs produced by the C# project and runs the C# code in the browser. For DOM updates, Blazor provides a JavaScript file that the WebAssembly code interacts with to perform the DOM updates. This allows developers to mostly use C# to write UI code (just like they did for older server-side applications), but run the code directly in the web browser like the new SPA frameworks such as React and Angular. Truly the best of both worlds. A great user and developer experience.
Does it work well? The answer is mostly yes. The only challenge with Blazor WebAssembly is the initial load of the web page. Because .NET and other DLLs need to be downloaded, there can be a slight delay in the initial load. While this can be worked around to a limited degree, the delay is still noticeable. In truth, React and Angular applications can experience a similar delay, but this is handled by the server-side rendering of the first page. The Blazor team is working on adding a pre-rendering mechanism to Blazor WebAssembly. Except for this issue, it works well, especially for traditional business web application development.
Before wrapping up this blog post, let's take a look at a component coded in React, Angular, and Blazor.
Here is an ItemList component in React:
import PropTypes from 'prop-types'; export const ItemList = props => { const itemAction = itemValue => { props.onItemAction(itemValue); }; return ( <ul> {props.items.map(item => <li key={item.value}> {item.text} <button type="button" onClick={}>{props.actionText}</button> </li>)} </ul> ) }; ItemList.defaultProps = { items: [], itemAction: "Select", }; ItemList.propTypes = { items: PropTypes.arrayOf(PropTypes.shape({ value: PropTypes.oneOfType[PropTypes.string,PropTypes.number], text: PropTypes.string, })), actionText: PropTypes.string, onItemAction: PropTypes.func.isRequired, };
The component is coded with a JavaScript function. The parameters, called props, are passed as a parameter to the function. The function contains both the code and template. In React, templates are implemented with an HTML-like syntax called JSX. Under the hood, JSX is nothing more than function calls to create elements that are ultimately rendered in the DOM tree. Data to be displayed in the component is passed in via props.
For dynamic parts of the template, plain JavaScript expressions are placed inside curly braces within the JSX. In the above code, the map function is used to transform the array of item objects into an array list item elements populated with the item object. Plain JavaScript such as this makes React easier to work with in many cases.
To communicate data from the child component to the parent component, an event handler pattern is followed. The parent component passes a function object reference via props to the child component. Then the child component invokes the function, passing whatever data is desired into the function, which is then utilized by the parent. In the above example, when the user clicks the action button, the list sends to the parent component the value of the item whose button was clicked.
The ItemList component would be called like this:
<ItemList items={someItems} actionText="Delete" onItemAction={deleteItem}
/>
The ItemList component is called with JSX and the parameters are passed as attributes, which is similar to how HTML works. The ItemList element itself is not rendered to the DOM tree.
Here is the same ItemList component implemented in Angular:
export type Item = { value: number, text: string, };
Angular uses TypeScript. So unlike React with JavaScript, all of the model types must be defined before they can be used. In the above code, a type alias is used to define the Item type.
Next is the string-based template of the component:
<ul> <li *ngFor="let item of items"> {{item.text}} <button (click)="doItemAction(item.value)">{{actionText}}</button> </li> </ul>
The template is an HTML string with special DOM markers called directives applied to it. This HTML string is processed by the Angular compiler, which applies logic to the template through the directives. For example, the ngFor directive will duplicate a tree of HTML based upon the elements in a collection of data.
With Angular, the template of the component and the code of the template are separated into two different files. Each component in Angular is a combination of a class and a template. While most components in React are created with plain functions, historically, classes could be used as well.
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Item } from '../../models/item'; @Component({ selector: 'app-item-list', templateUrl: './item-list.component.html', styleUrls: ['./item-list.component.css'] }) export class ItemListComponent implements OnInit { @Input() items: Item[] = []; @Input() actionText = "Select"; @Output() itemAction = new EventEmitter<number>(); constructor() { } ngOnInit(): void { } doItemAction(itemValue: number) { this.itemAction.emit(itemValue); } }
When a component is used, a new class instance is created. Observe that the class has a decorator named Component. This decorator applies metadata to the class definition to tell Angular that this class is a component. Classes be used for other Angular parts such as modules, pipes, and services.
The Input decorated properties are the parameters of the component. They are set after the component is instantiated and before it is rendered. Angular components output data to their parents through Output decorated properties. Outputs are coded as EventEmitters and use a technology called RxJS to output data.
<app-item-list [items]="someItems" actionText="Delete" (itemAction)="deleteItem($event)"></app-item-list>
Using the CSS selector pattern from the component's metadata, a custom HTML element is used to display the component. With Angular, the custom element is rendered to the DOM tree.
The element attributes will take one of three forms. The attribute with no square brackets and no parentheses assigns the literal string value to the component's input property. The attribute with the square brackets evaluates the attribute value as an Angular expression, then assigns the result of the evaluation to the component's input property. The attribute with the parentheses subscribes to the output property's event emitter to receive data from the child component.
Blazor uses Razor Components to display its UI. The name Razor comes from the Razor syntax used in traditional server-side ASP.NET MVC applications. The Razor syntax is used for components, too. Like Angular, Razor Components are coded with a statically typed language, C#, so the model has to be defined.
namespace ToolsApp.Models { public class Item { public int Value { get; set; } public string Text { get; set; } } }
Like React, the template and code for the Razor Component are stored in the same file. The string- based template is at the top and the code block is specified underneath. Like Angular's Input and Output decorators for parameters, all parameters for Razor Components are decorated with the Parameter attribute. Plain data parameters can be of any type. Parameters used to output data to the parent are typed as an EventCallback.
@using ToolsApp.Models; <ul> @foreach (var item in Items) { <li> @item.text <button type="button" @onclick="@(() => itemAction(item.value))">@ActionText</button> </li> } </ul> @code { [Parameter] public IEnumerable<Item> Items { get; set; } = new List<Item>(); [Parameter] public string ActionText { get; set; } = "Select"; [Parameter] public EventCallback<int> OnItemAction { get; set; } private void itemAction(int itemValue)) { OnItemAction.InvokeAsync(itemValue) } }
<ItemList Items="@items" ActionText="Delete" OnItemAction="deleteItem" />
Like React, the component is called by name using HTML, but the component element itself is not rendered to the DOM tree. Parameters are passed via the attributes. Some attribute values will contain an @ symbol indicating that the value is a Razor expression that is evaluated first, and then the result is passed in.
All three examples above are components. Their implementation syntax greatly varies, but the code that calls the components appears quite similar. There are implementation details that would reveal many more similarities and differences, but let's focus on what they have in common.
For the component template, all three frameworks provide a place for an HTML-like syntax to design the UI. The templates support both normal HTML as well as code to populate the HTML with dynamic values.
React templates are coded with an HTML-like syntax called JSX. React templates are not string-based. The JSX is an alternative syntax for making a function call. So React templates are actually just JavaScript function calls where the element type and props are passed in. Angular uses string-based templates that are compiled with the Angular compiler to produce a view class. This view class is used by Angular to generate the UI. The Blazor template is compiled Razor syntax.
All three support a parameterization mechanism. In React, parameters are referred to as props and are passed as a single object into the component function. Each property of the object is an attribute key-value pair. In Angular, parameters are referred to as inputs and outputs, and they are properties on the component instance. Once the instance is created, Angular assigns data to the input parameter properties. Blazor's Razor components work similar to Angular's class-based components. While there is no class keyword in the Razor Component file, when the Razor code is compiled, a component class is created. Also, the properties decorated with the Parameter attribute will receive the parameter values, which is similar to how the Input decorator works in Angular.
An important ability of components is outputting data to a parent component via an event handler. All three frameworks support this ability. For React, a child component transmits data to its parent by invoking a function passed from the parent component to the child component via props. For Angular, an Output property is defined as an EventEmitter. An Event Emitter is essentially an RxJS Subject that is used to publish events that are subscribed to by the parent component. The Blazor syntax is a mix of the React and Angular syntax. The function is passed as a parameter, but the parameter has a type of EventCallback. The child component outputs data through Event Callback through the InvokeAsync method.
A big change in UI development many years ago was the adoption of the component-based pattern seen in frameworks such as React, Angular, and Blazor. For many years, client-side components were only available through JavaScript (and later TypeScript) UI frameworks. However, with the release of WebAssembly, popular languages such as C# can be used to build client-side components. Blazor enables server-side .NET developers to benefit from reusability and composability of components with the power of the statically and strongly-typed C# language. Even better, Blazor components are very similar to React and Angular components in both form and function. This similarity enables existing React and Angular developers to easily transition to this new and exciting technology.
Acclebrate offers private, customized Web Development courses delivered at your site or online for your team of 3 or more attendees.
Written by Eric Greene
Eric is a professional software developer specializing in HTML, CSS, and JavaScript technologies. He has been developing software and delivering training classes for nearly 19 years. He holds the MCSD Certification for ASP.Net Web Applications, and is a Microsoft Certified Trainer.
Our live, instructor-led lectures are far more effective than pre-recorded classes
If your team is not 100% satisfied with your training, we do what's necessary to make it right
Whether you are at home or in the office, we make learning interactive and engaging
We accept check, ACH/EFT, major credit cards, and most purchase orders
Alabama
Birmingham
Huntsville
Montgomery
Alaska
Anchorage
Arizona
Phoenix
Tucson
Arkansas
Fayetteville
Little Rock
California
Los Angeles
Oakland
Orange County
Sacramento
San Diego
San Francisco
San Jose
Colorado
Boulder
Colorado Springs
Denver
Connecticut
Hartford
DC
Washington
Florida
Fort Lauderdale
Jacksonville
Miami
Orlando
Tampa
Georgia
Atlanta
Augusta
Savannah
Hawaii
Honolulu
Idaho
Boise
Illinois
Chicago
Indiana
Indianapolis
Iowa
Cedar Rapids
Des Moines
Kansas
Wichita
Kentucky
Lexington
Louisville
Louisiana
New Orleans
Maine
Portland
Maryland
Annapolis
Baltimore
Frederick
Hagerstown
Massachusetts
Boston
Cambridge
Springfield
Michigan
Ann Arbor
Detroit
Grand Rapids
Minnesota
Minneapolis
Saint Paul
Mississippi
Jackson
Missouri
Kansas City
St. Louis
Nebraska
Lincoln
Omaha
Nevada
Las Vegas
Reno
New Jersey
Princeton
New Mexico
Albuquerque
New York
Albany
Buffalo
New York City
White Plains
North Carolina
Charlotte
Durham
Raleigh
Ohio
Akron
Canton
Cincinnati
Cleveland
Columbus
Dayton
Oklahoma
Oklahoma City
Tulsa
Oregon
Portland
Pennsylvania
Philadelphia
Pittsburgh
Rhode Island
Providence
South Carolina
Charleston
Columbia
Greenville
Tennessee
Knoxville
Memphis
Nashville
Texas
Austin
Dallas
El Paso
Houston
San Antonio
Utah
Salt Lake City
Virginia
Alexandria
Arlington
Norfolk
Richmond
Washington
Seattle
Tacoma
West Virginia
Charleston
Wisconsin
Madison
Milwaukee
Alberta
Calgary
Edmonton
British Columbia
Vancouver
Manitoba
Winnipeg
Nova Scotia
Halifax
Ontario
Ottawa
Toronto
Quebec
Montreal
Puerto Rico
San Juan