Modern Web UI with Blazor WebAssembly | BOD104

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Hello and welcome, my name is Steve Sanderson and I'm a Developer at Microsoft on the ASP.NET team. In this talk, I'm going to be telling you about Blazor WebAssembly, which is a new framework for building rich WebUI in.NET running on WebAssembly inside web browsers. The exciting news is that at long last, after 15 preview releases, Blazor WebAssembly is finally fully released and supported for production use. It's ready and I am so excited to be able to tell you about this new framework and what it can do for you. So in this talk, we are going to start with a short recap about what Blazor is and what it's good for. Then for anyone who hasn't seen it yet, I'm going to quickly walk you through how to get started with it. Now, if you already know about that, then don't worry because that part will not take long. Then for the main part of the talk, we're going to explore some of the major important features that are unique to Blazor WebAssembly, looking at a larger application to understand what you can do. Finally, I'm going to tell you about what else may be coming in the near future. So the first thing to talk about is; what on earth is this and what is the point of any of it? Well, let's say that you are building a sophisticated web application, it runs in regular desktop web browsers, maybe it's also installable as a progressive web app in mobile devices. There's a lot going on in there, you've got navigation, you've got grids, dialogues, wizards, forms, validation, and all of it requires authentication, localization, and so on. Well, a few years ago the only way to build this thing and have it run inside a browser was to use a JavaScript framework, and write your code in JavaScript or something that compiles to that. Well, with Blazor you have the opportunity to use .NET, that is C-Sharp and Razor code. Why would anyone want to do that? Well, what are the benefits? So first, you get the long proven productivity and power of the C-Sharp language, a language that's truly strongly typed at runtime. You also get the tooling and development environments that go with that, including Visual Studio or Windows or Mac or VS Code anywhere. You'll be working inside an ecosystem with clear, simple patents for things like build systems, deployment, and testing. As a whole, .NET is designed to be stable over many years, even decades, we don't change how everything works every few months. So let's have a look at some code and see how you would get started, and if you already know this don't worry, it will only take a few minutes. Getting started is pretty straightforwards, you can do it using either Visual Studio for Windows or Mac or the .NET command-line tool for any platform. I'm going to just start by showing you how we can do it with Visual Studio. So I'll start by going to File, New Project, and then from there I can search for my project type. Let's have a look for a Blazor application template. Here we've got "Blazor App", so I'll choose that one and then we'll give it a name, MyFirstApp and choose "Create". Then on this next screen that appears, we've got a few choices, we can either create a Blazor Server application or one that runs on WebAssembly, I'll talk about those differences in a minute, but this talk is of course about WebAssembly. Then over on the right, we've got a few more things to pick from, for example what authentication mechanism do we want to have, if any? I'll show you that in a second. Then down here at the bottom, we can choose if we want to give it an ASP.NET Core back-end, that's completely optional, there's no requirement to have anything particular on the server if you don't want to, but there are some benefits to combining an ASP.NET Core server and the client together. Then finally, we can choose to make it a progressive web application, a PWA, so that it can be installed and used offline, if you want to. Well, we'll talk about all those things as we go along, but for now I'll just create a project with the default settings. So when that thing has been created, I'm going to start the application up. To do so, I'm going to go to the Debug menu and choose "Start Without Debugging" because that starts the application up faster. So when that's compiled, it will open in a browser and we'll see the application is running. Now, out-of-the-box is a pretty typical dashboard application, it's got a few different pages in it, a few examples of how to implement certain types of functionality. So let me show you a little bit of the code so you can get used to it. Well, if you're familiar with any modern web UI development, you're probably familiar with the idea that things are built out of components. In a Blazor application, each one of these Razor files is a separate component and we can structure things out of those, for example here's the code for the counter component that I just showed you. Well, a Razor component let's you use HTML and C-Sharp together. So in this case for example I'm displaying a little bit of markup, including the current count value which is defined as the C-Sharp field down here. Then below that, we've got an HTML button that says when you click it, we want to call this method increment count, which is defined in C-Sharp down here. So that's it, that's how the count gets updated, it's pretty simple stuff. Alternatively, you can create your project using the command-line like I'm doing on this Mac here. So if you've got the.NET tool installed, you can type "dotnet new blazorwasm" and give a name to your project, and that will be created. Then you can cd into the new directory and type "dotnet run", that will compile and start your application up. When it's started, you can grab the URL that the dev server is running on there, and copy and paste that into a browser. You should see that it's exactly the same application as we had before, it's the same behavior, same functionality, and of course exactly the same code behind it. So a minute ago, you may remember that I had two types of application that I could choose from. I could have created out of a Blazor Server application or a Blazor WebAssembly application. Well, what are the differences between those? When you create a Blazor component with the Blazor programming model, you will get something that can run in potentially many different places. Now, currently we have two supported hosting models, we may add more in the future, but right now we've got two. The first one is Blazor Server, now that originally shipped as part of .NET Core 3.0, so that was about six months ago. In this model, your code is running on a server, presumably in the Cloud somewhere. Whenever you want to send UI updates to the browser, they are streamed over a WebSocket connection. So that's great for minimizing the load on the client and getting very fast startup times, but it does have a drawback that there may be some latency if your server is a long way from your user. The new hosting model that we've just shipped is Blazor WebAssembly, and that is in a sense simpler because there your code just runs inside the browser on WebAssembly, there doesn't have to be any server involved at all. Now, presumably you will have a server to do things like data access and authentication and such like, but strictly speaking you don't have to, Blazor WebAssembly apps can just be pure static files with no dependency on anything on the server. But it's still .NET code, and if you want to see that you can have a look inside your browser at some of the files that get retrieved when the application starts up. That will include files like this, dotnet.wasm, and that is the .NET runtime that we've compiled to WebAssembly so that it can run inside browsers. Then alongside that, you'll notice things like DLLs, these are regular real .NET DLLs that have been built by the normal C-Sharp compiler, and they can be loaded and run inside the .NET runtime there inside the browser. So that's how it works, and now you know the basics. Now, let's go a little further and learn about some more of the cool and powerful features and capabilities of Blazor WebAssembly, and hopefully illustrate why it's such a useful and productive way of building the applications that I think many of you are building. To do that, I'm going to need a larger application. So I've got a larger demo app here called CarChecker, and I'm just going to start that up and run it and hopefully explain to you what the scenario for this application is. So when it first comes up, the only thing you can do is log in. Who would log in? Well, the idea is this is for employees of a company, specifically it's a car rental company. The employees have to check the cars that are being returned by customers to make sure that the fuel level has been topped up and the vehicle doesn't have any damage, that kind of thing, and we can track any information we need in here. So I'm going to log in as an employee just now and I will login with an account that I've already registered earlier, then now hopefully the application will know who I am. The first thing that I have to do as a user is type in a license number for a vehicle that some customer is returning. So you'll see that we've got a nice autocomplete thing going on here, and I'm going to pick this particular vehicle. That will take us into the Editor view where we can see information about this car, about it's mileage, fuel level and so on. We can spin this 3D view around and have a look at where we expect there is existing damage on the vehicle and compare that with the car in real-life. So as an employee, maybe the first thing I'm going to do is update the mileage on the car and maybe I'll enter some crazy implausible value. You can see we've got some validation kicks in there, and I can go and change the fuel level to reflect whatever the customer has returned. You'll see the Save button has shown up because we are tracking the state of the form and we know that there are changes to save. We can also see a list of what damage is already known, and maybe we can record information about other damage that we discover. So maybe there's a problem with this wheel here at the front, so I'll tap on "Add Note" and I'll write a description of that. So the problem here is that it's completely missing, so I'll hit "Save" on that now. Then we can see we've now got an extra item of damage, and ultimately we can hit the Save button and that will synchronize our changes back to the server. Now, let's have a look at some of the newer features inside Blazor WebAssembly which make that possible. What I'm going to do is walk you through six different feature areas and give you a little demonstration in each case of how it works and what use it is for us in application building. The first one that we're going to start with is authorization or security in general. Now, Blazor WebAssembly when you create your application, gives you the option if you want to set up authorization against your own ASP.NET Core back-end server. If you do that, then all the user account data will be stored in your application's own database, and that's what I've done in this application, but that's not the only option. Blazor supports OpenID Connect which is a general industry standard Auth protocol and through that you can set up user logins through things like Azure Active Directory or Google login, Facebook any of those things. But like I said in this application, I've got my authentication set up to work with the ASP.NET Core back-end server that's here. So just to show you that, if I go into my Manage Account Settings, this will take me to some server ended pages where the user can control things to do with their profile, set up two-factor auth and all that thing. But I don't really want to do that right now, what I want to do is show you how this affects authentication against the back-end server. So every time the user comes to this page, we're trying to fetch updated state from the server and let me just show you how that works. When I tap on this "Update" button down here, you'll see every time we make a request to the server to get a list of vehicles that have changed since we last got information. Now, on the server this is being mapped to this controller called Vehicle Controller and just for demo purposes, I disabled the security on this controller. I don't have anything that actually enforces access control, which is of course very bad. So I'm going to add it back-end now and we'll see what effect this has. So I'm going to add to this authorize attribute and that's going to tell ASP.NET Core on the server that people can only make request to this controller if they've been issued a valid access token. Now, let's go back and see what effect that has, because I've also modified the client application so that it doesn't send any access tokens and I want to see what effect that has. You'll see when I reload now that it says it can no longer sync, it's just not able to do it and if we start clicking on this "Retry" button, you'll see each time we try to make a request to the server, but the server replies 401 not authorized. Which is great, because we always want to enforce our actual security controls on the server and we've just shown that we can do that. Now, I've caused one legitimate users to be able to access this data, which means they need to get an access token and they need to send that in their requests to the server. Now that's very easy to do because that happens by default when you create your project, and in fact I just disabled it so that I could show you the effects of not sending the access tokens. I'm going to turn that back on now by going into my Client-side program file, and if we look in there you'll see we've got various bits of conflict to deal with the different services we're using in this application. I don't really expect you to recognize or know what all that stuff is, the only part I'm trying to point out to you right now is this bit here, and this controls how we make requests to the back-end server. I've disabled this line of code that's normally there by default, let me just put that one back on. What that means is, whenever we making any requests to our back-end server, which is defined by whatever base address the application is running on, for those specific requests, we're going to use the authorization message handler, which includes the access token that we were given when we logged in. So hopefully that's all we need to do in order to get access back to the server side data which is now being secured. So I'll come back and I'll hit "Reload" one more time, and this time instead of getting the error message, hopefully we'll find that it's able to do updates again. This time if I click this button a few times, you'll see we're now getting these 200 okay status codes. To really understand what's happening there, let's have a look in the list of headers and you'll see that with the request we're now sending this authorization header which is a bearer token that we were given when we first logged in to the server. So by default, all that stuff happens for you and is set up by the project template, I just disabled some of that so that I could show you what's going on inside that. We could go quite a lot further with authorization if we want to. You can do things like self policies to do with roles or any other rules you want about who's allowed to do what. Like I said, you don't just have to use your own local database to store user accounts, you can use the OpenID Connect protocol to hook up to things like Azure Active Directory or third party login systems, but that's enough about that for now. Let's move on and think now about code sharing, and this is something that's a bit of a strong point for Blazor WebAssembly especially if you have got .NET code on the server. So let me show you what I mean by that. Back in my solution here, you can see I've got these three projects. I've got a client project, which is my Blazor WebAssembly application runs inside the browser. I've then got the server project, which is an ASP.NET Core server and that's where I'm actually storing data and implementing the login system, and then finally I've got this shared project here. That is a great place to put any code that we want to make shared by both server and client. There's nothing magic about it, it's just a regular .NET class library and you can put any normal .NET classes inside that. A thing that people commonly do with these shared projects is use them to store their business objects or their domain model classes to ensure that things are always consistent between client and server. So in this case, I've got for example, this vehicle class here that defines various fields and rules and so on. I've likewise got this inspection note and these classes are being used for quite a lot of different things. For example, they are being used to define the shape of the underlying database schema, they are also being used by the server to define the validation rules that enforces when requests arrive. It's also used to define the shape of the data traffic that is exchanged between the server and the client and finally, is used to define the validation rules that are enforced on the client. So we're getting quite a lot of consistency and reuse supplied automatically just by the use of these C-sharp classes. Now just to prove that this data and this metadata is really being used by the WebAssembly application, let's make a slight change to one of the validation rules. I'm going to say that the text on an inspection note is now only allowed to be a maximum of 10 characters long, which is obviously a bit short but it will help me demonstrate. Then I'm going to set an error message on there say something like, hey, be brief. That's a bit informal but I think they'll work, and I'm going to rebuild that and we'll see if this now starts showing up on the client project. So I'm going to go back and I'm going to reload my application and then I'll go into one of these vehicles and I'll start editing. So let's go to the same one we had before. Let's imagine that we've now got a problem with this headlights so I'm going to add note and I'll start typing stuff here now that's a bit long. So you'll see we now get this message, hey, be brief, and that's showing up and I can no longer submit the form without making it shorter. The same validation rule will be enforced on the server. I'm not going to demonstrate that you can just trust me. Now, another type of code sharing and reuse I want to talk about is to do with NuGet packages. NuGet is of course .NET's package management system and within a NuGet package, you can place components if you want to. Those packages can contain both the .NET code that's needed to implement the component, as well as any other static files like JavaScript or CSS that's needed for how they're implemented. To give you an example of that, let's say I want to be able to upload photos of the damage. Now I haven't implemented that yet. So if I tap on these buttons then nothing's actually going to happen. I want to have a way of uploading photos and I'm going to do that using a component from a NuGet package. So I'm going to go to my client project here and I'm going to right-click and go Manage NuGet Packages. Now, I could do this on the command line or by editing my project file directly but it's quite nice to do it in the ID, and I'm going to search for this component that I already know about called Blazor input file, and I'm going to install that. What that will give us is a component called Input File that makes it really easy to upload files and deal with things like images that might get uploaded. So now I've added that package, I can go to wherever I want to use it and do so. So I'm going to go into my vehicle note editor and you can see this is why we've got that big form where the user can type in things like the description and we've got the validation messages showing up and that sort of thing. Now, this is where the photo part of the form is and because I've added that package, I can say, Blazor input file and make use of that. If I don't like this prefix here, I can take that prefix and add that as a using statement. So let's add this using Blazor input file and now I've done that, I can simply make use of the input file component directly like that. Now, I've already prepared a little bit of code to use here. So what I want to have is the input file and I'm going to say, whenever the user selects a photo, I want to call a C-sharp method called Handle Photo Selected, and I'm also configuring the browser to only let the user pick from image files. Now, we see we've got the red squiggles because we haven't implemented this handle photo selected logic, so let's go and add that. I'll go down to my code block here and at the bottom of all these different event handlers, I'm going to put in this handle photo selected. That's going to receive the files that the user has selected in the browser and we'll check that they've given us one and then we'll ask for it to be converted to a JPEG at a particular maximum resolution and we'll get the data from that as bytes and we're going to store it on our domain model class there. So let's hit "Build" on that now and hopefully I'll be able to go back to my browser and actually add an image file. So back in the browser, I'm going to hit "Reload" and when that comes up, I will now be able to hopefully pick a photo. So let's say I want to add a photo of the damage to this body rear right here, I'm going to go and click on the "Take new" and that's going to open whatever type of UI the browser wants to show. So on a desktop it looks like this, on a mobile it's a bit different, it asks if you want to take a photo or pick an existing one. In this case, I'm going to choose a photo that I've already got, and we should see that now shows up in the UI and just to make things really cool, we've wired this up to an ML.NET Service on the back-end that can look at your photos and try and detect what level of damage there is in there. So if I click on "Detect damage", that's going to check with the server using this pre-trained model. Does that look like the car is damaged? In this case clearly it is damaged and we can say that with quite a high level of confidence. The next thing to talk about is JavaScript interop. Blazor supports .NET calling JavaScript and JavaScript calling .NET. So hopefully, it should be pretty easy for you to make use of any browser API or any third-party JavaScript library. But you know what's even easier than doing JavaScript interop? Well, it's not doing JavaScript interop and just letting someone else do that work for you, which is a general life principle I'd recommend to you, and there is an example of that in this project. So let me show you. This 3D view that we can see here, where we can select things and spin things around, well, this is implemented in mostly in JavaScript with the three.js library, which is a very popular third-party JavaScript library for 3D rendering. But the code for interacting with that from .NET, is not part of my project, that comes from a third-party NuGet package. Let me show you. If I go into my VehicleEditor razor file, and scroll down a little bit, you'll see we've got the use of this scene view component, and that's coming from this external NuGet package, and that lets us specify what environment we want to use, what object we want to display, which objects should be selected and highlighted and a.NET callback function that should fire whenever the user clicks on one of the parts of the vehicle. So the code for that isn't part of this project, it's something that comes in from an external package, and that's by far the easiest way of doing interop with JavaScript libraries. At the same time, they will also be cases where you do want to write your own JavaScript by hand and then interact with that from .NET code. There's another example of that in this project too. So the scenario for this application is for a car rentals company that deals with vehicles that customers are returning. One of the business requirements is that it needs to work fully offline, because when the employees go out into a large parking lot to check on the vehicles, they can't guarantee that they're going to have Wi-Fi access everywhere. So in order to support offline use, we've got a local database, so users can make changes, save that changes, and then eventually synchronized back to the server when they get network access. To implement that local database, I'm using a JavaScript API that's built into browsers called IndexedDB, and I can show you the data actually being stored inside the browser here. If I go on to the Application tab and we'll look on IndexedDB, you'll see that I've got three different tables in my local database here. One that's keeping track of local edits that haven't been synchronized yet, one that's got some general metadata, and then finally, a table that's got the full set of data that we know about that we've synchronized from the server. Now the implementation for this IndexedDB database is in a JavaScript file in my project, and I'm going to show it to you now. So I've got this localVehicleStore.js, which might look a little bit scary if you are not familiar with this JavaScript, but there's not too much of it. In total, I've got 36 lines of code here, that's all that's needed, and that is responsible for both defining the shape of the database, the schema here with those three tables, and it also provides general methods for getting data, putting data in, deleting stuff, and I've even got a custom application specific query down here that is used when we auto-complete. Now to call these JavaScript functions from .NET, I've got a C-Sharp class called LocalVehiclesStore here. That's made available through the dependency injection system to any component that needs to use it, and that provides a nice, strongly typed wrapper around all of those different JavaScript APIs. For example, I've got this method called GetOutstandingLocalEdits, and you can see that it uses the JavaScript interop APIs to call this particular JavaScript function and fetch data from this local table called localedits. Then I've also got this synchronized method here. That's going to get all of the local edits that we haven't sent to the server yet, and for each one of those, it will make an HTTP request to put that data onto the server, it will delete the local copy of the data now that we've got it onto the server, and then finally, it will fetch all the other changes that have become available on the server since we last synchronized. The next thing to talk about is debugging. Now, obviously it's very important that you are able to debug your code. But can you actually debug .NET code that's running on WebAssembly? Well, let's try it out. I'm going to start my application with the debugger attached. So I can go to the debug menu, and choose "Start Debugging", or I could just have pressed "F5", and that will launch a browser instance with the ID connected as a debugger. So let's say that we want to follow the execution of code that happens when you detect damage on one of the photos. I'm going to add a few breakpoints now. I'll start by adding a break point inside my Blazor WebAssembly components here. I'm going to add one in this event handler at the bottom, which runs when you click on the Detect damage button. So we'll have a break point here before we call the server, and another break point after the response comes back from the server. Then, I'm also going to add a break point in the server side code. So here's the MVC action method that will get called when we send that photo to the server to run the ML model against it. Then finally, to show this as really end-to-end, I'm going to put a break point in my JavaScript code. So this is the line that will get executed when we try to store changes to the local database. So let's go back and find that car that we were working on earlier. I will go to the note that we were editing, and I'm going to tap on the "Detect damage" button. What we will see is, that we immediately hit this break point inside the client side code that's running on WebAssembly, and we can inspect the state of what's going on that. I'm going to press "F5" now to continue execution. What will happen is we'll hit the server side break point. Again we can step through that and seeing what's going on in the server. I'll press "F5" another time, and we'll be back hitting the code that's on the client. There we go. For example, we could hover over this damageDetectionResult, and have a look. We can see that, yes, this vehicle is damaged, and here's our confidence level so we know what's going to show up in the UI. So I'll press "F5" again, and we're now back in the browser. Lets save those changes. Then finally, I'll hit the "Save" button, and we will hit the code that's inside our JavaScript now. So we can see that we were about to store some changes into this localedits table there. So we are able to simultaneously debug .NET on the server, .NET on the client, and JavaScript on the client, all in a single session and step between them. So that is full-stack debugging with Blazor. Now, debugging is not limited to Visual Studio. You can also debug directly inside the browser's DevTools if you want to, or from Visual Studio Code. Let me show you how we can do that. So I've opened the same project in VS Code, and I'm going to go to the debug menu and I have to start by creating a launch file that configures how the debugger works. I'll say I'm using .NET Core, and I'll say that the project that starts up is the server, because that's the thing that hosts the client. So now from the menu, I should be able to start-up my projects. So I'm going to say that I want to launch my project, and that is going to compile the application and start-up the server. When that's done, I can also say I want to launch the debugger with a web browser attached. So I'll hit the "Start" button there, and you'll see that the browser appears, and let's say we want to debug this autocomplete component. Well, I'll go over to my source code now, and I'll find the autocomplete component, which is here. This is the event handler that runs when the user types into the box. So I'll add a break point right there. Then I'll go and I'll start typing a character. We should see that we hit this break point here in VS Code, and I can start stepping through the execution of the code. If I want to, I can inspect the state of things. For example, I can have a look at the list of choices that we're going to display, and I could look at various other bits of information that the system has got at this point in time. So we can debug wherever we like. Moving on, let's talk about internationalization, and we'll start with translating the application into different languages. Well, if we look at the application right now, we'll see that it's all in English, because that's the default language that I've built in. But what if my user is a speaker of a different language? Well, let's change the browser language settings. I'm going to change it so that Spanish is my preferred language. I'll move that one up to the top, and then I'll come back and I will reload the site and we'll see what happens. Well, it's all become Spanish. So as I move around now, you'll see that all of it is Spanish. Well, not the text that's been entered by users, of course, but all of the text that is built-in to the application, including things like validation messages as you can see. Now, as for how that works, let's have a look at the code. If we look inside our program file at the list of services, you will see that one of the services is localization that's been added that. What that does, is it makes available a service called IStringLocalizer to all of your components. Let's have a look at how that is used in the index page, that's the starter page. You'll see that it's been injected there from the DI system, so we can make use of that. Then everywhere that we've got a string to display, we read it from this object called localize. So we're reading Enter license number from localize. Where does it get the actual translations from? Well, in this case, it's associated with the resource type called App. If I look in my resources directory here, you can see that I've got some files called App. I've got one for my default language and I've got one for Spanish. If we look in here, you can see that I've got the translations for all the different strings that I am reading. So that's a very straightforward and familiar system for localizing that .NET developers will be familiar with. But just to really clarify this, let's have a go at localizing another part of the application. If I have a look on this menu that shows up here, you'll see these strings Manage account and Log out still appearing in English. That's because I haven't localized them. Let's see how we can do that. I'm going to go to the component that is displaying that UI, so that's this login status component. You can see that it's hard-coded to display this text in English. So there's no way that can be translated because it's just hard-coded text. But let's change that. I'm going to inject one of these IStringLocalizers into my login status component there, and then I'm going to grab that and I'm going to use it to read localized versions of this text. So let's put that in there, and the same thing around Log out as well, I think. Now let's see what effects that has. Let's see if it's able to translate these texts into a different language, simply because I'm reading them from the localizer. So when that comes back up, let's see if our text is translated, and it is not. It's still in English. So why is that? Well, the reason is because localize is not magic, it doesn't actually know how to translate text into other languages unless you actually supply translations for them. The default behavior, if it doesn't have a translation, is just to use the text as it is. That's a really useful feature, because it means you don't have to localize your entire application upfront. You can just use localize everywhere even before you've got translations into other languages and then later on you can come and put the translations in. So let's add some translations for these two strings. I'm going to add a translation for Manage account and another translation for Log out. I've been given some translations by Javier on our team who not only speak Spanish, but also implemented quite a lot of Blazor. So I'm going to copy and paste these translations into my resx file and rebuild, and then I will switch back to the browser one more time and reload and then this time, hopefully, we will actually have the text showing up in Spanish correctly. So we can incrementally localize different bits of the application as we go. Now, another aspect of internationalization is being able to deal with data in different formats, such as different number and date formats. Well, the good news is, there's very little you have to do for that. We automatically set the .Net thread culture to match whatever the browser says the user's culture is and so we'll automatically display numbers and deal with parsing and formatting, matching the user's culture, but of course you can customize that further if you want to. Now, the last feature area to look at is building Progressive Web Apps. What is a PWA? Well, a PWA is a web application that in some ways behaves more like a native one. For example, you can install it into your operating system and you can have it work offline, receive push notifications from your server, things like that. Now, Blazor WebAssembly has a built-in PWA template, and that is something that I used when building this application. So let's have a go at using that feature. Now in my browser, if we look closely in the top right, you will see that the browser is offering the option to install this as an application in the operating system. Let's see what happens when I do that. I'll tap on "Install" and you'll see that my application now shows up on its own separate window without the usual browser chrome. So it starts to feel somewhat more like a native app and when it's installed, the user can start it up from the "Start" menu, that dock or homescreen or whatever it is that their operating system has. So in this case on Windows, I'm going to search for car checker and you'll see my application shows up there on the "Start" menu and I can immediately start it up. So what about offline support? Let's see what happens if I don't actually have a network connection when I start the application up. To simulate that, I'm going to open the browser dev tools like that and then I'm going to go into this network tab here and I'm going to tell the browser to simulate being offline. Then I'm going to come back and I'll right-click and I'll choose "Refresh", which is just like starting the application up, but without a network connection, and we'll see whether it still works. The answer is, it does not. We just get this error message. So you might be wondering why is that given that I've just been saying that PWAs can work offline. Why doesn't this one? Well, the answer is that we have only enabled offline support by default when your application is published not in development and there's a very good reason for that, which is that offline web applications receive their updates in an asynchronous way in the background while the user is using them. You don't always get the updates instantly and if you were developing the application, that would be extremely annoying because every time you change your code, you want to see those changes immediately. You don't want to wait for some background process to complete. So that's why we've only enabled it for published applications by default, but you can change that if you actually want to. So to see how this behaves when published, let's publish the application. I'm going to do that on the command line. I could do in VS if I want to, but the command line is quite nice. So I'm going to say dotnet publish in release configuration and that's going to fetch any dependencies. It's going to build the application and it's going to drop it all into a folder on my hard drive. So here we go. I'm going to cd into the directory that's just being created there and then I'm going to start it up by using the .Net tool to execute my car checker server process. Right now that's running so I'm going to get this URL here and I'm going to drop that into my browser and now I am looking at the published application. So let's see if it works offline now. Again, I'm going to go to the "Network" tab and tell the browser to simulate being offline and I'll click Reload again and let's see if we got an error message this time. Well, no, we don't. It actually comes up and runs even though we're offline. Although of course it says that we can currently synchronize our changes with the server, but let's see if we can still make changes. So we'll go back to our vehicle from before and we'll try to make a change to something. Let's say we want to change the description of this dent there to something else. I'll hit "Save" on that and save. You'll see we can't currently synchronize stuff with the server because we're offline, I can even reload and you'll see that I still got my changes locally because they're all in IndexedDB and then I'm going to simulate becoming online later. Then the user taps the "Retry" button and this time the changes are able to synchronize with the server. So that all works very nicely. But I think it's also interesting to show what the experience is like on an actual mobile device. So here I've got an iPhone and I'm going to go to the publish version of this application again, and that comes up, and of course I could use it in the regular web browser. But instead of using it in the regular web browser, I'm going to install it as a PWA on this device. So I'm going to go to this "Add to Homescreen" option, which you can see in the middle of the screen there and I'll tap that and it will prompt me to enter some information and I'll choose "Add" and it will add an icon on to the homescreen there and so when I tap that, you'll see the application shows up. Just like on desktop this does not rely on having network access to startup because all the data is stored for offline use in the Service Worker and everything is just going to work. So let's go and edit one of these cars. So we'll go in here and you'll see we've got a really nice mobile-friendly layout which makes full use of the full screen, including the notch and the curved areas. We've got all the usual kinds of mobile scrolling and we've got the 3D view working and different parts of the UI customized for different screen sizes. Of course, that's all just CSS, but it works the same with Blazor as with any other web technology. So that's six features of Blazor WebAssembly. The last thing I want to show you isn't really a feature as such. It's more just an inherent consequence of running on WebAssembly. Because you have a true client-site application, there's no dependency on anything particular being on the server. So you don't actually have to have .Net running on your server at all. Obviously, it works really great if he do, but you could if you wanted to host a Blazor WebAssembly app on a different server site technology such as NodeJS or a Python web server. You can even deploy your app to a pure static file host such as Azure static sites or GitHub Pages. To give you an example of that, I'm going to show you how it can be done with GitHub Pages. So over here I've got a repo where I have deployed the default Blazor application from the project template. So if we look in there, you may recognize some of these project structure. We've got these three pages with the Index Counter and FetchData and all the other usual files that you would get out of the project template. But one thing I've added is a GitHub workflow. So that's a few actions that GitHub is going to run every time I push some code to my repo. Let's have a look what we've gotten that. So what this configuration file says is that every time I push anything to this master branch here, we're going to run three actions. The first action is going to be publishing the application using the regular dotnet publish command and that's available by default. We've got hub actions. The next step is that we're going to rewrite the base_href in the index page. Now if you don't know what that means, that's simply a way of dealing with the fact that by default, GitHub Pages push your application in a sub-directory, not at the top level within a domain. So you only have to do this if you are deploying that way. You don't have to do it if you've got a custom domain. Then finally, assuming that everything's working out okay, we are going to use this GitHub action here to deploy the published output into the GitHub Pages branch, which is going to be made available to the public. So the result of doing all that is that we have a publicly visible website. Here it is. So here is the Blazor WebAssembly app running on GitHub Pages. It's all fully functional and working completely as normal. Which means you've got a .Net application running in the Cloud with zero hosting costs. Now, if you want things like deep linking to work correctly so that you can hit reload even when you're on a subpage and everything comes up okay, then there's a few more steps you have to go through, and those you can find in the documentation, or if you prefer, you can come and look at this repo and see each of the things that I've done to make it work seamlessly. You may notice that I've done something here to support pre-compressed files. That is because by default, GitHub Pages doesn't support broadly pre-compression, but you can add it with just a little bit of code. So with that done, let's see how big this application actually is. How much does the browser have to download when it's all fully published? I'll open the browser dev tools and we'll go to the "Network" tab and I will reload and you will see initially it doesn't really seem like we're fetching very much from the server at all and that is because by default, Blazor WebAssembly will cache all of the application files that it can. So if we want to get an accurate view of it, we're going to have to go and clear all of the storage for this particular website and then I'll go back to the "Network" tab and reload. Now we'll see that the true first-time download size is 1.7 megabytes and that is of course, a lot better than the six megabytes that we had when we were working in development, you can actually reduce a little bit further to about 1.6 megabytes if you choose to replace the styling system with something a little bit more lightweight and of course, all this stuff is cached after the first page load. So if I go and reload the site now you'll see that we only have to fetch 314 bytes from the network to start it up a second time. Well, that's enough for demos. What is next? Well, like I said, Blazor WebAssembly is now fully released and supported for production use. So maybe you were wondering what the Blazor team spends its time doing these days? Well, the answer to that is, we are already hard at work on the next major release. We've got some big enhancements to Blazor plan for .NET 5, which includes some general component programming model improvements which had been requested by the community, as well as some major items like hot reloading and CSS isolation. There are also some WebAssembly specific enhancements that we're looking at, which includes the ability to pre-compile your dotnet code all the way to WebAssembly bytecode ahead of time, which can lead to some pretty massive speed gains. So that's all we've got time for today. I strongly encourage you to go and try this out for yourself. If you go to blazor.net, you'll find instructions for getting started with VS Code Visual Studio for Windows or Visual Studio for Mac. and if you want the code from this talk, you can go to the second URL that and you'll find a repo that contains the Car Checker demo app which you can clone and try it yourself. So that's all. I hope that's been useful for you. Now, go and try out yourself. [MUSIC]
Info
Channel: Microsoft Developer
Views: 18,854
Rating: 4.9520001 out of 5
Keywords: b20, microsoft build, microsoft build 2020, microsoft developer, BOD104, Modern Web UI with Blazor WebAssembly | BOD104, Pre-recorded Session, Advanced (300), Daniel Roth, Steve Sanderson
Id: My_XOzQWwc4
Channel Id: undefined
Length: 44min 50sec (2690 seconds)
Published: Mon May 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.