>> 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]