Spokane .NET Conference: MVVM in Blazor WASM with the Community Toolkit

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
we'll have a c well I have to see how it goes so this is uh mvvm and blazer with the Microsoft Community toolkit um on Kelly Adam uh been with intellitex oh 10 and a half years now blimey I need to find a new job Blazer is really enabling us to write C sharp both on the client and on the server and we can forget all the bad memories about JavaScript and typescripts and all that stuff how many folks here have ever heard mvvm are model view view model as a pattern oh lots of bands awesome that is great good because I can't really afford the time to explain all the ins and outs of African and why you want to use it so we will blast on blast forward with uh it's fine this is just briefly to talk about why we would want to use nvvm in Blazer um definitely to reduce the risk of our models and our business logic with being changed by our application and now we've suddenly made it that everybody can get approved for a billion dollars of credit or you know whatever bad outcome could be so in this scenario The View model is kind of an adapter between the model between your business or domain model and the The View itself and so it prevents you from forcing those view changes down into your business life um number two testability I mean we saw Mark's wonderful uh talk where he was using live unit death thing to to show new c-sharp 11 features we all like unit testing because it keeps the project manager off our back for stupid books and typos and things like that so mvdm makes us or enables us to have more testable code thirdly the agnosticism of the UI platform so I've worked on a couple different projects using mpvm both on thin clients phones mobile stuff and the clients like Windows desktop and you can get better than 90 code reuse if you're really strict with your MB yeah so that means we can take our Blazer app and Port it to xamarin forms and and generate some phone apps if we want or we could just run Blazer in a hybrid Maui app and be done with it but that talk I think was downstairs earlier uh at fourthly collaboration um I don't really see any designer looking people here no long ponytails though and so we're not the best ux designers as code monkeys we tend to like lots of buttons and lots of doodads and give me a sea of radio buttons for all the settings don't hide anything from me but that's not really the best user experience for like my mother-in-law um and so this enables us to work with folks whose domain knowledge is more about the the UI and user experience and they don't need to understand now all the how the data gets there they can just do what they do best and we we wire up to it and it brought provides a way for us to collaborate without without conflict I guess that's a good way to say so slides are cool but let's look at some code so on just some quick things this talk is currently up in the um intellitex samples uh repo on GitHub under well Blazer mdvm the way I structured the dock is I kind of have big leaps from file to project to getting it working to doing messaging and then doing validation um each of those is a branch they sort of correspond to the different pages in my slide deck um the slide deck I think I checked into so um the final code is on the main branch but you can go through the branches to do a a more incremental um incremental step so file new project what we have here is basically the output from um dot net new blazer wasn't and um that gives you a Blazer sight with a Blazer server with a asbnet apples and then a shared project for um dtos or pocos or things that you would send from the server to the client um so that's that's all this is and then we get uh without doing the empty version we get a counter page we get well here let's just look at what it looks like we get a counter page we get the weather report and then we get an index and a layout with navigation um loading load and loading so here's our counter page this is just showing Blazer having statefulness um this is Blazer doing an API call to that back end that was generated and pulling some DD so there's nothing I haven't actually done anything here except had a test project because gusts are good although I don't actually have a test here it's just empty because um all new project so let's jump forward to um adding the toolkit to this oh it probably won't work while it's running though can't change the motor while the car is on so a bunch of new get things happened in the background because really the only changes that I've done here are to add in references to the community toolkit dot mvdm which is a it used to be a open source project and then Microsoft kind of took it over and they called it the Microsoft Community toolkit and now it's just the community toolkit um because I guess they wanted to kind of remove their main drama now unfortunately you'll see on point of that version 8.0 because that was correct yesterday they've since released version 8.1 uh last night and I didn't want to change all my stuff for fear of it breaking but the good thing is looking at the release notes they have fixed some of the things that I ran into in uh that were that I ran into his difficulties in in wiring up laser to to their agnostic MV the m they're not so what what this really provides for us is um observable objects and properties it provides a way to send commands to things which is a big part of the Nvidia and it provides a message bus for components to be able to talk together on the background so um that's basically all that changed here I believe just to make sure let me check uh oh no I did wire up some stuff here too so we need a few a few components in our Blazer project to make this work we need some sort of Base Class for our our field models and so I just have an interface here that extends I notify property changed which is now observable things work um and then I've got an abstract base that uses the mbvm class observable object so that's a class that's provided to us that says oh I must have observable properties I'll provide methods in coaching to make all that work and you'll notice here I've already got my first command which is um uh Cogen from this this attribute that comes from uh the mdvm Dual so um and of course it doesn't do anything except return a completed task right now because it is virtual we will be using that in our Base Class so that's sort of the starting point for view models using this the main the key takeaways are it's got an interface because I need I'm going to rely on dependency injection do you inject my view models into my view score so that I don't have to tightly couple that at all um and I need to assume some Behavior about those V models that they know about observables and that they can accept commands and do things like that um so I also need a base class for components for Razer components Pages stuff like that that extends the Microsoft component base but also as a generic property for what type of view model that it gets and then as a property that gets injected through dependency injection so it has a reference to the view model and that's really all hope there's a drain perfect I told you guys to wait um so that the idea is that the view can be aware of the view model in knowing what properties it has but the view model shouldn't know anything about the few and in doing it this way I'm able to keep all of these key microsoft.asp.net core out of my view model base so I don't have to I don't have to have a hidden dependency to whatever framework so these three models should still work for any other C sharp project that will do mvvm so prism on WPF or light and vvvm light or xamarin forums um up on Android or iOS or something like that um other interesting things about the component is that I need a way because I know that my view models are durable when they change I need to tell Blazer re-render the page to re-render the component because you know stuff is different now and the way to wire that up is to grab the property changed event on my view model and then call State as changed on the Blazer object which just says re-render yourself and anybody who used your child um I've also got a uh a delegate so that I can pass through the Blazer Auto initialized async I can pass that through to my view model because my view model needs to know when it's loaded and the way the way I'm handling that is down in the bass I'm catching um I'm I'm catching that event and then reissuing it as a loaded command so then I just have to receive the loaded command in any of my other V models and they'll know when they've been called into service so to speak so um as far as change is there oh one other change is that it generated that weather forecast object for us when we did file new project but it was just a dumb class with properties on it I wanted to be observable properties and the mvvm toolkit gives us a clever way to do that where I Define the things that I want as properties as private fields and that I stick an attribute on there and I make my class partial and then he uses the new Rosalind coach in to go and generate the rest of this so for example if I go look at the definition of this property you'll see this is generated code and it won't let me change it and so this is this is generating your standard I notify property change pattern or I'd fire some of it before it changes and then finally sets the value of the backing field and then fires events saying it's done changing and um I wake a wire validation into that too and we'll get there shortly so this is this is kind of the the first table Stakes that you need to be able to wire up Blazer 2 mdvm you need some sort of component base you need a view model you need a way to inject it oh and I suppose that's the third thing here oh I'm not actually doing injection yet because all I have is an abstract class um probably not going to actually end up injecting that anyway so um let's move on to um uh okay so we've already done the wire up to the community stuff let's move on to commodit or creating our first component that uses that fetch data give me a weather report example that comes in the built-in object for the the built-in solution and I could demo this but there's actually nothing interesting happening on the client yet so it wouldn't look like much uh component is featuredina hold that guy true way there we go so uh okay we did this one the last and the last branch so now we have our first um Cumberland and maybe it'll make more sense to show before and after on this so the the generated file that comes from the.net new blazer wasm template it's got a bunch of code behind where they're doing the uh the web call to get the data from the backend API that's kind of a big No-No in nvvm you never want you never want your view to be that smart it should just take care of how things look um it shouldn't be going and getting data so I've I've gotten rid of the the entire code block which incidentally also makes it a heck of a lot more testable because if I wanted to write a yetif test and pass in a mocked HTTP client good luck um you could use it with B unit maybe but it's still pretty gnarly and not much fun radical so I took that code and I put that down in the view model so now in our view model we've got brand new fetch data view model and this is where I have my first override of that loaded command so this guy gets called by the Blazer on initialize Daisy event I think that fires off a command that says all the views the all the all the view models be doing need to handle being loaded and that's where I go and make a call to my HTTP client to get that data off the back end um one other thing I'm using it which is actually not part of the mpvm toolkit is an observable collection this doesn't strictly have to be an observable collection because this is a read-only endpoint I can't create a new forecast and send it back that would be kind of cool like to make it warmer um but and that's why you would want to use a verbal collection I could have just done an array Or List doesn't matter in this case because it is a it is a immutable collection to be so that guy goes and gets the data and then um now this guy changes to wire up to those v-model properties remember our base our component base now has a view model property on it and so I can just go and gets go in test and see are there any are there any weather forecasts if so Loop through each of them and bind them to a table row inside of my table here and so the way this works long tea launchy go to fetch data I don't know it's kind of quick it did say loading there for a couple milliseconds but because it's local um it's hard to it's keep blinking you miss it we could make that a little slower to go down into our weather forecast controller and do the worst thing ever don't tell Kevin is this milliseconds or seconds so let's milliseconds you could simulate a slower yeah so restart yeah got that pulling the Wi-Fi card out no swipe there we go so there you can see it transition States um which shows that it's getting those property changed events from the view model and realizing that it needs to re-render when the vmodel starts collecting changes in the case getting the weather report um let's see feel like I'm missing something on there other than I'm glad it's all coaching because writing all of those observable classes by hand with you know I notify property changed is no fund uh similarly with the commanding better so that's kind of the like Hello World level example of mbvm let's go to the next the next step I want more so let's do some messaging oh yeah because I made changes and I'm switching branches we'll just keep them and have it be slowed is this the impossible he has a stabilities that you have no there's no signal R here um and there's no like cascading parameters or anything like that I mean one of the common problems I bum my head in with blazers that I've been very diligent and made a lot of sub-components and I have the sum component that's six levels deep and he needs to raise a callback on some other sub component that's nine levels deep how do I do that I guess I raise the the Callback all the way up to the top and let him fire off a new event all the way down the other key and that's really a pain in the neck and I do it differently every time so it's hard for somebody coming by me to maintain it because they're like oh is this the Tuesday Kelly or the Thursday Kelly got hurt did he have a good night's sleep or not um and what the mbvm toolkit gives us is a both a struggler friends or a weak reference messager that acts just like the message bump in Windows or um you know hits or your OS here and we can we can take all of that load off of Blazer and handle it ourselves so the way this works is I need to make a couple message types and since these are essentially event arcs I can use records because they will be immutable hopefully the messages won't change during Transit this will prevent the male from being tampered with and then um inside my my model here oh cast so just to step back real quickly this was the test of that of that first fetch data that just instantiates my view model and then injects a mock HTTP client and verify that it does what it what I expect um and so I can verify that my view model is now handling those requests and not the view and I don't even have to like call methods on my viewmodel I can just send it a command that cause it to do stuff huh but we want to do a similar thing with um with our our fetch our uh messaging in between two components so what I've got here is a a page with yield bootstrap row and column with two components on it one for entering ASCII text and one for entering hexadecimal and I want I want the text one to send the value to the hex guy and translate it to hex and I want the hex one to send the value back to the ASCII one and translate it back to ASCII that might be a little confusing I'll show you what it looks like so two big text boxes text what does that look like in x so okay hex that's pretty cool want to find monkey with the hex and send it back oh but yet yoga so this is this is what I'm talking about the the one component only knows about ASCII and then he sends a message when he needs to change in the hex component and vice versa so the way I wired that up um is not through dependency Properties or any of that stuff you could see none of these guys actually have any um razor parameters on them at all um all of that is done through the message bus that comes with the mvdm toolkit which on the outside sort of feels like how does this even work in the in the browser because you know we're writing all this c-sharp stuff and we're running it on a browser in you know webassembly how does that even work it does I don't know It's Magic it might as well be magic so what I've added here is some Blazer components which are essential to the same as Blazer pages that also have view models in them and are binding um binding properties of those view models to some properties um to some some stuff on the screen so my text area gets bound to the hex text property of the V model and then my button is sending a a command to the view model saying hey send uh an ASCII converter message to the other guy so what do those view models look like so right now we're looking at hex entry let's look at the hex entry view model so here's the um here's the method that accepts that that command the send to SD converter that the button is wiring up to and I already have a reference to the messenger and that is because I've created a new view model Base Class here in addition to just plain old observable objects um uh the mvvm toolkit has the concept of an observable recipient which is a thing with observable properties that also cares about messages of certain types and so I sort of create those together by saying my base class for these view models accepts a message type and then inherits from is observable of recipient and he is a recipient of that message it's a really all that means is that I have a I have to implement a receive method and I could do these as async as well I have to implement a receive method that is void returning and takes in that message type um so uh and here's what the implementation of that receive method looked like in the case of converting ASCII to text if I get my message off the message bus um I do some car Ray magic and uh stash that in my local hex text hex text probably should have named that something else property which then gets bound to The View and The View um the one thing that's a little bit different here is you'll notice I've overridden the loaded method because in addition to implementing observable recipient and being able to deal with messages you have to tell the messenger when you're ready by setting he is active to true so you can um kind of subscribe for messages when you want and you can turn it off to when you want so um so my my base view model for this guy and this is that sort of the inverse of converting ASCII to hex um he looks very similar he sets his active to true sends a a different message a convert ASCII to hex instead of a convert hex to hesky um but he sends it to the messenger and then whoever receives that picks that up and then he receives these requests to return to convert ecstasy and that's what that code does here again the beautiful thing about having this all in my view models is now I can have tests for it for all the possible permutations of um hex and ASCII and I can handle um stuff that's not ASCII I can handle empty string and I can I can write tests around all of them and I don't even have to really know about um how the commanding works I can just call receive with with an example message of whatever I want so that's that's a way to have communication between a bunch of different components on screen and also it isn't a one-to-one correlation I could have five components that are listening for uh convert ASCII to hex passage and they would all get a copy of it and they can all act on it and just play cool stuff on a dashboard um but uh so it isn't it isn't a just a simple point to point messaging it is a like a multicast delegate style message um and I'm not using any Blazer resources to do that it's all happening in the mvvm toolkit on the client so let's take a one level greater and see about adding validation rules to an object and wiring up like an edit form to it because that's unfortunately that is my day job is data over text boxes and incidentally this is where looking through the Hound that change log for the new version of the toolkit that just released last night this is where a lot of the changes have been uh have been focused because there's kind of some knuckle-lighted things that I have to do here so I've created a new model type we want to get contact information because my wife says I need more friends and um we need to know their name uh phone number that kind of thing and so I'm using just plain old data annotations um attributes here to to do our validation um there's yet another class that's available on mvvm that provides this observable validator pattern and what this does it provides an overload for the set property method that says Okay I want to set this property's value and also I want you to validate that it's good Data before you do it and then it provides some some other things to us like you could say validate all the properties on the object but unfortunately that's a protected method so I've redirected it to be a public method here so that I can call it in my view model ten fingers okay 10 minutes left um so you'll notice that I'm not using any of the cool coach in here I'm actually using like plain old backing Fields with properties um that is the thing I think they fixed in the uh in the latest version of the amphibian toolkit the reason why I'm doing that is that if I put these attributes on this private property or private field they wouldn't get transferred forward in the generated code it could be on the property and then none of my validation um so it's kind of a bummer but it's I it looks like it's no longer a bummer um so we've added yet another page for um called form because I'm not very creative and this has got pen uh a Blazer edit form on it and I'm telling it that the model is the view model's contact property which is that contact info record or class that we were just looking at um and you'll notice there's no like regular data annotation summary here or any of that stuff and I'm not I'm not calling uh validate on the built-in because you can do data annotation throughout Asia in Blazer natively what you but that means that then now data validation is a concern of your view and not of your view model your View Auto won't have any idea if data is valid or not unless you give it a reference to that view which is bad and makes it makes it undustable because now you gotta you've got to build that up in in your testing to be able to test your view model so instead I've created my own really poorly implemented validation summary for data validation errors and this is using some more convenience methods given to us from the nvidium toolkit um the main one here is this has errors property on my view model object in this case and then get errors which returns to me a memorable of strings um of your or a numeral of validation results I guess yeah yeah yep and so that's what I'm what I'm looping through to show validation errors the other the reason why there is a code block on this is that the um when there are errors in the mbvm validations Library it raises a different event called errors changed because those are because the properties are validating themselves whatever they're set there isn't there isn't a need for me to call validate at the very end they get validated when they get set and so then when they find problems they raise those errors changed a bit and so all I'm do doing is getting that and telling my my summary re-render yourself because there's new errors that that you don't know about yet so what is the uh what does the view model actually look like for the form um this guy is pretty simple he's just extending our regular view model base he doesn't actually need to know much about the the validation stuff now where he starts to care about it is in the save command when my views when somebody clicks the submit button on my view it sends a save command to the view model and in handling that command I want to make sure one last time that everything is valid and then if it has errors I want to write out some stuff to the console because why not and if it doesn't ever we'll write out a happier message to the console because I didn't want to implement a back to a back-end API for this purpose yet so let's look at how this all works foreign remember from five minutes ago our email has got both the required attribute and the email address attribute so that means it can't be null and it actually has to parse to a valid um a valid email address and and now you can see just by changing Focus because that errors changed event was raised on my view model and um per inside my summary object her summary control that summary then started said oh I need to re-render and show these new error message same case with the phone number obviously names it's a lot less picky about because any string is a name um and it's a little bit subtle because of the bootstrap styling that comes out of the box but this save button is actually disabled now um because uh because the the model is no longer valid so it's like clearly you can see it gets a little bit darker and I get the like pointy finger Behavior because it's valid but because those are all required if I can't actually submit submit with that on the back end anyway I need to need to get close to the end here let's see tests for this it did not write just for this because it's just attributes um and I wrote my own validate validation summary which is horrible but the idea is that you don't really want to mix your Blazer data annotations validation with your mvvm DNA annotation the validation boy those are long term because if you do now your mvvm stuff has to know about those view classes and it's going to make your stuff a lot harder to test and a lot more fragile and you might end up having uh validation rules in two spots which makes it easy to get them out of whack questions I know I blasted through a lot there it's been on purpose because uh I tend to do code rich and slide poor um talks but for further introspection all this stuff is up on GitHub uh there's some leaks inside there to both the mvvm toolkit source and the documentation for it which is pretty good there's also some samples on the mvvm toolkit beat none of them are for a Blazer yet that's one of the biggest issues that's in their repo and um in an effort to coax them to do something about it I replied to one of those issues saying hey I'm doing a talk on this it'd be really cool if you guys had like an official sample here's my crappy one um and as ever um I'm Kelly that's where I live on the internet and where I work questions is there ever a face where you need to sort of injured thought leader of Earth oh the question was is there a case where I use regular Blazer web assembly over webassembly with our Blazer web assembly with the Canadian toolkit when I have to um there's a couple clients that I've worked with that have kind of rolled their own mvvm pattern and it's pretty gnarly because they've got stuff like async method calls and property Setters and you know it it makes for a weird class of bugs I would much rather use an mdvm framework made by people smarter than me so you know while it's not my preference I think there still is a valid use case for that if somebody has got some existing Legacy code that they want to bring bring into uh into Blazer first and then go through and fix all the bad end of the end decisions they've made um so yeah I think there is a use case for plain old Blazer there was one other question over here guess not that it oh geez that's how you capture right uh it seems like having pain purpose of the nvpn true kit is to steer you away from the son of the Dead adamant that it happened that yeah no I think that that would have been a a good number five to to this slide Venus stubborn if they put the latest is that in the same with the brazer a pass student there or pendulkin to Trapper boards that you can fall through if you're not super duper strict and he didn't can in episode into the wed but it seems like this you didn't talk that at least guarded a little lip yeah well and it's to provide a lot of useful stuff that would it that his boilerplate in a in an mbvm app um and so that you don't have to write it or maintain it you can rely on Microsoft to hopefully do a better job of that then at least I can anyway get that you're referring to does it have something where business us is there any way want their play either they got if new proponent all of a sudden shows up that you created dynamically uh can you get the last so uh the question was are the Houston messaging in mbbm toolkit persistent or not I think is what you're asking or durable and it's really not because it only ever lives in memory inside of the runtime there is by default the the usage is a weak reference so that the GCE can actually clean up those messages when it wants to you can change that to use a strong reference which means you can while you're processing a message you can hold on to a reference of it and it won't get cleaned up so if you do bad things and stick it somewhere you could get it back you couldn't necessarily send it again except by manually sending it again you couldn't like replay it anyway so that is all my time thank you very much uh we have the 10 minute break
Info
Channel: IntelliTect
Views: 4,676
Rating: undefined out of 5
Keywords:
Id: h53P6Je-Fbs
Channel Id: undefined
Length: 45min 59sec (2759 seconds)
Published: Thu Mar 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.