Tumgik
#blazor c example
Text
ReBlogger
As someone who's had their nsfw blog terminated for far too many times, I've decided to take matters into my own hands & start working on a tool to help people back up & restore & manage their blogs by using a (web)app to leverage the Tumblr API.
The concrete goals of a v1.0 for this project would be to have the ability to:
Back up a list of blogs you are following, so that these can be refollowed on a new blog in case of termination through the application.
Download posts with specific tags, in order to store these posts, should they need to be reuploaded.
Reupload the stored posts to the new blog.
go through the user's followers, for example for my blog, check whether or not the followers have no blog title or description , think empty "untitled/sin titulo/unbeteitelt" blogs, and block them en-masse to purge these bots from your followers.
Current Nice-To-Haves:
The ability to quickly have an overview of your followers' title, description & pinned post if they have one, to create an easier way to vet blogs. Since it's etiquette to have your age clearly visible on your blog in the nsfw community, this should make this process a bit smoother.
The ability to have this app run as a service on your pc, locally, so that you don't need to manually start the app when you turn on your pc, this would enable me to work on auto-back up features.
For this application to be able to be hosted online, with either donations and/or ads to cover hosting services. Currently, if anyone were to try & use the app, they'd need to go through the process of registering their own application & change keys & secrets in the settings. While this isn't the most complicated process, it might be enough of a barrier for some.
Currently, I am working on the app with .net 6/C#, and I'm using Blazor for both frontend & backend. The Blazor frontend uses WASM/Webassembly. Blazor is a component based framework, like Angular or React, but uses C# instead of type-/javascript. Blazor server is very akin to ASP.NET WebAPI
On that note: if you are a .net c# dev, I can strongly suggest giving Blazor a try if you haven't. There is definitely some weirdness since it isn't as mature as Angular or React, but it makes it way more easy for you to get more mileage out of your backend code. Since you can keep the Client & Server in the same solution & share c# objects across both. API calls between the client & server are fairly easy & straightforward to make. It is, however, a departure from the ASP.NET ViewModel paradigm which can take some getting used to.
Please feel free to request any features or things i'd need to pay attention to & I'll consider adding them to the backlog.
Currently, i'm keeping the repo a bit secret in my back pocket, but once i've reached 1.0, i'll share the link with the repository & consider collaboration. I fully intend to open source the project so that the more savvy among you can go through the code & make more targeted suggestions.
2 notes · View notes
devleader · 3 months
Link
0 notes
compudicted · 1 year
Text
Book Review of Blazor WebAssembly by Example: Use practical projects to start building web apps with .NET 7, Blazor WebAssembly, and C# 11, 2nd Edition
This is a book focusing on the modern Web development on the Microsoft platform involving WASM which has recently gained a lot of traction. It seems to me that this technology is here to stay and evolve.What influenced me to buy the book in particular is that it is a second edition so it has to be very up-to-date, and ironically, the foreword by Scott Hanselman who I value as a technical leader…
View On WordPress
0 notes
ryadel · 2 years
Link
1 note · View note
dotnettricks · 2 years
Text
asp.net interview questions and answers
We’ll concentrate on ASP.NET and interview questions on asp.net in this post. We assume that you have experience with C# programming to condense the article’s scope. It is also anticipated that you have a basic grasp of standard object-oriented ideas and front-end technologies like HTML, CSS, and JavaScript. ASP.NET Interview Questions:
1) What is a web application?
Ans. Software that users may access using a web browser like Chrome or Firefox is known as a web application. The browser sends an HTTP request to access a specific URL for the web application. The user receives a dynamic HTML response built by the web application server after intercepting and processing the request. StackOverflow, Reddit, Google, and other well-known online apps are a few examples.
A web application differs from a standard webpage in several ways. Websites are static. The website returns an HTML page when you visit it without building its contents. If you reload the browser, the same page will appear. An online program, however, can provide a different result each time you use it.
Take the following scenario: You post a question on Stack Overflow. You will see your query when you first go to the URL. If another user responds to your query, the browser will show that response on your subsequent visit to the same URL.
A web application is made up of several distinct layers. A three-layered architecture consisting of display, business, and data levels is a typical example. For instance, the browser (presentation) speaks to the application server to retrieve the required data, which then connects to the database server. This is yet another common interview questions on asp.net.
2) What exactly is a web application framework, and what advantages does it Offer?
Ans.Learning how to build a modern web application could be scary. A standard set of features that most web applications provide include:
a. Create a dynamic response to match an HTTP request.
b. Permit users to log in and manage their data on the application.
c. Put the information in the database.
d. Connect to databases and manage transactions.
e. Send URLs to the proper procedures.
f. Supporting user authorization, sessions, and cookies.
g. Improve security and output formatting (e.g., HTML, JSON, XML).
Frameworks aid programmers in the creation, upkeep, and scaling of programs. They offer programs and libraries that make it easier to complete the before mentioned repetitive processes, reducing the amount of needless complexity.
3) What advantages does ASP.NET Core have over the original ASP.NET?
Ans. a) Cross-Platform:
The key benefit of ASP.NET Core is that, unlike the original ASP.NET framework, it is not dependent on the Windows operating system. Linux or a Mac may be used to create and operate production-ready ASP.NET Core applications. Since Windows licenses are not necessary when using an open-source operating system like Linux, you may save a lot of money.
b) Exemplary performance:
Additionally, it was created from the ground up with performance in mind. One of the quickest web application frameworks now.
c) Free Software:
Finally, it is open-source, and hundreds of developers actively contribute to it worldwide. Anyone may see, modify, and contribute to the source code, which is all accessible on GitHub. Despite the patches, bug fixes, and improvements made to the framework by contributors worldwide, it has led to a considerable increase in goodwill and confidence for Microsoft.
d) Modern technologies:
In addition to the conventional Model-View-Controller methodology, ASP.NET Core allows you to create apps utilizing cutting-edge technologies like Razor Pages and Blazor.
4) How does the HTTP protocol works?
Ans. An application-layer protocol called Hypertext Transfer Protocol (HTTP) is used to send hypermedia content like HTML. It manages the interaction between web servers and web browsers. HTTP uses the traditional client-server architecture. A client, such as a web browser, establishes a connection to send a request and waits for the server to respond.
The protocol HTTP makes it possible to retrieve resources, including HTML pages. It is a client-server protocol, which means that all requests for data transmission on the Internet are initiated by the recipient, which is frequently the Web browser.
5) What is a web server?
Ans. The words web server can describe software or hardware that functions alone or in tandem.
A web server is a machine with additional processing power and memory that keeps static assets like photos and JavaScript, CSS, and HTML files in addition to the back- end code for the application. Due to its internet connection, this computer enables the communication between many linked devices.
A web server is a piece of software that receives HTTP requests from clients like web browsers, processes them, and then sends back a response. The answer might be either static (an image or piece of text) or dynamic (a summation of the shopping cart's contents).
Web servers that are often used include Apache, Nginx, and IIS. If you take up asp.net training online, you will learn a lot about asp.
6) What is the NuGet package manager?
Ans. Not all of the code written by software engineers is original. They depend on programming community-created code libraries. Any contemporary development environment must offer a way for developers to obtain and utilize pre-existing libraries, sometimes called packages. For instance, the Node Package Manager (NPM) in the JavaScript ecosystem enables developers to identify and use libraries created by other JavaScript developers.
A package manager for the.NET ecosystem is called NuGet. Microsoft created it to give users access to thousands of.NET developer’s packages. It may also be used to distribute the code you’ve written.
Many open-source NuGet packages often support the functionality of an ASP.NET- developed web applications. Consider Newtonsoft. With 91,528,205 downloads as of this writing, the most widely used package for working with JSON data in.NET is JSON.
7) What is the goal of the Program class?
Ans. The main interface of our Program is the Program.cs class. A static void Main() method is where an ASP.NET program begins, much like a console application.
The request-handling web server is configured using this class. Application lifecycle management, including graceful shutdown, is the host's responsibility.
The host must configure a server and a pipeline for handling requests. The host also controls dependency injection, configuration, and logging.
8) What is the aim of the Startup class?
Ans. This class manages the middleware pipeline and service registration, two crucial components of your application.
Services are C# classes utilized by both the framework and your application to provide additional functionality. Databases and logging are some examples. When your Program is operating and in need of them, these services must be registered to be Instantiated.
Your application handles HTTP requests in a particular order using Middleware (the next question explains the concept of Middleware in detail).
Two methods are included in the Startup class: ConfigureServices() and Configure (). As its name indicates, the first technique registers every service the Program requires. Utilizing the second method, the middleware pipeline is set up.
9) What is the goal of the www-root folder?
Ans. The static files and built assets your web application needs, such as JavaScript, CSS, and pictures, are located in the www-root folder. The only folder in the whole project displayed to the browser in its current state is www-root.
10) What is caching?
Ans. Caching is the technique of temporarily storing data that is easier to reach than its an original place so that it may be obtained more rapidly the next time the same data is needed.
Your application's speed and scalability are enhanced by caching. It accomplishes this by making obtaining the data more accessible. Data that is expensive to develop and obtain and doesn’t change frequently might benefit from caching.
Caching options are available right out of the box with ASP.NET. For straightforward use cases, you may employ the IMemoryCache interface. It indicates a cache kept in the web server’s memory. Along with Redis, ASP.NET provides distributed caching, or a cache shared by several app servers.
Conclusion:
In this ASP.NET interview questions post, you learned about the traditional ASP.NET framework and its contemporary replacement, ASP.NET Core. The post covered various fundamental and sophisticated questions that might be asked during a job interview for a junior/intermediate developer position. We hope it will be helpful for your upcoming job interview!
0 notes
miralberry · 2 years
Text
Xl deployit
Tumblr media
#Xl deployit update#
#Xl deployit free#
#Xl deployit windows#
#Xl deployit update#
NetSparkle is a C# software update framework for. NET Core library for all your SIP, VoIP and WebRTC needs! Open Source Continuous File SynchronizationĪ real-time communications, cross platform, C#. NET Core version from the System Linq Dynamic functionality.Ī Json based Rules Engine with extensive Dynamic expression supportĪ tiny win10 (dynamic) wallpaper changer | 巨应壁纸 | 动态壁纸Īn ASP.NET Core web application exposing OPC UA Servers to non OPCUA-compliant clients with a REST interfaceĪ Node.js implementation of RTMP/HTTP-FLV/WS-FLV/HLS/DASH/MP4 Media ServerĪ fully customizable and extensible all-purpose diagrams library for Blazor Go语言开发的端口转发工具 for port data forward (TavenLi 李锡远)Ī lightweight IoT edge analytics softwareĪ lightweight RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/GB28181 server and client framework based on C++11 Net, LiveCharts2 can now practically run everywhere WPF, Xamarin, Avalonia, Uno (WIP), WinForms and UWP (WIP). Simple, flexible, interactive & powerful charts, maps and gauges for. NET core (it currently supports Kafka, RabbitMQ and MQTT).Ĭreate cross-platform (Xamarin, Windows. Silverback is a simple but feature-rich message bus for. SqlTableDependency extensions, Joker.OData, Joker.Redis, Joker.MVVM and ksqlDB LINQ provider Reactive data changes from SQL server to. High performance, cross platform ionic app for Home/Commerical Security Surveillance using ZoneMinderĮxpose a local server to the internet. Modern and flexible load testing framework for Pull and Push scenarios, designed to test any system regardless a protocol (HTTP/WebSockets/AMQP etc) or a semantic model (Pull/Push). NET Core lightweight configuration server YOLOv5 object detection with C#, ML.NET, ONNXįace analytics library based on deep neural networks and ONNX runtime.
#Xl deployit windows#
WeText is a sample application that demonstrates the implementation of DDD/CQRS and microservice architectural patterns in C#.Ī wrapper executable that can run any executable as a Windows service, in a permissive license.Ī remote control and remote scripting solution, built with. Payment,目前支持:支付宝(Alipay)、微信支付(WeChatPay)、QQ钱包(QPay)、京东支付(JDPay)、银联支付(UnionPay)ĭefinitely Fastest and Zero Allocation JSON Serializer for C#(NET. NET developers to easily add auto update functionality to their classic desktop application projects. Good luck demystifying the secrets of is a class library that allows.
You should be able to see the output of variable list on double clicking the script.
Now start a new deployment and open the Plan Analyzer after mapping the type to container.
You may change the type to deploy on any container type. This type will deploy on overthere.LocalHost by default.
Create new Application or include "test.scriptdeployable" type under existing package.
#Xl deployit free#
Copy the above free marker snippet into that file.
Create a folder script under XLDEPLOY_HOME/ext folder and create a new file called osscript.sh.ftl under that folder.
Or if you're just more interested in getting details of deployed, you can do thisĪ simple example to try this out would be TIP: Prefer keeping depth=0 or depth=1 if you prefer to use it with plugins other than XL-rules. Exception are generated while trying to find out values of certain properties who are unresolved at that point and throws exceptions. statics : is not explored since its not of type hash or a simple type step : when this is being explored, you'll see a lot of exceptions being generated in the log and it takes a lot of time but it would still show up in a while. NOTE: This won't work for jython step in XL-rules since that doesn't exposes the freemarker context IMPORTANT : If you use this with generic or another plugin except XL-rules, it will mostly include 3 top level variables that will be explored further. It gives you convenient output if you include it in a script that shows up in plan analyzer while planning for deployment. "Maybe $ Now this Freemarker snippet can be used wherever a free marker context is available. "What variable are exposed by Freemarker ?" A lot of new people starting on XL Deploy always get these questions in mind,"How do it get to know what variables i could use in my script ?"
Tumblr media
0 notes
nhjust · 2 years
Text
Silverlight for mac latest version
Tumblr media
#Silverlight for mac latest version full
#Silverlight for mac latest version code
The Uno platform is another option for Silverlight and there is a migration guide.
#Silverlight for mac latest version code
NET code cross-platform or in the browser. NET Core as well as Blazor means there are now a number of options for running. The existence of OpenSilver demonstrates the way in which Microsoft's various shifts of direction in its development platform has left some users stuck with technologies that have no future – see Visual Studio Tools for Office for another example. According to its press release: "The beta version can now make use of AoT compilation for up to 15 times improved performance compared to the first release." Blazor also runs C# in a web browser via WebAssembly, and OpenSilver makes use of it. The OpenSilver developers are also waiting anxiously for Ahead-of-Time (AoT) Compilation for Microsoft Blazor, coming in. Sometimes these companies offer a loosely equivalent HTML/JavaScript component in which case the code can be substituted. The company behind OpenSilver, Userware, warned that "many compilation errors are expected, since OpenSilver currently supports a subset of Silverlight functionality." There is also a problem with third-party components from the likes of Telerik or DevExpress.
#Silverlight for mac latest version full
Blazor: Full stack C# and Microsoft's pitch for ASP.NET Web Form diehardsĪccording to the docs: "The general principle for migrating a Silverlight application to OpenSilver consists of creating an OpenSilver-type project for each of the original Silverlight projects, then copying/pasting all the files from the original projects to the OpenSilver projects, and finally compiling the solution.".Last stop before MAUI: Xamarin Forms 5.0 released for cross-platform mobile, new features, new bugs.Cross-platform Windows Presentation Framework, anyone? The short answer: yes.Feeling saucy? Wave of Microsoft releases includes go-live licence for.It is open source and related to another project, CSHTML5, which compiles C# and XAML to JavaScript and HTML. OpenSilver is a reimplementation of Silverlight that runs in modern browsers using WebAssembly. There is another option (aside from rewriting everything in JavaScript). Determined users should also note that IE 11 goes out of support on 15 June 2022, though IE Mode in Edge continues. Silverlight applications may continue to work, but the installer will no longer be available. There is no longer support for Chrome, Firefox, or any browser using the Mac operating system." If your a Mac user running an older PowerPC setup (ie G5 or older system) or are running OSX on windows hardware, you prob noticed you cant install. "Silverlight development framework is currently only supported on Internet Explorer 10 and Internet Explorer 11, with support for Internet Explorer 10 ending on January 31, 2020. "Microsoft Silverlight will reach the end of support on October 12, 2021," the company stated.
Tumblr media
0 notes
webtrainingroom · 5 years
Link
Blazor Tutorial in Asp.net Core, Blazor Example in Asp.net Core C#, blazor application .net core, blazor c# example step by step
0 notes
mattn · 5 years
Text
.NET Core 3.0 が gRPC をサポートした。
「.NET Core 3.0」正式版が登場。Windowsデスクトップアプリ開発可能、exeファイルを生成、マイクロサービス対応など - Publickey
最新記事10本 Kafka開発元のConfluentに聞いた。エンタープライズ市場への道筋、大手クラウドとの現在の関係について 最大32コアのAMD EPYCプロセッサを搭載、コストパフォーマンスを高...
https://ift.tt/2mg6uiV
これまで通り、dotnet コマンドで SDK テンプレートを使って色々なアプリケーションを作成できる様になっている。dotnet 3.0 から使える SDK テンプレートは以下の通り。
使用法: new [options] オプション:   -h, --help          Displays help for this command.   -l, --list          Lists templates containing the specified name. If no name is specified, lists all templates.   -n, --name          The name for the output being created. If no name is specified, the name of the current directory is used.   -o, --output        Location to place the generated output.   -i, --install       Installs a source or a template pack.   -u, --uninstall     Uninstalls a source or a template pack.   --nuget-source      Specifies a NuGet source to use during install.   --type              Filters templates based on available types. Predefined values are "project", "item" or "other".   --dry-run           Displays a summary of what would happen if the given command line were run if it would result in a template creation.   --force             Forces content to be generated even if it would change existing files.   -lang, --language   Filters templates based on language and specifies the language of the template to create.   --update-check      Check the currently installed template packs for updates.   --update-apply      Check the currently installed template packs for update, and install the updates. Templates                                         Short Name               Language          Tags                                  ---------------------------------------------------------------------------------------------------------------------------------- Console Application                               console                  [C#], F#, VB      Common/Console                        Class library                                     classlib                 [C#], F#, VB      Common/Library                        WPF Application                                   wpf                      [C#]              Common/WPF                            WPF Class library                                 wpflib                   [C#]              Common/WPF                            WPF Custom Control Library                        wpfcustomcontrollib      [C#]              Common/WPF                            WPF User Control Library                          wpfusercontrollib        [C#]              Common/WPF                            Windows Forms (WinForms) Application              winforms                 [C#]              Common/WinForms                       Windows Forms (WinForms) Class library            winformslib              [C#]              Common/WinForms                       Worker Service                                    worker                   [C#]              Common/Worker/Web                     Unit Test Project                                 mstest                   [C#], F#, VB      Test/MSTest                           NUnit 3 Test Project                              nunit                    [C#], F#, VB      Test/NUnit                            NUnit 3 Test Item                                 nunit-test               [C#], F#, VB      Test/NUnit                            xUnit Test Project                                xunit                    [C#], F#, VB      Test/xUnit                            Razor Component                                   razorcomponent           [C#]              Web/ASP.NET                           Razor Page                                        page                     [C#]              Web/ASP.NET                           MVC ViewImports                                   viewimports              [C#]              Web/ASP.NET                           MVC ViewStart                                     viewstart                [C#]              Web/ASP.NET                           Blazor Server App                                 blazorserver             [C#]              Web/Blazor                            ASP.NET Core Empty                                web                      [C#], F#          Web/Empty                             ASP.NET Core Web App (Model-View-Controller)      mvc                      [C#], F#          Web/MVC                               ASP.NET Core Web App                              webapp                   [C#]              Web/MVC/Razor Pages                   ASP.NET Core with Angular                         angular                  [C#]              Web/MVC/SPA                           ASP.NET Core with React.js                        react                    [C#]              Web/MVC/SPA                           ASP.NET Core with React.js and Redux              reactredux               [C#]              Web/MVC/SPA                           Razor Class Library                               razorclasslib            [C#]              Web/Razor/Library/Razor Class Library ASP.NET Core Web API                              webapi                   [C#], F#          Web/WebAPI                            ASP.NET Core gRPC Service                         grpc                     [C#]              Web/gRPC                              dotnet gitignore file                             gitignore                                  Config                                global.json file                                  globaljson                                 Config                                NuGet Config                                      nugetconfig                                Config                                Dotnet local tool manifest file                   tool-manifest                              Config                                Web Config                                        webconfig                                  Config                                Solution File                                     sln                                        Solution                              Protocol Buffer File                              proto                                      Web/gRPC                              Examples:     dotnet new mvc --auth Individual     dotnet new --help
WinForms や WPF を使ったアプリケーションの開発もできる。すばらしい。
dotnet 3.0 で WinForms なアプリ動いた。 pic.twitter.com/46TkkM47SP
— mattn (@mattn_jp) September 24, 2019
以下の手順でアプリケーションを作成すると、SayHello というメソッドを持った Greeter サービスが作られる。
$ dotnet new grpc -o mygrpc
proto ファイルは以下の通り。
syntax = "proto3"; option csharp_namespace = "mygrpc"; package Greet; // The greeting service definition. service Greeter {   // Sends a greeting   rpc SayHello (HelloRequest) returns (HelloReply); } // The request message containing the user's name. message HelloRequest {   string name = 1; } // The response message containing the greetings. message HelloReply {   string message = 1; }
dotnet コマンドを使ってそのまま実��できる。
試しにこの proto ファイルから Go のクライアントを作って接続してみる。以下のコマンドで Go のクライアントが作られる。
$ go get github.com/golang/protobuf/protoc-gen-go $ protoc --go_out=plugins=grpc:. greet.proto
以下がそのクライアントを使ったサンプル。dotnet のサーバ側はポート 5000 番で通常ソケットの HTTP、5001 番で HTTP/2 で通信可能。dotnet run コマンドでは HTTP 通信のサーバは起動しないので dotnet build でビルドし、bin 配下にある exe ファイルを実行する。この exe ファイル出力も今回の .NET Core 3.0 の新しい機能になる。
package main import (     "fmt"     "log"     pb "github.com/mattn/grpc-greeter/Greet"     "golang.org/x/net/context"     "google.golang.org/grpc" ) func main() {     conn, err := grpc.Dial("127.0.0.1:5000", grpc.WithInsecure())     if err != nil {         log.Fatal(err)     }     /*         opts = append(opts, grpc.WithTransportCredentials(creds))     */     defer conn.Close()     client := pb.NewGreeterClient(conn)     hello := &pb.HelloRequest{         Name: "おるみん",     }     result, err := client.SayHello(context.Background(), hello)     if err != nil {         log.Fatal(err)     }     fmt.Println(result.GetMessage()) }
実行すると以下の結果が得られる。
Hello おるみん
from Big Sky https://ift.tt/2msTsz2
1 note · View note
Text
What is Blazer WebAssembly? And what is it used for?
Tumblr media
What is Blazer WebAssembly?
Blazer WebAssembly is another UI innovation from Microsoft, authoritatively delivered with .NET Core 3.1 and getting refreshed in .NET 5. Blazer permits engineers to make single-page applications (SPAs) utilizing C# and .NET using a part-based design. Blazer WebAssembly is a client-side in-program execution of Blazor, which incorporates a .NET runtime carried out in WebAssembly.
With the arrival of Blazer WebAssembly, it is presently conceivable to make customer-side SPAs utilizing C#. Beforehand it has been feasible to make sites utilizing ASP.NET Core MVC and Blazer Server, with every one of these contributions being worker side arrangements. In case you're hoping to extend your range of abilities and use some new Microsoft innovations, or simply have an overall interest in WebAssembly, then, at that point, Blazer WebAssembly is for you. Many web development companies help you in Blazor, Web assembly.
On the off chance that you come from a .NET foundation and need to foster a SPA, then, at that point, Blazer is a positive development. The advantage of composing a Blazer application is that you can utilize a ton of a similar form apparatus, biological systems, and language includes that you as of now use. Assuming you have loads of involvement in .NET, moving to another dialect (like JavaScript or TypeScript) and another structure (like Angular or React) requires a great deal of speculation that probably won't be achievable.
Following this instructional exercise, you will learn how to make a SPA in Blazer WebAssembly. It will cover the nuts and bolts of Blazor and its part-based engineering. The individuals who know about Razor will perceive a portion of the format highlights, and for those that aren't, this instructional exercise will cover highlights like mandates and occasions.
How do we develop web applications today?
For worker side turn of events, we use programming dialects like C#Java, PHP, and so forth. These are the worker-side programming dialects.
For the customer side improvement we use JavaScript systems like Angular, React, Vue and so forth There's no question these JavaScript structures overwhelmed customer side advancement up to this point.
To remain in the business as an engineer and stay cutthroat, it's inescapable that we learn both a worker-side programming language and a customer-side programming language.
In any case, the inquiry is, the reason we should learn and utilize 2 unique arrangements of programming dialects and systems.
Can we use C# both for server-side and client-side advancement?
With Blazer, we would now be able to fabricate intelligent web UIs utilizing C# rather than JavaScript. C# code can be executed both on the worker and in the customer program with many development services in it. This implies existing .Net designers can reuse their c# abilities instead of acquiring new JavaScript systems and their tremendous expectation to learn and adapt.
Programs comprehend and execute just JavaScript. How might we execute the c# code in the consumer program?
Blazer can run C# code straightforwardly in the program, utilizing WebAssembly. It runs in a similar security sandbox as JavaScript systems like Angular, Reacts, Vue, and so on Not only C#Truth be told, we can run any kind of code in the program utilizing WebAssembly.
WebAssembly depends on open web guidelines. So it is a local piece of all advanced programs including versatile programs. This implies for the blazer application to work, there is no compelling reason to introduce any exceptional module like back in the times of silver light and glimmer.
This is additionally called the customer side facilitating model and in this model, the application runs straightforwardly in the program on WebAssembly. Thus, all the application requires i.e the arranged application code itself, its conditions, and the .NET runtime are downloaded to the program. We will see this in real life in our forthcoming videos. The worker-side programming dialects.
For the customer side advancement we use JavaScript structures like Angular, React, Vue and so on There's no question these JavaScript structures ruled customer side improvement up to this point.
However, the inquiry is the reason we should learn and utilize 2 unique arrangements of programming dialects and structures.
All things considered, we can and that is by and large why we use Blazer. With Blazer, we would now be able to fabricate intuitive web UIs utilizing C# rather than JavaScript.This implies existing .Net engineers can reuse their c# abilities instead of acquiring new JavaScript structures and their immense expectation to absorb information.
This is additionally called the worker facilitating model and in this model, the application is executed on the worker from inside an ASP.NET Core application.
At the point when an occasion happens on the customer, for example, a catch click, for instance, the data about the occasion is shipped off the worker over the SignalR association.
The whole HTML isn't sent back again to the customer, it's just the diff that is shipped off the customer over the setup SignalR association. The program then, at that point refreshes the UI. Blazer accepts the single page application engineering which reworks a similar page powerfully in light of the client activity. Since just the diff is applied to refresh the UI, the application feels quicker and more receptive to the client.
We utilize the Blazer Server App layout, to make a Blazer application with the worker facilitating model. We will make a blazer application with both the facilitating models and talk about the upsides and downsides exhaustively in our impending recordings.
What is Blazer Web assembly Used for?
Indeed, since, supposing that we use Blazer, we can run our application in any program, including portable ones, because WebAssembly is important for all significant programs. We are at this point not reliant upon modules like we were some time ago with Flash and Silverlight. Also, we use Blazor because it permits us to reuse our C# abilities.
C# is an incredible advancement language and specifically which implies we can get the mistake at assemble time rather than runtime. Blazer is only a structure that suddenly spikes in demand for the .NET runtime, so we can utilize any library that we need to use, as long as it is viable with .NET Standard.
This implies that we can utilize our own libraries and furthermore practically all open NuGet bundles
There are several in number reasons, which show why we should utilize Blazer
WebAssembly is upheld by all significant programs
Use C# for intuitive web applications
Reuse existing libraries
Execution is close to local
Tooling and troubleshooting
You can check and pick the apparatus according to your accommodation. The following are alternatives, to begin with, Blazer.
Blazer WebAssembly runs on the customer in the program. It downloads all that it requires to the program including HTML, CSS, perhaps some JavaScript, and conceivably pictures. It likewise downloads the congregations that make up the application, and it even downloads the total .NET runtime that is changed over into WebAssembly bytecode.
The entirety of this runs totally on WebAssembly, which is a piece of all significant internet browsers, including portable programs. This kind of Blazor application needn't bother with an association with a worker. It simply needs to stack into the program, and that is it.
A Blazer WebAssembly application comprises static records, so you needn't bother with an undeniable worker to get this to the program. You could have the static records on a substance conveyance organization, or CDN, which is somewhat modest and is internationally performant. You can likewise have it on a worker in the cloud, in your own server farm, or elsewhere.
Benefits
Close Native Performance, running your application on web assembly is quick.
The application can work totally disconnected.
No Server is Needed, you don't need to keep up with the worker and keep the design basic. it requires asset handling at customer gadget
No Plugin is required, WebAssembly is a local piece of every significant program. it takes all static documents into the program
Run in every Modern Browser
Disadvantages
Limited to capabilities of the program. It downloads everything into the program including .NET runtime.
The program accomplishes basically everything.
More to download plan to longer load time.
Mystery or Key needs an association with API, it needs to download at the client-side and there is an approach to encode which isn't protected.
Required WebAssembly, time your application running to the old program which doesn't have web assembly and application won't work all things considered.
Blazer Server is a worker-side Blazor application. The application runs inside an ASP.NET site that suddenly spikes in demand for the .NET runtime. This site then, at that point serves the Blazer site and loads Blazor through a WebSocket association that it utilizes through SignalR. Utilizing this, UI refreshes are spilled from the worker continuously. This sounds confusing, yet you don't need to do anything exceptional to make this work. This all emerges from the case.
Source:
https://fuerte-developers.medium.com/what-is-blazer-webassembly-and-what-is-it-used-for-af0fbd1b140a
0 notes
tak4hir0 · 5 years
Link
Every market is ruled by certain common concepts, and JavaScript development is no exception. The product lifecycle is a concept that you can apply to several different environments to understand and predict their behavior. It is a business concept that helps us understand the stages that a product goes through during its life, explaining the impact of these stages on its popularity measure—in most cases, sales. If we observe market behavior patterns, we can estimate the current stage of a product and therefore make some predictions about its popularity. There are four stages: introduction, growth, maturity, and decline, and on the chart above, you can see the impact on expected product sales for each stage. For example, smartphones sales aren’t growing like five years ago—actually, quite the opposite is true—so we can fairly say that smartphones are getting into their maturity stage. In the past few years, we’ve seen the introduction of a lot of new technologies in JavaScript, but we needed time to see how the market was going to adopt them. Nobody wants to be the specialist on another promising technology that ends with zero adoption. Now, however, is the time to take another look. In this article, I will take a look at how popular JavaScript is becoming and the factors that may have affected this popularity, and I will try to predict what the future of JavaScript will look like. The Future of JavaScript Language Features Since the European Computer Manufacturers Association (ECMA) established the year-based release cycle for ECMAScript, a standardized JavaScript specification, we haven’t seen a lot of new features coming to the language—just a few each year. This could be one of the reasons we saw an increase of adoption of languages that compile to ES5 like TypeScript or ReasonML, both bringing features to the language that are highly requested by the community. This is not new—JavaScript went through this process before (CoffeeScript) and, in the end, those features ended up being merged into the language standard itself, and that’s probably the future that we can expect for these new typed features, too. But now we are starting to see a game changer move in the compile-to-js market with the increasing availability of WebAssembly in the browsers. Now, we can use almost any language and compile it to run at almost native speed in a browser and, more importantly, we are starting to see support for future-proof features like support for threads that will allow us to take advantage of the multi-processor architecture that represents the inevitable future of all devices. The official toolchain for WebAssembly will help you to compile C/C++, but there are a lot of community provided compilers for different languages, like Rust, Python, Java, and Blazor (C#). Particularly, the Rust community is pretty active and we started to see complete front-end frameworks like Yew and Dodrio. This brings a lot of new possibilities to browser-based apps, and you only need to test some of the great apps built with WebAssembly to see that near-native browser-based apps are a reality now, e.g., Sketchup or Magnum. Adoption of typed languages that compile to ES5 is mature enough, the players are well established, and they won’t disappear (or be merged with ES) in the near future, but we’ll see a slow shift in favor of typed languages with WebAssembly. Web Front-end Frameworks Every year, we see a big fight on the front-end frameworks market for the web, and React has been the indisputable winner for the past few years—since the introduction of their game-changer technology, the Virtual DOM, we saw an almost obligated adoption from their counterparts in order to remain relevant in the battle. Some years ago, we saw the introduction of a radical new approach to web application development with Svelte, the “compiler framework” that disappears at compile time leaving small and highly efficient JavaScript code. However, that feature was not enough to convince the community to move to Svelte, but with the recent launch of Svelte 3.0, they introduced real reactive programming into the framework and the community is thrilled, so perhaps we are witnessing the next big thing in front-end frameworks. Inspired by the destiny operator: var a = 10; var b <= a + 1; a = 20; Assert.AreEqual(21, b); Svelte brings reactivity to JavaScript by overloading the use of label statements with reactivity at compile time by instructing the code to be executed in topological order: var a = 10; $: b = a + 1; a = 20; Assert.AreEqual(21, b); This is a radical new idea that might help in different contexts, so the creator of Svelte is also working on svelte-gl, a compiler framework that will generate low-level WebGL instructions directly from a 3D scene graph declared in HTMLx. Needless to say that React, Angular, and Vue.js won’t disappear overnight, their communities are huge, and they’ll remain relevant for several years to come—we are not even sure if Svelte will be the actual successor, but we can be sure of something: We’ll be using something different sooner or later. WebXR and the Future of the Immersive Web Virtual reality has been struggling for the past 60 years to find a place in the mainstream, but the technology was just not ready yet. Less than ten years ago, when Jon Carmack joined Oculus VR (now part of Facebook Technologies, LLC), a new wave of VR started to rise, and since then, we’ve seen a lot of new devices supporting different types of VR and of course the proliferation of VR-capable applications. Browser vendors didn’t wanted to lose this opportunity, so they joined with the WebVR specification allowing the creation of virtual worlds in JavaScript with WebGL and well-established libraries like three.js. However, the market share of users with 6dof devices was still insignificant for massive web deployments, but the mobile web was still able to provide a 3D experience with the device orientation API, so we saw a bunch of experiments and a lot of 360 videos for a while. In 2017, with the introduction of ARKit and ARCore, new capabilities were brought to mobile devices and all sorts of applications with AR and MR experiences. However it still feels a little unnatural to download one specific app for one specific AR experience when you are exploring your world around you. If we could only have one app to explore different experiences… This sounds familiar. We solved that problem in the past with the browser, so why not give it another shot? Last year, Mozilla introduced the WebXR Device API Spec (whose last working draft, at the time of this writing, is from two weeks ago) to bring AR, VR, and MR (ergo XR) capabilities to the browser. A few of the most important browser vendors followed with their implementation, with an important exception: Safari mobile, so to prove their point, Mozilla released a WebXR capable browser under the iOS platform WebXR Viewer. Now, this is an important step because the combination of AR and VR brings 6dof to mobile devices and mobile device-based headsets like Google Cardboard or the Samsung Gear VR, as you can see in this example, increasing the market share of 6dof devices by a large margin and enabling the possibility of a large-scale web deployment. At the same time, the guys at Mozilla have been working on a new web framework to facilitate the creation of 3D worlds and applications called A-Frame, a component-based declarative framework with HTML syntax based on three.js and WebGL, having just one thing in mind—to bring back the fun and ease of use to web programming. This is part of their crusade to the immersive web, a new set of ideas on how the web should look like in the future. Luckily for us, they are not alone, and we’ll start to see more and more immersive experiences on the web. If you want to give it a try, go ahead download the WebXR Viewer and visit this site to see the possibilities of the immersive web. Once again, standard browser-based apps won’t fade in a year or two—we’ll probably always have them. But 3D apps and XR experiences are growing and the market is ready and eager to have them. Native Support for ES6 Almost every technology invented in JavaScript in the past decade was created to solve problems generated by the underlying implementation of the browsers, but the platform itself has matured a lot over these past few years, and most of those problems have disappeared, as we can see with Lodash, which once reigned the performance benchmarks. The same is happening with the DOM, whose problems once were the actual inspiration for the creation of web application frameworks. Now, it is a mature API that you can use without frameworks to create apps—actually, that’s what web components are. They are the “framework” of the platform to create component-based apps. Another interesting part of the platform evolution is the language itself. We’ve been using Babel.js for the past few years to be able to use the latest features of ECMAScript, but since the standard itself started to stagnate a little bit in the last few years, that was enough time to allow the browser vendors to implement most of their features, including native support of the static import statement. So now, we can start to consider the creation of applications without Babel.js or other compilers since we have (again) the support of the language features in the platform it self, and since Node.js uses the same V8 VM as Google Chrome, we’ve started to see stronger support of ES6 in Node.js, even with the static import statement under the experimental-modules flag. This doesn’t mean that we’ll stop seeing apps being compiled at a professional level, but it means that starting with a browser-based application will be easy and fun as it once was. Server-side JavaScript Even though JavaScript started with server side in 1995 with the Netscape Enterprise Server, it wasn’t until Ryan’s Dahl presentation in 2009 that JavaScript started to be seriously considered for server-side apps. A lot of things happened in the past decade to Node.js. It evolved and matured a lot, creating once again the opportunity for disruption and new technologies. In this case, it comes from the hand of its very own creator, Ryan Dahl, who has been working on a new perspective of server-side secured apps with Deno, a platform that supports natively the latest language features as async/await, and also the most popular compile-to-js language TypeScript, targeting the best performance thanks to their implementation in Rust and the usage of Tokio, but more importantly with a new security philosophy that differentiates it from most of the server-side platforms like Python, Ruby, or Java). Inspired by the browser security model, Deno will let you use the resources of the host only after the user explicitly granted the permissions to the process, which might sound a bit tedious at the beginning, but it might result in a lot of implications by allowing us to run unsecured code in a secured environment by just trusting the platform. Node.js will still be there in the future but may be we’ll start to see serverless services like AWS Lambda and Azure Functions to provide the Deno functionality as an alternative to provide unsecured server-side code execution on their systems. Conclusion These are exciting times in the JavaScript world—a lot of technologies have matured enough to leave space for innovation, the active community never stopped to amaze us with their brilliant and incredible ideas, and we expect a lot of new alternatives to well-established tools since their mature stages are arriving quickly; we won’t stop using them since a lot of them are really good and there is plenty of proof in the battlefield, but new and exciting markets will start to emerge, and you’d better be prepared. Staying up to date with the latest in JavaScript world isn’t easy, because of the pace of development, but there are some sources that can really help. First, the most important news source, in my opinion, is Echo JS, where you can an incredible amount of new content every hour. However, if you don’t have the time, the JavaScript Weekly newsletter is an excellent summary of the week in JS. Besides this, it is also important to keep an eye on the conferences around the world, and YouTube channels like, JSConf, React Conf, and Google Chrome Developers are wonderfully helpful. Conversely, if you’re interested in seeing some constructive critique of where JavaScript is heading, I recommend reading As a JS Developer, This Is What Keeps Me Up at Night by fellow JavaScript developer Justen Robertson.
0 notes
just4programmers · 6 years
Text
What is Blazor and what is Razor Components?
I've blogged a little about Blazor, showing examples like Compiling C# to WASM with Mono and Blazor then Debugging .NET Source with Remote Debugging in Chrome DevTools as well as very early on asking questions like .NET and WebAssembly - Is this the future of the front-end?
Let's back up and level-set.
What is Blazor?
Blazor is a single-page app framework for building interactive client-side Web apps with .NET. Blazor uses open web standards without plugins or code transpilation. Blazor works in all modern web browsers, including mobile browsers.
You write C# in case of JavaScript, and you can use most of the .NET ecosystem of open source libraries. For the most part, if it's .NET Standard, it'll run in the browser. (Of course if you called a Windows API or a Linux specific API and it didn't exist in the client-side browser S world, it's not gonna work, but you get the idea).
The .NET code runs inside the context of WebAssembly. You're running "a .NET" inside your browser on the client-side with no plugins, no Silverlight, Java, Flash, just open web standards.
WebAssembly is a compact bytecode format optimized for fast download and maximum execution speed.
Here's a great diagram from the Blazor docs.
Here's where it could get a little confusing. Blazor is the client-side hosting model for Razor Components. I can write Razor Components. I can host them on the server or host them on the client with Blazor.
You may have written Razor in the past in .cshtml files, or more recently in .razor files. You can create and share components using Razor - which is a mix of standard C# and standard HTML, and you can host these Razor Components on either the client or the server.
In this diagram from the docs you can see that the Razor Components are running on the Server and SignalR (over Web Sockets, etc) is remoting them and updating the DOM on the client. This doesn't require Web Assembly on the client, the .NET code runs in the .NET Core CLR (Common Language Runtime) and has full compatibility - you can do anything you'd like as you are not longer limited by the browser's sandbox.
Per the docs:
Razor Components decouples component rendering logic from how UI updates are applied. ASP.NET Core Razor Components in .NET Core 3.0 adds support for hosting Razor Components on the server in an ASP.NET Core app. UI updates are handled over a SignalR connection.
Here's the canonical "click a button update some HTML" example.
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" onclick="@IncrementCount">Click me</button> @functions { int currentCount = 0; void IncrementCount() { currentCount++; } }
You can see this running entirely in the browser, with the C# .NET code running on the client side. .NET DLLs (assemblies) are downloaded and executed by the CLR that's been compiled into WASM and running entirely in the context of the browser.
Note also that I'm stopped at a BREAKPOINT in C# code, except the code is running in the browser and mapped back into JS/WASM world.
But if I host my app on the server as hosted Razor Components, the C# code runs entirely on the Server-side and the client-side DOM is updated over a SignalR link. Here I've clicked the button on the client side and hit the breakpoint on the server-side in Visual Studio. No there's no POST and no POST-back. This isn't WebForms - It's Razor Components. It's a SPA app written in C#, not JavaScript, and I can change the locations of the running logic, while the UI remains always standard HTML and CSS.
Looking at how Razor Components and now Phoenix LiveView are offering a new way to manage JavaScript-free stateful server-rendered apps has me realizing it’s the best parts of WebForms where the postback is now a persistent websockets tunnel to the backend and only diffs are sent
— Scott Hanselman (@shanselman) March 16, 2019
It's a pretty exciting time on the open web. There's a lot of great work happening in this space and I'm very interesting to see how frameworks like Razor Components/Blazor and Phoenix LiveView change (or don't) how we write apps for the web.
Sponsor: Manage GitHub Pull Requests right from the IDE with the latest JetBrains Rider. An integrated performance profiler on Windows comes to the rescue as well.
© 2018 Scott Hanselman. All rights reserved.
Tumblr media Tumblr media Tumblr media Tumblr media Tumblr media
0 notes
devopsengineer · 3 years
Text
Azure pipelines docker
Azure pipelines docker Azure pipelines docker News stories Azure pipelines docker Publishing to Azure Container Registry using Azure Pipelines My book, Blazor in Action – an example-driven guide to building client-side web apps using C# and .NET – is now available to buy via the Manning Early Access Program (MEAP). Containerising Blazor Applications With Docker (Part 3) Part 1 – Containerising…
Tumblr media
View On WordPress
0 notes
devleader · 10 months
Link
0 notes
erossiniuk · 3 years
Text
Working with Blazor’s component model
Welcome to Working with Blazor’s component model” post! In this new post I’ll build a simple project in Blazor and explain the basic Blazor components and interactions. Also, the source code of the project I’m going to create in this post is available on GitHub.
Here the posts I wrote about Blazor that help you to learn this new technology better and faster:
Getting Started With C# And Blazor
Setting up a Blazor WebAssembly application
Working with Blazor’s component model
Table of contents
What is a Blazor component?
Structuring Components
Single file
HomePage code
Code explained
Visual Studio and Razor
Partial class
Pros of separating UI and logic
Cons of separating UI and logic
Component lifecycle methods
The first render
Render explained
The lifecycle with async
Dispose – The extra lifecycle method
Communicating between parent and child components
Passing values from a parent to a child
The TrailDetails component
Define a component parameter
Drawer implementation
Updating the HomePage component
Passing data from a child to a parent
Code explained
Styling components
Global styling
Why a global CSS?
Scoped styling
Example of scoped styling
Global styles can still have an effect
Using CSS preprocessors
Integrating a CSS preprocessor
Integrate tools
Conclusion
What is a Blazor component?
First, the fundamental building blocks of Blazor applications are components, almost everything you do will directly or indirectly work with them. In order to build great applications, you must know how to harness their power.
So, components define a piece of UI, that can be something as small as a button or as large as an entire page, components can also contain other components. They encapsulate any data that that piece of UI requires to function. They allow a piece of UI that you can reuse across an application or even shared across multiple applications.
Then, you can pass data into a component using parameters. Parameters define the public API of a component. The syntax for passing data into a component using parameters is like defining attributes on a standard HTML element, with a key/value pair. The key being the parameter name and the value being the data you wish to pass to the component.
Also, the data a component holds is more commonly referred to as its state. Methods on a component define its logic. These methods manipulate and control that state via the handling of events.
Also, we can style the components via traditional global styling; however, it is more common to use scoped styles. Scoped styles allow the component to define its own CSS classes without fear of collision with other styles in the application. It’s even possible to use CSS pre-processors such as Sass with scoped styling.
The new component for drawer in our application
Now, we’ll be adding a cool new slide out drawer feature to Blazor Trails. The drawer will slide out from the right-hand side of the page when the user clicks on a button we’ll add to the TrailCard component. The drawer will display more detailed information about the selected trail. When the user clicks the close button on the drawer it will cleanly slide back out of view.
Structuring Components
As you will find with almost every part of Blazor, there are multiple ways of doing things. The Blazor team have been very deliberate with making the framework unopinionated. So, developers can build applications the way that works best for them.
It is possible to define a component in a single .razor file that contains both its markup and logic. Also, it is possible to separate a component into a .razor file which defines the markup and a C# class which defines the logic.
Single file
When using a single file approach all mark-up and logic for a component is defined in a single file. The primary advantage of this approach is that it allows you to work with everything in one place. This can really help with productivity as you don’t need to keep swapping back and forth between multiple files.
Single file is the default when creating new components.
HomePage code
@page "/" @inject HttpClient Http @if (_trails == null) { <p>Loading trails...</p> } else { <div class="row row-cols-1 row-cols-md-2"> @foreach (var trail in _trails) { <div class="col mb-4"> <TrailCard Trail="trail" /> </div> } </div> } @code { private IEnumerable<Trail> _trails; protected override async Task OnInitializedAsync() { try { _trails = await Http.GetFromJsonAsync<IEnumerable<Trail>>("trails/trail-data.json"); } catch (HttpRequestException ex) { Console.WriteLine($"There was a problem loading trail data: {ex.Message}"); } } }
Code explained
The code should look familiar, this is the Blazor Trails home page component. The entire component is defined in a single .razor file with the markup coming first then the logic coming second, defined in the code block.
So, having everything in a single file, it allows me to work faster as I don’t have to switch files. But there is another benefit which I find useful, monitoring component size.
When building applications, it’s easy to create very large components which are doing lots of things. However, just like when creating regular C# classes, you should try to keep your components focused, with a single purpose.
One way I use to gauge this is the size of my component files. When I find I’m constantly scrolling up and down a file adding markup and logic, it’s an indication that my component may be doing too much and I should be thinking about splitting it out into additional components with more focused responsibility. This isn’t a clear-cut method however, there are times when a component may be quite large but still only have one responsibility, but it at least makes me think about it.
One argument I often hear against this method is that mark-up and logic should be separated because otherwise we’re mixing concerns. I disagree with this view. The logic in a component should be logic which operates over the mark-up and drives the function of the component. Business logic has no place in components.
If you take this view then the logic and mark-up are intertwined, they are tightly coupled, one can’t exist without the other. In which case separating them seems to fall into the same category as organizing an applications files by type rather than feature, and this is inefficient and hinders productivity.
Visual Studio and Razor
There is one drawback of this approach at the moment, and that is the functionality of the Razor editor in Visual Studio. There are several key refactoring abilities which don’t work in razor files, namely quick actions and refactorings. Renaming of variables can be a bit sketchy and only work some of the time. This is due to the fact the Razor editor in Visual Studio is not fully support Blazor yet.
However, the tooling teams are currently completely rebuilding the Razor tooling experience from the ground up to include all the rich functionality that developers have come to expect when working with regular C# class files. Once this work is complete, the experience working in Razor files should be on par with that of C# class files.
Partial class
Another approach is to split the markup and logic of a component into two separate files. The markup of the component is kept in the .razor file, the logic is added to a C# class. In earlier versions of Blazor it was only possible to apply this approach using inheritance as there was no support for the partial keyword, this is no longer the case.
Let’s take a look at the home page component refactored to use this approach.
@page "/" @if (_trails == null) { <p>Loading trails...</p> } else { <div class="row row-cols-1 row-cols-md-2"> @foreach (var trail in _trails) { <div class="col mb-4"> <TrailCard Trail="trail" /> </div> } </div> }
Now the logic for the component.
using Microsoft.AspNetCore.Components; using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; namespace BlazorTrails.Features.Home { public partial class HomePage : ComponentBase { private IEnumerable<Trail> _trails; [Inject] public HttpClient Http { get; set; } protected override async Task OnInitializedAsync() { try { _trails = await Http.GetFromJsonAsync<IEnumerable<Trail>>("trails/trail-data.json"); } catch (HttpRequestException ex) { Console.WriteLine($"There was a problem loading trail data: {ex.Message}"); } } } }
As you can see, using this technique you can make the two elements of the component completely separate. You should also note the naming of the files, HomePage.razor and HomePage.razor.cs. If you’re using Visual Studio to build your applications, following this naming convention will produce a nested effect.
 Naming a partial class the same as the markup portion of the component will produce a nested effect when using Visual Studio
Pros of separating UI and logic
This makes it easier to work with the component. It also keeps the number of files being displayed in the IDE to a minimum as you can simply hide any partial classes you’re not currently interested in.
The major benefit of separating the markup and logic of the component is the development experience. As I mentioned when talking about the single file approach, the razor editor is not as fully featured as when working with regular C# class files. By separating out the logic to a C# class file, developers can access all the editor features.
Cons of separating UI and logic
The drawback of this approach is that you now have two files to manage when you’re working with a component. This can end up with lots of switching back and forth as you will need to be in the logic file when adding methods or other members to the component. Then you will need to be in the mark-up file to add any UI, hook up event handlers, etc.
Largely which approach you choose for building your applications is a personal choice based on which method you find most productive.
Component lifecycle methods
Just as in other component-based frameworks, components in Blazor have a lifecycle.
Tumblr media
Blazor component lifecycle methods
Depending on what an application is doing, it may need to perform actions at certain points during this lifecycle. For example, load initial data for the component to display when it is first created, or update the UI when a parameter has a certain value from the parent. Blazor supports this by giving us access to the component lifecycle at specific points.
Initialized component: OnInitialized/OnInitializedAsync
Parameter set: OnParametersSet/OnParametersSetAsync
After Render: OnAfterRender/OnAfterRenderAsync
The lifecycle methods are provided by the ComponentBase class which all components inherit from. Each method has a synchronous and asynchronous version. The synchronous version is always called before the asynchronous version.
<h1>Componet Lifecycle</h1> <p>Check the browser console for details...</p> @code { public override async Task SetParametersAsync(ParameterView parameters) { Console.WriteLine("SetParametersAsync - Begin"); await base.SetParametersAsync(parameters); Console.WriteLine("SetParametersAsync - End"); } protected override void OnInitialized() { Console.WriteLine("OnInitialized"); } protected override async Task OnInitializedAsync() { Console.WriteLine("OnInitializedAsync"); } protected override void OnParametersSet() { Console.WriteLine("OnParametersSet"); } protected override async Task OnParametersSetAsync() { Console.WriteLine("OnParametersSetAsync"); } protected override void OnAfterRender(bool firstRender) { Console.WriteLine($"OnAfterRender (First render: {firstRender})"); } protected override async Task OnAfterRenderAsync(bool firstRender) { Console.WriteLine($"OnAfterRenderAsync (First render: {firstRender})"); } }
The first render
During the first render, all the component’s lifecycle methods will be called – during subsequent renders only a subset of the methods will run.
The process starts with SetParametersAsync being called. This is the only lifecycle method which requires us to call the base method, if we don’t then the component will fail to load. This is because the base method does two essential things:
Sets the values for any parameters the component defines – This happens both the first time the component is rendered and whenever parameters could have changed
Calls the correct lifecycle methods depending if the component is running for the first time or not
If we removed the call to the base method the output in the browser console would look like this.
Render explained
During a first render, the component hasn’t been initialized. This means that OnInitialized and OnInitializedAsync will be called first – it is also the only time they will run. This pair of methods are the only ones which run once in a component’s lifetime. You can think of these as constructors for your component. It makes them a great place to make API calls, for example, to get the initial data the component will display.
Once the OnInitialized methods have run, OnParametersSet and OnParametersSetAsync are called. These methods allow developers to perform actions whenever a components parameters change. In the case of a first render, the components parameters have been set to their initial values.
The final methods to run are OnAfterRender and OnAfterRenderAsync. These methods both take a Boolean value indicating if this is the first time the component has been rendered. On the initial render, the value of firstRender will be set to true for every render after, it will be false.
void OnAfterRender(bool firstRender) Task OnAfterRenderAsync(bool firstRender)
This is useful as it allows one-time operations to be performed when a component is first rendered, but not on subsequent renders. The primary use of the OnAfterRender methods are to perform JavaScript interop and other DOM related operations such as setting the focus on an element.
The lifecycle with async
One key point about the render we just covered was that it ran synchronously. In the Lifecycle component there are no awaited calls in any of the async lifecycle methods, meaning each method ran in sequence. However, when async calls are added then things look a bit different. To demonstrate this let’s update the OnInitializedAsync method in the Lifecycle.razor component to make an async call.
protected override async Task OnInitializedAsync() { Console.WriteLine("OnInitializedAsync - Begin"); await Task.Delay(300); Console.WriteLine("OnInitializedAsync - End"); }
While Blazor was awaiting the async call, the component was rendered. You can see it was then rendered a second time after the OnParametersSet methods, as before. This is because Blazor checks to see if an awaitable task is returned from OnInitializedAsync, if there is it calls StateHasChanged to render the component with the results of any of the asynchronous code which has been run so far, while awaiting the completion of the task. This behavior is also true for async calls made in OnParametersSetAsync.
Dispose – The extra lifecycle method
There is another lifecycle method which we can use but this one is optional and it’s not built-in to the ComponentBase class, Dispose. This method is used for the same purposes in Blazor as in other C# applications, to clean up resources. This method is essential when creating components which subscribe to events, as failing to unsubscribe from events before a component is destroyed will cause a memory leak.
In order to access this method a component must implement the IDisposable interface.
@implements IDisposable <h1>Componet Lifecycle</h1> <p>Check the browser console for details...</p> @code { // Other methods ommitted for brevity public void Dispose() { Console.WriteLine($"Dispose - Begin"); Console.WriteLine($"Dispose - End"); } }
To see the effect of this new lifecycle method we need to navigate away from the component, this will remove it from the DOM and invoke the Dispose method.
Blazor understands the IDisposable interface, when it detects its presence on a component it will call the Dispose method at the correct point when destroying the component instance.
As of .NET 5, Blazor also supports the IAsyncDisposable interface. This allows disposal of resources asynchronously. This is useful when using JavaScript interop. But for now, note that IDisposable and IAsyncDisposable can’t both be implemented on the same component.
Communicating between parent and child components
A great analogy for components is Lego blocks. Each Lego block is a self-contained unit, but the real fun comes when you plug the blocks together in order to build something bigger and better. This is the same for components, they can be useful on their own, but they are more powerful when used together. In order to do this in any meaningful way, components need to be able to communicate with each other, passing data, and firing and handling events.
In Blazor, we achieve this using component parameters. Component parameters are declared on a child component which forms that components API. A parent component can then pass data to the child using that API. But component parameters can also be used to define events on the child that the parent can handle. This allows data to be passed from the child back up to the parent.
We’ll add a view button to the TrailCard which when clicked, will slide open a drawer on the right of the application. This drawer will display more detailed information about the selected trail. For this to work we will need to have three different components communicate and pass data.
The HomePage component will coordinate the operation. It will handle any OnSelected events from the TrailCard component. When an OnSelected event is raised, the HomePage component will record the selected trail and pass it into the TrailDetails component. Inside the TrailDetails component, whenever the trail value changes, it will be the trigger for the drawer to active and slide into view.
Passing values from a parent to a child
In order to build our new drawer, we need to create a component which takes in a trail and then displays its information. We will use a component parameter to create its API.
The TrailDetails component
The TrailDetails component will display the selected trail which is passed in via a component parameter.
<div class="drawer-wrapper @(_isOpen ? "slide" : "")"> <div class="drawer-mask"></div> <div class="drawer"> @if (Trail != null) { <div class="drawer-content"> <div class="trail-image"> <img src="@Trail.Image" /> </div> <div class="trail-details"> <h3>@Trail.Name</h3> <h6 class="mb-3 text-muted"><span class="oi oi-map-marker"></span> @Trail.Location</h6> <div class="mt-4"> <span class="mr-5"><span class="oi oi-clock mr-2"></span> @Trail.Time</span> <span><span class="oi oi-infinity mr-2"></span> @Trail.Length km</span> </div> <p class="mt-4">@Trail.Description</p> </div> </div> <div class="drawer-controls"> <button class="btn btn-secondary" @onclick="@(() => _isOpen = false)">Close</button> </div> } </div> </div> @code { private bool _isOpen; [Parameter] public Trail? Trail { get; set; } protected override void OnParametersSet() { if (Trail != null) { _isOpen = true; } } }
Define a component parameter
A component parameter is defined as a public property which is decorated with the Parameter attribute. Blazor uses this attribute to find component parameters during the execution of the SetParametersAsync lifecycle method we looked at earlier in the chapter. During this lifecycle method, the reflection sets the parameter values.
We’re using the OnParametersSet lifecycle method to trigger the drawer sliding into view. As we learned earlier, this lifecycle method is run every time the components parameters change. This makes it perfect for our scenario as we can use it to trigger opening the drawer.
Drawer implementation
Opening and closing the drawer is done using CSS. When a new trail is passed in, the isOpen field is set to true, this triggers the logic at the top of the component to render the slide CSS class.
<div class="drawer-wrapper @(_isOpen ? "slide" : "")">
In the app.css file (this is found in wwwroot > css folder) we need to add the styles to the bottom of the file.
.drawer-mask { visibility: hidden; position: fixed; overflow: hidden; top: 0; right: 0; left: 0; bottom: 0; z-index: 99; background-color: #000000; opacity: 0; transition: opacity 0.3s ease, visibility 0.3s ease; } .drawer-wrapper.slide > .drawer-mask { opacity: .5; visibility: visible; } .drawer { display: flex; flex-direction: column; position: fixed; top: 0; right: 0; bottom: 0; width: 35em; overflow-y: auto; overflow-x: hidden; background-color: white; border-left: 0.063em solid gray; z-index: 100; transform: translateX(110%); transition: transform 0.3s ease, width 0.3s ease; } .drawer-wrapper.slide > .drawer { transform: translateX(0); } .drawer-content { display: flex; flex: 1; flex-direction: column; } .trail-details { padding: 20px; } .drawer-controls { padding: 20px; background-color: #ffffff; }
The two key parts of the styles above are the transform: translateX properties on the .drawer and .drawer-wrapper.slide > .drawer classes. Without these properties, the drawer would sit in its open position, in full view. Figure 3.12 shows the effect of the properties on the drawer.
The transform property on the .drawer class repositions the drawer off the right-hand side of the screen by 110% of its width. The transform property on the .drawer-wrapper.slide > .drawer class repositions it back to its default, bringing it into view.
Updating the HomePage component
To pass the trail into the TrailDetails component, we use attributes when defining the component in the parent. The parent for us is the HomePage component. Listing 3.10 shows the HomePage component updated with the TrailDetails component.
@page "/" @inject HttpClient Http @if (_trails == null) { <p>Loading trails...</p> } else { <TrailDetails Trail="_selectedTrail" /> <div class="row row-cols-1 row-cols-md-2"> @foreach (var trail in _trails) { <div class="col mb-4"> <TrailCard Trail="trail" /> </div> } </div> } @code { private IEnumerable<Trail> _trails; private Trail? _selectedTrail; protected override async Task OnInitializedAsync() { try { _trails = await Http.GetFromJsonAsync<IEnumerable<Trail>>("trails/trail-data.json"); } catch (HttpRequestException ex) { Console.WriteLine($"There was a problem loading trail data: {ex.Message}"); } } }
In the HomePage component we have defined a field called _selectedParameter which will store the selected trail. We then pass this into the TrailDetails component using an attribute style syntax.
<TrailDetails Trail="_selectedTrail" />
The attribute name matches the component parameter we defined on the TrailDetails component. It is important that the case also matches otherwise Blazor will consider it a regular HTML attribute and ignore it. If you’re using an IDE such as Visual Studio for Windows or Mac, or JetBrains Rider you will receive IntelliSense to help you do this. Visual Studio Code also has IntelliSense support for Blazor via the C# extension.
Passing data from a child to a parent
We have successfully used a component parameter to define the API of the TrailDetails component, but we can’t see the fruit of our labor yet. In order to see something happen on screen we need to be able to select a trail to display. To do this we need to be able to pass that information up from the TrailCard component to the HomePage component.
To do this we are going to use component parameters to define an event on the TrailCard. This event will pass the selected trail, the HomePage component can then handle this event and pass the trail to the TrailDetails component to display.
<div class="card" style="width: 18rem;"> <img src="@Trail.Image" class="card-img-top" alt="@Trail.Name"> <div class="card-body"> <h5 class="card-title">@Trail.Name</h5> <h6 class="card-subtitle mb-3 text-muted"><span class="oi oi-map-marker"></span> @Trail.Location</h6> <div class="d-flex justify-content-between"> <span><span class="oi oi-clock mr-2"></span> @Trail.Time</span> <span><span class="oi oi-infinity mr-2"></span> @Trail.Length km</span> </div> <button class="btn btn-primary mt-3" @onclick="@(() => OnSelected?.Invoke(Trail))">View</button> </div> </div> @code { [Parameter] public Trail Trail { get; set; } [Parameter] public Action<Trail> OnSelected { get; set; } }
We define the event as a delegate of type Action<Trail>. This allows us to pass the trail which this TrailCard is displaying back to the parent component. This happens when the View button is clicked. We handle the buttons click event using Blazor’s @onclick event.
With the TrailCard updated all that’s left to do is handle the event in the HomePage component. First, we need to add a method to the code block which will be called whenever an event is raised.
private void HandleTrailSelected(Trail trail) { _selectedTrail = trail; StateHasChanged(); }
Code explained
This method accepts the selected trail, assigns it to the _selectedTrail field. However, in order to see anything happen we must call StateHasChanged – this is to let Blazor know that we need the UI to update. The reason we must do this manually, is that Blazor can’t know the intent of our code. It has no idea that our custom event should trigger a re-render of the UI.
There are some cases where this manual control over re-renders is preferred, however, in most cases this is just an extra line of code which must be added to achieve the desired effect. There is another way. We can use a different type to define our event on the TrailCard called EventCallback. By using this type for our event, Blazor will automatically call StateHasChanged on the component which handles the event, removing the need to manually call it.
To take advantage of this we can update the component parameter on TrailCard and update how the event is invoked.
<div class="card" style="width: 18rem;"> <img src="@Trail.Image" class="card-img-top" alt="@Trail.Name"> <div class="card-body"> <h5 class="card-title">@Trail.Name</h5> <h6 class="card-subtitle mb-3 text-muted"><span class="oi oi-map-marker"></span> @Trail.Location</h6> <div class="d-flex justify-content-between"> <span><span class="oi oi-clock mr-2"></span> @Trail.Time</span> <span><span class="oi oi-infinity mr-2"></span> @Trail.Length km</span> </div> <button class="btn btn-primary mt-3" @onclick="@(async () => await OnSelected.InvokeAsync(Trail))">View</button> </div> </div> @code { [Parameter] public Trail Trail { get; set; } [Parameter] public EventCallback<Trail> OnSelected { get; set; } }
Then, we can simply remove the StateHasChanged call from our handler in the HomePage component:
private void HandleTrailSelected(Trail trail) { _selectedTrail = trail; }
The final update is to assign the HandleTrailSelected method to the OnSelected event. We do this the same way we did to pass the selected trail into the TrailDetails component, using attributes.
<TrailCard Trail="trail" OnSelected="HandleTrailSelected" />
If all has gone to plan, then clicking the view button should trigger the drawer and display the trail. Clicking the close button at the bottom of the drawer will close it and allow a new trail to be selected.
Styling components
The styling is an important element to building any application and a powerful tool in delivering great UX. Look at the drawer we just built, the ability for it to slide in and out of the viewport was achieved using CSS, not C#.
There are two approaches to styling component-based applications such as Blazor:
Global styling
Scoped styling
As you would expect, global styles are classes which are declared on the global scope and can apply to any element which uses that class name or meets the selector for that class. Scoped styles are the opposite, a stylesheet is created for a specific component and any classes defined in it are made unique to that component using a unique identifier produced during the build process.
No matter which of these approaches you take to style your application, it is possible to combine it with CSS preprocessors. CSS preprocessors like SASS, allow CSS to be written in a more modular and maintainable way – taking advantage of features such as variables and functions.
Global styling
Global styling is the default method when building applications. This is how we have been styling Blazing Trails up to this point. To apply global styling, one or more stylesheets are added to the host page which, by default, is index.html in Blazor WebAssembly and _Host.cshtml in Blazor Server. The styles defined in those stylesheets are then available throughout the application.
Global styles are fantastic for creating a consistent look and feel across an application. For example, if all buttons needed to be blue with certain font size and rounded corners. This can be defined once in a global style and would apply to all buttons in the app:
button { font-size: 1rem; background-color: blue; border-radius: .25rem }
This makes global styles an incredibly powerful tool because if we wanted to change how the buttons, or any aspect of the applications design looked, we can change the styles in one place, and the application is immediately updated.
Why a global CSS?
This global scope of styles can also cause some issues when developing larger applications. For example, if we wanted a certain button to be green with square corners rather than the global blue style above, we would need to add another style to the stylesheet. We would then need to apply the style to the particular button. That doesn’t seem too bad, but think of this happening many times over, you end up with a stylesheet which is full of one-off styles or niche styles. You could say this is down to bad design or lack of maintenance, which would be fair, but it still doesn’t stop it happening.
Making changes to global styles can also be cumbersome. Constantly scrolling up and down a stylesheet with 100’s of lines of style classes can become tedious. Especially, when changes need to be made in multiple places.
There are mitigations for this of course, using a CSS preprocessor like SCSS allows the global styles to be broken up and kept next to the component they are for in the project structure. This makes working with them much easier and more efficient. There is also another option which has come about with the rise of SPA frameworks, scoped CSS.
Scoped styling
Scoped styling works by allowing a developer to create styles which only effect a certain component in the application – this is done by creating a stylesheet with the same name as the component. During the build process, Blazor generates unique IDs for each component and then the styles for that component are rescoped using each ID.
To get a feel for this, let’s rework the styles for the TrailDetails component we just built to use scoped CSS. To do this, we first need to create a new stylesheet called TrailDetails.razor.css, then take all the styles we added to app.css for the TrailDetails component and move them to this file.
It is important that we name the file this way otherwise Blazor won’t pick it up and associate its styles with the TrailDetails component. If you’re using Visual Studio, a nice effect of this naming convention is the file nesting in Solution Explorer.
Example of scoped styling
When using scoped CSS, there will be a lot of stylesheets dotted around the application. Adding each and every one of them to the host page would be tedious and difficult to maintain. So, what Blazor does as part of the build process is bundle all the styles from the various stylesheets into a single stylesheet. This means we just need to reference that one stylesheet in our host page. The file has a naming convention of [ProjectName].styles.css. As our project is called BlazingTrails.Web, the file will be called, BlazingTrails.Web.styles.css. Listing 3.13 demonstrates where to reference the file.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>BlazingTrails.Web</title> <base href="/" /> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> <link href="BlazingTrails.Web.styles.css" rel="stylesheet" /> </head>
If we run the project and select a trail to open the drawer, we can use the browsers dev tools to look at the HTML and styles produced.
Tumblr media
Inspecting the HTML of the application in a browser shows a unique ID applied to each HTML element belonging to the TrailDetails component
Each HTML element belonging to the TrailDetails component now has a unique attribute applied to it. This attribute follows the format of b-[uniqueID]. If we then select an element to inspect its styles.
Each selector for the styles in the TrailDetails.razor.css file has been rewritten to use the unique ID Blazor generated for the component. Doing this is what scopes the style to that component and stops the style effecting another element in another component.
Global styles can still have an effect
If you use scoped styles and nothing else in your application, then what I’m about to say isn’t an issue. However, if you have some global styles and some scoped styles then you may still run into issues.
To give an example, let’s say we had the following CSS class called .drawer in our global CSS file, in addition to the one we have in the TrailDetails components scoped stylesheet:
.drawer { border: 5px solid lawngreen; }
As you can see, using scoped styles doesn’t make components immune from the standard behavior of CSS. This is something to think about when deciding on how to style your components, mixing global and scoped styles could make things more complicated.
Using CSS preprocessors
Whether you choose to use global styles, scoped styles, or a mix of both, you can still leverage the power of CSS preprocessors. Preprocessors work in a similar way as TypeScript does for JavaScript, as a superset language. They provide access to a richer feature set than CSS provides alone.
There are many options out there when it comes to CSS preprocessors, but the main players are:
LESS (http://lesscss.org/)
SASS/SCSS (https://sass-lang.com/)
Stylus (https://stylus-lang.com/)
They all provide similar feature sets which offer the following enhancements over regular CSS, just with different syntaxes.
Mixins – reusable groups of styles
Variables – works the same way as variables in C#
Nesting – the ability to define the scoped of a style by writing it within another
Import – allows us to organize large CSS files into smaller more focused files. Then import common aspects such as variables.
Choosing a preprocessor largely comes down to which syntax you prefer. My favorite preprocessor is SCSS (https://sass-lang.com/). It has a syntax very similar to regular CSS which makes everything easy to read. Also, it has been around for a very long time so there’s lots of documentation and blog posts out there to help if you get stuck.
Integrating a CSS preprocessor
I’m going to show you how to integrate SCSS into a Blazor app, specifically when using scoped CSS. There are two ways we can integrate SCSS into our application, using JavaScript tools or not using .NET tools.
If you don’t want to use any JavaScript tools in your application, then I would suggest of the following options.
WebCompiler from Mads Kristensen (https://marketplace.visualstudio.com/items?itemName=MadsKristensen.WebCompiler). This hasn’t had any meaningful updates for a couple of years, but it does still appear to work.
WebCompiler by excubo-ag (https://github.com/excubo-ag/WebCompiler). This is forked from Mads WebCompiler and is looking like a promising project. It uses a dotnet CLI tool to perform the compilation of SCSS files and ties in with MSBuild. However, it currently only supports SCSS. Meaning if you are using a different preprocessor such as LESS or Stylus you are out of luck. Configuration is a bit difficult and there is limited documentation.
The option I prefer, and I’m going to show you, is to use a mix of NPM and MSBuild. This does require having an up to date version of NodeJS installed which can be downloaded at https://nodejs.org/. The version I’m using is 14.15.0 and is the latest LTS version.
Integrate tools
We’re going to use a tool called dart-sass (https://sass-lang.com/dart-sass) which we can install as an NPM package called sass (https://www.npmjs.com/package/sass). We’re then going to use MSBuild to call this tool during the build process, specifically at the start of the build process. This is important as we need to compile our SCSS files to CSS before Blazor’s compiler runs so it can pick up the compiled CSS files and bundle them into the single [ProjectName].style.css file we talked about earlier.
This new SCSS version of the TrailDetails styles only has one slight modification, it’s using the nesting feature from SCSS. It will allow us to confirm that the compilation steps worked and that the SCSS generates CSS.
Conclusion
Finally, we did it! We understand how working with Blazor’s component model and it works. If you have any problem, you have the source code on GitHub. If you have any question, please use the forum.
Happy coding!
The post Working with Blazor’s component model appeared first on PureSourceCode.
from WordPress https://www.puresourcecode.com/dotnet/blazor/working-with-blazors-component-model/
0 notes
erossiniuk · 3 years
Text
Setting up a Blazor WebAssembly application
Welcome to “Setting up a Blazor WebAssembly application” post! In this new post I’ll build a simple project in Blazor and explain the basic Blazor components and interactions. The source code of the project I’m going to create in this post is available on GitHub.
Here the posts I wrote about Blazor that help you to learn this new technology better and faster:
Getting Started With C# And Blazor
Blazor is a framework for building interactive client-side web UI with .NET:
Create rich interactive UIs using C# instead of JavaScript.
Share server-side and client-side app logic written in .NET.
Render the UI as HTML and CSS for wide browser support, including mobile browsers.
Integrate with modern hosting platforms, such as Docker.
Using .NET for client-side web development offers the following advantages:
Write code in C# instead of JavaScript.
Leverage the existing .NET ecosystem of .NET libraries.
Share app logic across server and client.
Benefit from .NET’s performance, reliability, and security.
Stay productive with Visual Studio on Windows, Linux, and macOS.
Build on a common set of languages, frameworks, and tools that are stable, feature-rich, and easy to use.
So, here we’re going to start off by looking at the available templates provided by Microsoft to create a new application. Templates are a great way to get started quickly and provide all of the primary building blocks we need for a working an application. Once we have an understanding of the options, we’ll then choose a template as the base for our Blazor Trails app. We’ll build and run the template so we can get a feel for how it behaves, then we’ll strip out all of the unnecessary parts leaving us with only the key components.
As a result, this is a screenshot of the web application I’m going to explain.
Blazor Trails home page: final
Table of contents
Setting up the application
Blazor WebAssembly template configurations
Standalone mode
Hosted mode
Creating the application
Building and running the application
Local SSL certificate
Key components of a Blazor application
Index.html
More details about Index.html
Base tag
Program.cs
More details about Program.cs
Dependency injection
App.razor
Router component
wwwroot folder & _imports.razor
Writing your first components
Organizing files using feature folders
Routable component
Defining the layout
Main layout
The Blazor Trails home page
Prepare the data
The first injection
Read the data
JsonAsync methods
Waiting for the data
Refactor
Conclusion
Setting up the application
So, in other frameworks setting up a new application involves creating everything manually, from scratch. Generally speaking, .NET applications aren’t created this way. Many, if not all, start life being generated from a template. Using a template has many advantages:
Developers can have a working application in seconds
Boilerplate code is taken care of and doesn’t need to be written for every new application
The template serves as a working example of using the framework
The process is repeatable, using a template will give you the same starting point time and time again
Blazor comes with two templates which can be used to create new applications. When choosing a template, we’re essentially making the choice of which hosting model we want to use, either Blazor Server or Blazor WebAssembly. In fact, the two available templates are named Blazor Server and Blazor WebAssembly which makes knowing the hosting model they use pretty straightforward. However, we’re going to be using Blazor WebAssembly to build our Blazor Trails application so that is the template type we’re going to focus on in this post.
Blazor WebAssembly template configurations
Before we actually create the application, I want to talk about the configuration options available for the Blazor WebAssembly template. This template is the more complex of the two available because you can configure it in two modes: hosted or standalone.
Tumblr media
The left side shows the projects created when configuring the template in hosted mode. The right shows the project created when configuring the template in standalone mode.
Standalone mode
In the standalone mode, which is the default configuration, you will end up with a single Blazor WebAssembly project in the solution. This template is great if you’re looking to build an application which doesn’t need any kind of backend or server element to it, or perhaps you already have an existing API.
Hosted mode
Hosted mode is a little bit more complex. If you enable the ASP.NET Core Hosted option when creating the template, you will end up with three projects in the solution:
Blazor WebAssembly project
ASP.NET Core WebAPI project
.NET Standard class library
In this configuration you are getting a full stack .NET application. A fully functioning backend (ASP.NET Core WebAPI), a place to put code which is shared between the frontend and backend project (.NET Standard class library), and the frontend application (Blazor WebAssembly).
So, I want to highlight that using this configuration does require a .NET runtime to be installed on the host server. You may remember in my previous post I mentioned an advantage of using Blazor WebAssembly was it didn’t require a .NET runtime on the server, that benefit doesn’t apply when you’re using the hosted configuration. This is because there is a full ASP.NET Core WebAPI project in the solution which does need a .NET runtime on the server to function.
Creating the application
So, I want to create a Standalone mode application. There are two way to create a new application using a template, the dotnet CLI (Command Line Interface) or via an IDE (Integrated Development Environment) such as Visual Studio. To create the application, open Visual Studio and follow these steps (there may be slight differences in wording or order of screen on other IDEs):
File > New Project.
From the project templates list select Blazor WebAssembly App.
The next screen allows us to set the name of the project and the solution as well as where the files will be saved on disc. Enter the details and then click Next to move to the next step.
Select .NET5 as Target Framework
Create a new Blazor WebAssebly app
Configure your new Blazor project
Additional information
Be sure, ASP.NET Core hosted and Progressive Web Application are not checked.
This will create a new application with the same configuration and folder structure we setup using Visual Studio. At this point you’ve created your first Blazor application, congratulations! Now we have our shiny new application we’re going to look at how we can build and run it.
Building and running the application
When it comes to running .NET applications there are 3 steps that need to happen:
Restore any packages (also referred to as dependencies)
Compile or build the application
Fire up a web server and serve the application
In previous versions of .NET Core these steps needed to be performed manually so you would need to first restore any packages, then build the code, and finally run the app. However, this is no longer the case, we can now jump straight to running the application and either Visual Studio or the .NET CLI will take care of performing the first two steps, if they’re required.
However, it’s always good to understand how to perform these steps yourself manually if the need arises. When using Visual Studio, you can restore packages by right clicking on the solution and selecting Restore NuGet Packages from the context menu. If you’re using the .NET CLI then you can execute the dotnet restore command.
To perform a build from Visual Studio, select Build > Build Solution from the top menu. You can also use a keyboard shortcut to perform the same task, Ctrl+Shift+B. From the .NET CLI you can use the dotnet build command. Performing a build will also perform a package restore, if it’s required, both when using Visual Studio or the CLI. So, having to manually restore packages shouldn’t be an issue.
All that’s left is to run the application. From Visual Studio this can be done in several ways. First, you can press the play button found in the main toolbar. You can also use a keyboard shortcut which is F5. Finally, you can select Debug > Start Debugging from the top menu. Any of the above will run the application and Visual Studio will fire up a browser and load the application automatically.
Local SSL certificate
Depending on what types of application you’ve created and run before, you could see an extra step which asks if you want to trust the development SSL certificate. Answering yes to this will install the development certificate on your machine and this allows the application to be run over https rather than http. I would recommend trusting and installing the development SSL certificate as running sites over https is best practice, even in development as it mimics the live environment.
First run of a boilerplate Blazor app
Key components of a Blazor application
While the template has generated a fair few files, some of these files are more important to know and understand than others. In this section, we’re going to look at each of those key files to understand what they do and why they’re important. Then we’re going to remove all of the other files from our project to give us a clean base ready to start building Blazor Trails.
Index.html
This is one of the most important components of a Blazor WebAssembly application. It can be found in the wwwroot directory of the project and it’s the host page for the Blazor application.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>BlazorTrailsWA</title> <base href="/" /> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" /> <link href="css/app.css" rel="stylesheet" /> <link href="BlazorTrailsWA.styles.css" rel="stylesheet" /> </head> <body> <div id="app">Loading...</div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.webassembly.js"></script> </body> </html>
8: the tag base is used by Blazors router to understand which routes it should handle
15: the app tag is where the Blazor application will load
19: this div is displayed automatically by Blazor when an unhandled exception occurs
22: Blazors JavaScript runtime which downloads and initializes the application
More details about Index.html
The key element in the index.html file is the link to the Blazor JavaScript runtime, found near the bottom of the page. As we saw previously, this is the file which downloads the .NET WebAssembly based runtime as well as the application and any of its dependencies. Once this is complete it also initializes the runtime which loads and runs the application.
When the application runs its content needs to be outputted somewhere on the page and, by default, this is outputted to the app tag. This is configurable and is setup in the Program.cs file which we will look at in a second. Any default content which exists in the tag will be replaced at runtime with the output from the application. This has a useful benefit; initial content can be used as a placeholder which will be displayed to the user until the application is ready.
If there is ever an unhandled exception caused inside the application, then Blazor will display a special UI which signals to the user that something has gone wrong. This is defined here in the index.html. This can be customized however you would like but the containing element much have an id attribute with the value blazor-error-ui. The default message states there has been a problem and offers the user a button which will cause a full page reload. This is the only safe option at this point as the application will be in an unknown state.
Exception bar in a Blazor application
Base tag
The final key piece to the index.html file is the base tag. This is an important tag when it comes to client-side routing. The reason this tag is important is that is tells Blazors router what URLs, or routes, are in scope for it to handle.
If this tag is missing or configured incorrectly then you may see some unexpected or unpredictable behavior when navigating your application. By default, the tag is configured with a value of /. This means that the application is running at the root of the domain (for example www.puresourcecode.com). The router should handle all navigation requests within that domain.
However, if the application was running as a sub-application for example https://www.puresourcecode.com/blazortrails, then the base tag would need to reflect this with a value of /blazortrails/. This would mean the router will only handle navigation requests which start with /blazortrails/.
Program.cs
Just like other ASP.NET Core applications, Blazor apps start off as .NET console apps. What makes them a Blazor application is the type of host they run. In the case of Blazor WebAssembly it runs a WebAssemblyHost. The purpose of the code contained in this file is to configure and create that host, figure 2.9 shows the default configuration of the Program class.
public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); } }
17: create an instance of WebAsseblyHostBlazor
18: define the root component for the application
20: configure and register services with the IServiceCollection
24: build and run an instance of WebAssemblyHost using the configuration defined with the WebAssemblyHostBuilder
More details about Program.cs
There are two critical pieces of configuration happening, the root component for the application is defined and any services are configured and added to the IServiceCollection.
When defining the root components – there can be more than one although that is usually not the case in most applications – we are actually giving the builder two pieces of information.
The first is the type of the root component for the application. By default, this is the App component (which we will look at next). However, you can configure this to be any component you wish.
The second is the place in the host page where we want to inject the application. The standard setup has the application being injected into the app element we looked at previously on the index.html page. But again, you can configure this to be injected anywhere you wish. The argument the builder.RootComponents.Add method takes is a CSS selector which is used to identify the target element where the component will be injected. Specific elements can be targeted such as app or elements with a specific ID, for example, #root-component, or any other valid CSS selector.
Dependency injection
The next line shows the HttpClient being configured and registered with the IServiceCollection making it available to classes and components via dependency injection (DI). Blazor uses the same DI container as other ASP.NET Core apps and allows registering of services using one of 3 lifetimes:
Transient – A new instance is provided each time it’s requested from the service container. Given a single request, if two objects needed an instance of a transient service, they would each receive a different instance.
Scoped – A new instance is created once per request. Within a request you will always get the same instance of the service across the application.
Singleton – An instance is created the first time it’s requested from the service container, or when the Program.Main method is run, and an instance is specified with the registration. The same instance is used to fulfil every request for the lifetime of the application.
The last thing that the Main method does is to take all of the configuration specified with the WebAssemblyHostBuilder and call its Build method. This will create an instance of a WebAssemblyHost which is the heart of your Blazor app. It contains all of the application configuration and services needed to run your app.
App.razor
This is the root component for a Blazor application, and we saw how this was configured in the Program.Main method in the previous section. This doesn’t have to be the case however; you can configure a different component to be the root component if you wish, or even have multiple root components, you just need to update the configuration in the Program.Main method.
Router component
The App component contains a vital component for building multi-page applications, the Router component. The Router component is responsible for managing all aspects of client-side routing. When an application first starts up, the router will use reflection to scan the applications assemblies for any routable components. Then, it stores information about them in a routing table and whenever a link is click or navigation is triggered programmatically, the router will look at the route which has been requested and try and find a routable component which handles that route. If a match is found, then it will load that component, otherwise it will load a not found template which is specified inside the Router component.
wwwroot folder & _imports.razor
Now, I’m going to cover both of these files in this section as there is not a huge amount to say about them. In fact the _imports.razor file is the one component on this list which is not required to run a Blazor application, but it makes things a lot easier if you do use it.
By convention all ASP.NET Core applications have a wwwroot folder which is used to store public static assets. This is the place where you can put things such as images, CSS files, JavaScript files or any other static files you need. Anything you put in this folder will be published with your application and available at runtime. As I mentioned earlier, this is also where the index.html file is kept.
The _imports.razor file is optional and not required when building a Blazor application, however, it’s really useful to have at least one of these files. The _imports.razor file has a simple job, it contains using statements. What is really great about the way this file works is that it makes those using statements available to all of the components in that directory and any sub-directories. This saves you having to add common using statements to every component in your application.
As I alluded to, you can also have multiple version of this file at different points in your folder structure. So, if you had a structure of BlazorTrails > Features > Home, and you only wanted specific using statements to be applied to components in the Home folder. Then you could add a _Imports.razor file in the Home folder with those using statements and they would only apply there but would still inherit any using statements from _Imports.razor files higher in the structure.
Writing your first components
We’ve had a look at the app created by the template, and we’ve covered each of the key files and, at a high level, what they do. Now it’s time to write some code of our own. As I said at the start of the chapter, we’re going to be building the foundations of the Blazor Trails application.
List of trails
The first thing we’ll do is take about how the application files are going to be organized. Next, we’ll remove all of the unneeded files which were generated by the template. This will give us a clean base to start building from. We’ll then define several new components to create what you see, a layout component, a page component and a couple of regular components.
Organizing files using feature folders
Before we start adding our own code, we need to remove all of the unnecessary files generated by the template. By default, the app structure used by the template divides files by responsibility. There’s a Pages folder for routable components. There is a Shared folder for anything which is used in multiple places or is a global concern.
This kind of separation doesn’t scale well and makes adding or changing functionality much more difficult as files end up being spread out all over the place. Instead we’re going to use a system called feature folders to organize our application.
When using feature folders all of the files relating to that feature are all stored in the same place. The has two major benefits, first, when you go to work on a particular feature all of the files you need are in the same place making everything easier to understand and more discoverable.
The second is that it will scale well, every time you add a new feature to the app you just add a new folder, and everything goes in there. You can also arrange each feature with sub-features if they contain a lot of files.
Routable component
The other little thing I like to do when using this organization system with Blazor is to append any routable component with the word Page. The reason is when a feature has several components in it it’s almost impossible, at a glance, to see which one is the routable component. The only real way to know is to open the file and check for the @page directive at the top.
So, start by deleting the Pages and Shared folders along with their contents, then delete the sample-data folder from the wwwroot folder. Also delete most of the contents of the app.css, just leave the import statement for the open iconic styles and the class called #blazor-error-ui and #blazor-error-ui .dismiss.
We also need to delete the last using statement from the _Imports.razor file, @using BlazorTrailsWA.Shared.
Add a new folder at the root of the project called Features, then inside that add a folder called Layout and another called Home. Inside Layout, add a new Razor Component called MainLayout.razor. Inside Home add a new Razor Component called HomePage.razor. Once you’ve done that head back over to the _Imports.razor and add the following using statements.
@using BlazingTrails.Web.Features.Home @using BlazingTrails.Web.Features.Layout
Defining the layout
Blazor borrows the concept of a layout from other parts of ASP.NET Core and essentially it allows us to define common UI which is required by multiple pages. Things such as the header, footer and navigation menu are all examples of things you might put in your layout. We also add a reference to a parameter called Body where we want page content to be rendered. This comes from a special base class which all layouts in Blazor must inherit from called LayoutComponentBase. The following image shows an example of what might be defined in a layout along with where the rendered page content would be displayed.
Tumblr media
An example layout defining shared UI
You don’t have to stick with a single layout for your whole application either, you can have multiple layouts for different parts of your app. So, if you wanted a particular layout for the public facing pages but a different one for the admin pages, you can do that. In Blazor the default layout is defined on the Router component. This will automatically be applied to all pages in the application.
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
In line 3 and 6 you see the default layout is defined by passing the type of the component you wish to use. If you want to use a different layout on certain pages, you can specify an alternative by applying the @layout directive. This goes at the top of the page and you pass the type of the component you wish to use. For example, if we had an alternative layout called AdminLayout, our layout directive would look like this: @layout AdminLayout.
Main layout
We’re going to be updating the MainLayout component. To start with we are going to do two things; First, we’re going to use the @inherits directive to inherit from the LayoutComponentBase class. This will mark this component as a layout component and will give us access to the Body parameter. Second, we’re going to define where our page content is rendered using the Body parameter.
@inherits LayoutComponentBase <main class="container"> @Body </main>
The only thing we’re missing from our layout now is the header. We’re going to define this as a separate component and as it’s part of the overall Layout feature it will go in the Layout feature folder next to the MainLayout component. As we did before, add a new Razor Component called Header.razor. Then, we’re going to add the markup shown which adds a Bootstrap navbar displaying the text.
<nav class="navbar navbar-light bg-light border-bottom mb-4"> <span class="navbar-brand mb-0 h1">Blazing Trails</span> </nav>
That’s all we need in the Header component at the moment; we can now add that to the MainLayout by declaring it as we would any normal HTML element.
@inherits LayoutComponentBase <Header /> <main class="container"> @Body </main>
That’s it for the layout, if you try and run the application at this point you will be able to see the header we’ve just created but there will be a message saying “Sorry, there’s nothing at this address”. That’s because we haven’t defined any routable components (pages) yet.
The Blazor Trails home page
We already created the HomePage component. It still has the boilerplate code which comes with a new component. We need to update this code to make the component routable. Once we have that done, we’re going to define a class which represents a trail. We can then define some test data to use to build out the rest of the UI. Finally, we’re going to load the test data into the HomePage and loop over it to display the various trails via a reusable TrailCard component that we’ll create.
As we talked about earlier, to make a component into a routable component we need to use the @page directive and a route template which specifies the route it will be responsible for. At the top of the HomePage.razor file, add the directive along with a route template of “/”, which tells the Router that this page is the root page of the application. You can run the application at this point, if you wish, to check that the HomePage’s content is being displayed.
We need a way of representing a trail in our code, to do that we’re going to add a new class called Trail to the Home feature folder. Inside this class we need to add a few properties which represent the various data about a trail.
public class Trail { public string Name { get; set; } public string Image { get; set; } public string Location { get; set; } public string Time { get; set; } public int Length { get; set; } }
Prepare the data
Now, we have a definition for a trail we’re going to define some test data to use. At the moment our app doesn’t have a backend, there is no API we can call to retrieve or save data from and to, but later on there might be. In order to simulate making an HTTP call to load data from an API we’re going to define our test data in a json file. So, this is a great way to develop frontend applications which don’t currently have a useable server element. We can still use a HttpClient to load the data from the JSON file in the same way we’d load data from an API. Then once the server element is established, the HTTP call just needs to be updated to point at the API endpoint instead of the JSON file.
Now, in the wwwroot folder create a directory called trails. Inside that folder add a new json file called trail-data.json. You have the full json on GitHub.
Then, with our test data in place we’ll return to the HomePage where we need to load it. We’re going to load the data using the HttpClient, but in order to use it we need to get an instance of it using dependency injection. Blazor makes this really easy by providing an inject directive that has the following format, @inject [TYPE] [NAME], where [Type] is the type of the object we want and [Name] is the name we’ll use to work with that instance in our component.
The first injection
So, under the page directive add the following code which will give us an instance of the HttpClient to work with: @inject HttpClient Http.
Before we can use the HttpClient, we need somewhere to store the results the call will return. Our JSON tests data is an array of trails and as we’re not going to be modifying what’s returned, just listing it out, we can create a private field of type IEnumerable<Trail>.
@page "/" @inject HttpClient Http <h3>HomePage</h3> @code { private IEnumerable<Trail> _trails; }
Read the data
Now, we have somewhere to store our test data we can make the call to retrieve it. A great place to do this kind of thing is the OnInitialized life-cycle method. This method is provided by ComponentBase, which all Blazor components inherit from, and it one of three primary lifecycle methods; The other two are OnParametersSet and OnAfterRender, they all have async versions as well. OnInitialized is only run once in the component’s lifetime making it perfect for loading initial data like we need to.
In order to retrieve the data from the JSON file, we can make a GET request just like we would if we were reaching out to an API. Except, instead of passing the address of the API in the call, we pass the relative location of the JSON file. As the file is in the wwwroot folder it will be available as a static asset at runtime just like the CSS file, this means the path we need to pass in the GET request is simply, “trails/trail-data.json”.
JsonAsync methods
So, a great productivity enhancement which ships with Blazor is the addition of some extension methods for the HttpClient:
GetFromJsonAsync
PostAsJsonAsync
PutAsJsonAsync
Under the hood, these methods are using the new System.Text.Json library. The first method will deserialize a successful response containing a JSON payload to a type we specify. The second and third will serialize an object to JSON to be sent to the server. All three of these methods do this in a single line. No more having to manually serialized and deserialize objects or check for success codes, making everything much cleaner and removing a lot of boilerplate.
Also, one thing to be aware of when using these new methods is that when a non-success code is returned from the server, they’ll throw an exception of type HttpRequestException. This means that it’s generally a good practice to wrap these calls in a try catch statement so non-success codes can be handled gracefully.
@code { private IEnumerable<Trail> _trails; protected override async Task OnInitializedAsync() { try { _trails = await Http.GetFromJsonAsync<IEnumerable<Trail>>("trails/trail-data.json"); } catch (HttpRequestException ex). { Console.WriteLine($"There was a problem loading trail data: {ex.Message}"); } } }
Great! We now have our data being loaded into our component, but we need to do something with it. It would be nice to have a message displayed to the user to let them know that when we’re loading the data, just in case it takes a while.
Waiting for the data
We can use a simple if statement in our markup to check the value of the _trails field. If it’s null then we can surmise that the data is still being loaded, excluding an error scenario of course. If the value is not null, then we have some data and we can go ahead and display it.
@if (_trails == null) { <p>Loading trails...</p> } else { <div class="row row-cols-1 row-cols-md-2"> @foreach (var trail in _trails) { <div class="col mb-4"> <div class="card" style="width: 18rem;"> <img src="@trail.Image" class="card-img-top" alt="@trail.Name"> <div class="card-body"> <h5 class="card-title">@trail.Name</h5> <h6 class="card-subtitle mb-3 text-muted"><span class="oi oi-map-marker"></span> @trail.Location</h6> <div class="d-flex justify-content-between"> <span><span class="oi oi-clock mr-2"></span> @trail.Time</span> <span><span class="oi oi-infinity mr-2"></span> @trail.Length km</span> </div> </div> </div> </div> } </div> }
At the point you should be able to build the app and run it, if all has gone to plan you should see the trails displayed. Now, we could finish here but there’s one little refactor I think we should do first.
Refactor
While it’s all perfectly valid as is, wouldn’t it be nice to encapsulate it all in a component instead? This would make the code in the HomePage component much easier to read.
So, create a new component called TrailCard.razor in the Home feature folder. Then replace the boilerplate code with the markup for the card from the HomePage, be careful not to copy the outer div with the class’s col mb-4. That was pretty painless, but now we have a problem. How do we get access to the current trail data? The answer is parameters.
Now, we can pass data into components via parameters, you can think of these as the public API for a component and they work one way, from parent to child. We can define them in the code block by creating a public property and decorating it with the Parameter attribute. Then, we pass data into them from the parent using attributes on the component tag.
For our TrailCard component we’re going to create a parameter which will allow us to pass in the current trail from the parent. We can then update the razor code to use this parameter.
<div class="card" style="width: 18rem;"> <img src="@Trail.Image" class="card-img-top" alt="@Trail.Name"> <div class="card-body"> <h5 class="card-title">@Trail.Name</h5> <h6 class="card-subtitle mb-3 text-muted"><span class="oi oi-map-marker"></span> @Trail.Location</h6> <div class="d-flex justify-content-between"> <span><span class="oi oi-clock mr-2"></span> @Trail.Time</span> <span><span class="oi oi-infinity mr-2"></span> @Trail.Length km</span> </div> </div> </div> @code { [Parameter] public Trail Trail { get; set; } }
All that’s left now is to update the HomePage component to use the new TrailCard component.
@page "/" @inject HttpClient Http @if (_trails == null) { <p>Loading trails...</p> } else { <div class="row row-cols-1 row-cols-md-2"> @foreach (var trail in _trails) { <div class="col mb-4"> <TrailCard Trail="trail" /> </div> } </div> } @code { private IEnumerable<Trail> _trails; protected override async Task OnInitializedAsync() { try { _trails = await Http.GetFromJsonAsync<IEnumerable<Trail>>("trails/trail-data.json"); } catch (HttpRequestException ex) { Console.WriteLine($"There was a problem loading trail data: {ex.Message}"); } } }
Conclusion
Finally, we did it! We finished setting up a Blazor WebAssembly application and it works. If you have any problem, you have the source code on GitHub. If you have any question, please use the forum.
Happy coding!
The post Setting up a Blazor WebAssembly application appeared first on PureSourceCode.
from WordPress https://www.puresourcecode.com/dotnet/blazor/setting-up-a-blazor-webassembly-application/
0 notes