After comparing the two frameworks from the development experience’s point of view, we now analyze the features that both provide in terms of components, infrastructure, and maturity.
As Angular is based on ECMAScript and a set of libraries specifically created for the framework, Blazor is based on the . NET Core framework and inherits all its features and peculiarities. Unlike the classic . NET Framework, . NET Core natively provides Dependency Injection, Logging and configuration support. Blazor inherits directly these features, as well as many advanced aspects such as Reflection, LINQ and everything we know about the .NET world. Combined with the strong typization of C#, These aspects can make a difference in many business scenarios, provided that you know them.
One of the most common methods to implement the principle of control inversion (Ioc), is to use an abstract Factory that provides dependencies to the classes that need them. In programming, it is used to make the elements of your solution low coupled, so as to make dependency implementations replaceable. In addition to being a very useful practice to have high quality code, maintainable and testable, it is also a very interesting way to replace implementations of a feature based on your needs, even rashly. This technique is called Dependency Injection and is implemented natively by modern software development frameworks.
A dependency is an instance of a class that serves our component to perform operations, such as invoking the back-end to retrieve information, or a service class that performs calculations.
An interesting fact of the dependency injection libraries, is that they allow us both to define the dependencies, and their life cycle, solving in chain all the dependencies that you need. This means that if we have a component that needs a class to do some operation, it asks the Factory to provide it, rather than directly instantiating the class: the Factory then decide whether to instantiate a new class or to return a previously created instance, based on its configuration,
A life cycle management that always creates a new instance at each request, is called Transient. If an instance is created on the basis of a certain context, for example only once within the same HTTP request and I always return the same to whoever requests it in the span of that request, the life cycle is called Scoped; the scope, in this case, is the HTTP request. If an instance is created only once and always the same is returned, it is a Singleton.
Please note that if the class that needs to be instantiated has, in turn, dependencies, the Factory solves these too, creating a real tree of objects that dynamically couple the parts of the application.
If you are wondering why you should do that way instead of implementing everything in the methods of the component itself, I suggest you to start the long journey in software engineering. But I would like to point out that, apart from all the benefits in terms of quality, the possibility of configuring the life cycle of an object has the pleasant side effect of being able to create objects to share among several components, providing a communication mechanism among your UI elements that are not in father/child relationship.
Both Angular and Blazor provide tools for dependency injection, although they are very different. Angular implements a hierarchical mechanism, based on provider, in which it is possible to define who provides the dependencies to whom. We can for example indicate that a module is the provider of specific dependencies: this means that all components of that module can receive the same instance of the desired class. Alternatively I can define a new provider at the component level: in this case the component creates the instance for itself and its children (the components contained within it). The classes injected into Angular are called Service, and are identified by the @Injectable annotation. Actually, this annotation is necessary only if the class defines dependencies.
Blazor, , being based on . NET Core, does not need to implement a library for dependency injection, since the framework already provides it natively. We can use everything we already know about the dependency injection engine. NET, including the possibility of replacing it with a custom engine we prefer.
Let’s just make a clarification for managing the life cycle of objects in Blazor Server and Blazor Webassembly. In Blazor Server the application is, in effect, an ASP.NET Core application, so the concept of Singleton, Transient and Scoped remains unchanged. In a Blazor Webassembly application, the code runs in the browser, so Scoped and Singleton tend to get confused, since, until you update the page, Scoped objects have the same duration as Singleton objects.
CSS isolation is also present in both frameworks: we have the possibility to write CSS rules that apply to a particular component, so the rest of the application will not be impacted. For example, we can decide that in our component the titles are red, without making red the titles of the other components of the application.
In the case of Angular you can have an array of CSS, SAAS or LESS files associated with your component, declaring the list in the definition of the component itself (styleUrls properties of the Component annotation).
In the case of Blazor, we work again by convention, calling the file <Namecomponent>.razor.css. This also means that at the moment you can have a single file and must necessarily be a CSS. Obviously we could intervene in the build phase to generate this file from a SAAS or LESS file, and we could then have more files, but the framework and its tools do not support us directly, for the moment.
A personal note: only use CSS Isolation if you need it or are making components for redistribution in a library. In a real application I would recommend the use of common CSS rules, perhaps based on a framework like Bootstrap. The stylization part is often complex, and partializing the rules hinders both the maintainability and the entry into the team of a figure that deals only with stylization.
In Angular you have two different libraries for the management of forms. The first one, present since the first version, is called Form Template, and provides you with a whole series of guidelines to solve the creation and management of forms on the markup part of your component. It’s the same library that allows you to define the famous bidirectional binding between your object and the form itself, which makes it easy, in the most common scenarios, the management of the capture of the user’s input and its validation, for which you can use HTML5 validators or create custom validators.
The possibility to create reactive forms has been added to this library, and it takes from that its name: Reactive Forms. You no longer have bidirectional binding (the performances sincerely thank)but the possibility to take advantage of the reactive approach to keep track of the state of the form and intervene at every change. In this case the form definition is all code side, providing guidelines to “hook” the form created on the markup elements.
In Blazor we have instead a series of components and events ready for the creation of forms. A form is delimited by the component <Editform>/Editform>, and we can insert within it the main UI elements already provided or specifically created . We have the classic text box (Inputtext), numerical box (Inputnumber), picker date (Inputdate), and so on. All these components allow bidirectional binding with an object using the @bind-Value attribute, after specifying the object instance with the Model property of the Editform. Using the <DataAnnotationValidator /> component you can add support for validation on the basis of the Data Annotation of the class from which the object containing your data was created.
You can find more information here, but we immediately notice that this is one of the killer feature of Blazor for those who have a back-end. NET: you can share these classes, decorated with data annotations, between back-end and front-end in a shared library and thus centralize not only the definition of these objects, but exploit the Form Blazor for the validation front-end, and the Model Binder of ASP.NET to repeat the validations also back-end side, operation that you should do in any case.
Blazor, like Angular, was primarily created to manage Business applications, so there’s actually so much more behind the scenes, including creating custom Form components, intervene in the binding and validation process using Editcontext instead of simply specifying the Model, up to the dynamic generation of the UI using the Reflection and Expression of LINQ. I held a session at Ugidotnet’s Webday on the subject (only available in italian) Dynamic UI generation with Blazor Webassembly.
Integration with the Back-end
Angular also provides a specific library for integration with the back-end, thanks to which you can inject in your service an Httpclient that allows you to invoke HTTP API in a very simple way. The library also provides the ability to sneak into the invocation process to do something before and/or after a call, such as logging in or adding headers to the request (the classic JWT token for example), defining what the framework calls HTTP Interceptor. More info here.
In Blazor you can use the HTTP client of . NET Core, including its JSON serialization/deserialization engine and everything you know about this library, which you will surely have already used in other scenarios. For Blazor are also provided some Extension Methods to simplify the invocation of REST API, more info here.
In Blazor you can also enter the HTTP invocation pipeline, defining the message handler HTTP client configuration using the AddMessageHandler() extension method of the Microsoft library.Extensions.Dependencyinjection. Again this is not a specific library of Blazor, but the HTTP client. You can learn more here.
Also, by using the gRPC-Web library, you can use grpc to invoke the back-end with both frameworks.
JS Interop and JS Isolation
In very large applications it can be a great advantage to partially download our code based on strategies other than the single initial download. This feature is called Lazy Load and is provided by both Angular and Blazor (starting with .NET 5).
Angular support for Lazy Load is complete: you can pull down entire modules on-demand by configuring the router. This way only when you first access the configured route, the code will be downloaded and executed. You can therefore prevent the downloading of entire forms if the user does not have access to that route or until it is accessed. With the introduction of Ivy, you can also make Lazy Load of individual components, instead of the entire module, as Salvatore clearly illustrated in his article on Angular 9.
In Blazor at the moment the support to Lazy Load is rather limited, and requires the subdivision of the code that you want to partially in different Dlls, going to specify at project level that you want to make the download of those Lazy DLLs. We are provided with a LazyAssemblyLoader object, which we can inject where we need it, to activate the functionality.
Despite being a first working approach, the Lazy Load provided in Blazor is still unripe and does not allow automatisms that relieve the programmer from managing the download on the basis of routing, which hopefully arrives, along with compilation AOT, with . NET 6.
It’s another matter with the reactive programming, which pervades the entire Angular framework thanks to RxJS.
This library introduces the ability to manage data streams over time, using the pattern Observer: thanks to the ability to work with observable objects, We can use a whole series of operators and create custom ones, to react when something happens in our application.
Despite all this firepower, this is just the least known and exploited part of Angular programmers. In addition, if you want, there is the counterpart Rx.NET to bring the Observable and its operators also in Blazor, even if, most of the time, you do not feel the need because of delegates and events provided natively by .NET.
A different issue is the applications for which can be an added value the use of a library of State Management. In a fully responsive approach, we can imagine having locally (in the browser I mean) a centralized store that represents the state of the application to which you can subscribe to be notified of changes. This store can only be modified by following a series of pure functions (to avoid side effects) that treat immutable data structures.
I regret trivializing the concept because it is very interesting, maybe we will talk about it in another article, but in my opinion it makes sense for very large applications that have a complex local logic, because it makes the maintainability of the application more sustainable, in the face of a higher technical debt.
In Angular there are libraries that have become de facto standards to manage such an approach, like Ngrx, which has a maturity and diffusion that can base its solutions quite lightly.
However, it remains the possibility to manage the application status even in simple applications, simply by going to keep in memory of the services or exploiting access to the storage of the browser. I leave you a link to the official documentation to get an idea.
We can conclude that, despite the difference in seniority, the two frameworks are equivalent in most of the medium/small business scenarios in which they are used. In medium/large scenarios instead Angular, thanks to the support of RxJS that dramatically boosts all its libraries and a more complete Lazy Load, is definitely better positioned.
But if the bulk of the logics to implement are CRUD and the back-end is written in . NET, the ability to share libraries between back-end and front-end, which centralize rules of validation and dynamic generation of the UI, thanks to standard and custom attributes, inevitably tilts the balance towards Blazor.
However, there is still a very important aspect to consider, the performances, in all its facets, so stay tuned for the third and final part of this comparison at the last bit.