Nx After Dark: Creating a data access library with NgRx Component Store

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to nx after dark today we're continuing the energy insights angular application and last time we set up and a workspace using nx12 and angular12 today we're going to do some final uh configurations that we want for now and then i'm going to start implementing the first data access library using ngrx component store because that's really my cup of tea that or rx angular but yeah i'm using inject component store today here we go this is my my homemade background it's not quite after dark yet but the sun is setting and the markets just uh just past 9 p.m beautiful weather still no not much wind so energy today is probably not looking too great have a look you might remember if you've seen the the last episode that we're actually i'm going to visualize some of the public data about the inner danish energy grid let's look at how the co2 emission is right now so it's very high in the coming uh four hours or so until midnight and then early morning it will be come pretty low um if you want to say that yeah hey michael yeah you're here for the component store i know uh yeah if we want to say that denmark is green it should really be below 80 grams per kilowatt hour so this is talking about co2 equivalent emission gases greenhouse gases so not too great let's see the last hour we had 35 wind and solar power again it's dark and there's not a lot of wind so it's actually pretty good for for this time of day um well it would be better if there was a lot of wind but fortunately we usually get a lot of hydropower excess hydropower from sweden and norway as well but yeah i want to visualize this data first uh this data set here the co2 emission forecast or prognosis which they wrongfully call it in my opinion uh so here in denmark we have two uh price areas dk1 which is western denmark connected to norway and germany and netherlands and dk2 which is eastern denmark connected to sweden and yeah do we have one across the baltic yeah i think we do we have one to eastern europe as well so as you can see here today is the 19th and this forecast is showing the 20th the whole day of the 20th as well so that is tomorrow this next day forecast is released i think it's around three o'clock in the afternoon or five minutes past but yeah i'll show whatever data is available so let's get into this nx workspace this is what we created yesterday energy insights is on my github profile now so this is all open source if you want to have a look i'll just be making some minor configurations to start with let's see the editor config is there something i usually put in here yeah i could do this i could say if we have css or sassy css files um i want to use single quotes if i'm in an uh html file for example an angular component template file which i won't be using i'm using inline templates i'm getting into single file components these days so i want everything in one file including the modules and the templates and the styles but if i had one it would be okay to have 120 characters um let's see we also have some configuration files like json files yaml and yml which is also yaml which is a stupid language but we have to use it they can also have infinite length basically yeah because the the wrapping by prettier is not always pretty it it actually adds some noise to the yaml configurations especially um markdown files yeah mark mclean length 120 and in the ts files i want to use single crops okay is there anything else here for formatting let's look in the prettier configuration ah pros wrap yeah single quote yes print with 80 hour parents avoid yeah those are my prettier settings and right now i'm just pushing to main but we'll start doing real pull requests for this feature okay we also set up our github actions workflow last time a basic one but uh good enough for now but i just want to add some hey claplati please zoom in the editor okay well i usually have it here where you can see 30 lines of code in one screen that's usually big enough but i can give it one more notch but then it starts to become quite annoying for everyone so yeah i only have um hd 720p so that's the reason why you might not be able to see it or if you're on a small screen um so this is the best um i'm going to to do so hopefully that's good enough okay let's add some environment variables for this workflow this is shared across the jobs in this workflow uh so we will no this is not needed node options yeah so node.js is you might know that it's by default it's configured to maximum of two gigabytes of ram which is not a lot so especially with tooling like nx and then all our web tooling it quickly runs out of ram so most machines including zi's have at least six gigabytes you can afford to set aside to allocate to node.js and so we do this like so node will look for this option whenever node is started uh now now let's configure let's um let's add the proper nx environment variables as well so that it's grouped nicer in the nx cloud dashboard for that we just need to add this github.event number and that will group it per branch which will be visible in the dashboard and then there will even be let's see the run id will show the individual runs in in the same branch yeah this is what we need for nx cloud and um if you saw it last time you might see that i also i'm using pnpm instead of yarn or npm and i had an issue with uh okay good that it's a bit better um it won't be nice for anyone i think if i zoom in more than this so yes um [Music] yeah so i had an issue with cyprus so i just removed and trend testing hello nice to see you today uh so i removed cyprus because i really don't need it and but it turned out i was trying it out today at work and it was actually an issue with the newest version of pnpm and cypress and i simply couldn't figure out what the problem was so someone tell cypress or pnpm or whatever i i think i tried every possible configuration i could find and i couldn't solve it so yeah there are some issues with sometimes with pnpm sadly uh because it's the best option it's the fastest option but yeah it has to do with cyprus using some really old dependencies that are not nice and modern but i'll um i'll make an update if i if i ever figure out how to make it work for now i'll do without cyprus in this project as what else well we could add cash for cashing our pmpm store but it was already very fast so there's not really a reason to do that yet but github action does have an action to cache for example well we won't cache a node modules folder but we'll cache the global cache folder between the runs because it's always running on a fresh image of a machine but we can copy and the cached pnpm artifact from the last time but there's no need for that yet so what else we could share the note note version and say we want 14 x um because we're using angular 12 so it's finally supporting this version of node unused major version stable major version so we'll do like so so that we can easily switch it out between all the jobs like that okay anything else so we have our triggers after merge to main this workflow will be run on every pull request no matter the branch it's targeting on every workflow dispatch which is the manual trigger for github workflows and now that we have merged this one we should be able to see a button on github.com actually to run this manually but yeah we'll do it at some some point uh oh yeah let's see we have a linting task here it is our linting job and i re i actually don't want to run this if it's a pull request so if github event name is push now if it's pull request then we'll run linting if not we'll fix it in the next pull request there's not really i don't want to push to the main anyways so and i certainly don't want it to block my deploy just because of a linting error so yeah so now this will only be run on pull requests not in the merge run of this workflow anything else but pmpm yeah we could add some more options here we could say uh what could we say we could say a reported silence to not have a lot of logs from it we would have to turn that off if something went wrong but yeah i actually want to leave it in so that we can might work on that cypress problem at a later point we want to have the okay with pros and log files yes i don't know remember if this is the default but i'll set it here explicitly what about this verifying the store integrity no i don't want to spend time doing that actually so this will be the pnpm install command in our steps yeah this is good we're missing a deploy job but we don't have a host yet this is just a web app and the back end is a public api so yeah i might just deploy it to github pages as soon as we have the first version ready so we'll do that later as well let's see what we have here we have configure node pnpm and nx good good good good let's see what we have we have just one app at some point we're going to extract some of these generated files into separate libraries for now let's look at our um dependency graph from nx and you can see that we just have this one problem project the app project the energy insights app project so what do we want to have um well the application should only be uh connecting to uh well i wanted to connect to some kind of a shell module and that chill module or library will then connect to a feature library that feature library will then be able to access the data access library which is what we're going to build today so we're going to build a disconnected project right now and we'll use the we'll use the tests to to create it instead of testing it manually through the ui and the ui will come later and uh we're using nx so we have jest for our automated unit tests so i usually use what's called wallaby let's see if it works today i've been having some issues after it updated but i might get lucky yeah okay it's running so wallaby is a paid tool it runs the tests in the background and i don't think i have any tests intentionally uh but as soon as we'll add some we'll see that i start getting these inline messages if a test is failing or if i'm logging some output through the console similar to what you might be used to from eslint extensions or tsend extensions or whatever but for the tests and that is really a nice quick feedback i don't have to wait for test runners it's it's a lot faster it's keeping the tests hot as as i'm working and i don't even have to save the file for the test to run again it's a really powerful tool if you're doing a lot of automated unit tests okay so now we want to generate our first library and which domains do we want to have we also have some grouping folders here so let's say we want a domain about co2 so now i want to use my nx console and generate an angular library and now i want to look at my i'll close this down for a second and then i want to look at my defaults which is in my workspace configuration my defaults for my the different generators i already set them up for angular application and now let's look at the angular library generator i have some defaults here and i want let's look at the other options [Music] and i don't want to go do i want to go with buildable libraries no not right now it's still a small project and i've been having some issues with that on windows that it's not really faster so why why would i do it but that that would be used for incremental builds but sadly it hasn't been working well for me on windows prefix okay let's set up the prefix and i think i made the prefix of inner energy i know clever you have it called energy insights publishable that would be if we would create it for the npm registry to publish their routing no we don't want routing by default script tags okay tags shouldn't be there shouldn't be a default tag but we should definitely add some tags okay so these are the defaults we need this project configure the library generator defaults and now this was on the main branch i'll push those commits and now i'll actually branch out this is a feature branch and it's for the co2 data access library so we'll run it again just to have the my defaults here so i'll call it uh beta access yeah dry run is starting here by nx console um data access well it's already it will already be in the co2 folder so i don't need to name it co2 but normally i would do that but it happens to fit with the name of this grouping folder the domain grouping folder um what else we need we need some tags for sure some linux tags here they are so i will say type beta access library and i'll add a scope and that will be co2 and we haven't really set up any um linting rules workspace linting rules yet for which types of libraries can access other ones but we'll do that when we have a few more of them so creating this library let's see what we're getting here it is in the co2 grouping folder it's a library and then nx library doesn't mean that it's reusable necessarily or that it's meant to be published on an npm package a library this is a workspace library so it's an encapsulated set of code that corresponds to for example a layer of a part of our application so this is the co2 part of our application and it's in the data access layer um well actually it can it contains a few layers like it will have this one will have persistence it will have an http client which is for the persistence layer and it will have state management for uh yeah using ngrx component store so we got our library and everything is set up here so the readme file the co2 data access http um persistence and weak management local state management and it should be added here yeah so now just it's it's this is the just workspace configuration which is also used by my wallaby tool here but nx is managing this configuration we also see an entry here in the ts config base json because now it set up this path mapping so that i can import it using this name in the other libraries or in the application so that references this path lib co2 data access source the index file so that's the entry point for this library that's where we should be exporting everything we want to be usable outside of this library so that would be services like the ngrx component store yeah workspace.json also has a project for this live this library and it has one target uh test oh and also the linting target yeah so it doesn't have a build target um intentionally on my behalf so let's go with this generate co2 beta access library and what do we need now now we need njx component store so let's say we're using pnpm so i'll say p npm add ngrx component store and it's a self-contained library so uh oh here i must have set up some some configuration to which is confusing oh it might be because i updated the major version of pnpm since yesterday on my machine so that's just recreating the the node modules folder but we'll we'll be installing and jerk's component store and it doesn't depend on any other ngrx package and it's self-contained it can be used uh to combine it with the inject store for example if you want to subscribe to observables from the global and your store you can do that using the generics component store but it's not a requirement so that's that's good to know it's standalone library from ngrx and pretty small okay so are we good now to add nvrx component store oh we're at version 12 nice we're in sync with angular so now now is the question does this one have an ng ad schematic i don't think so there's not really any global setup it's a local isolated service but let's just try for funds because if there was one we were not able to use ng add with uh ngrx or sorry nx cli but we are able to call that schematic manually if it exists so it would be called ng add for for angular libraries okay there is actually a schematic i didn't know that i wonder what that is doing exciting exciting stuff wait what's this okay pnpm is doing some interesting stuff so where's my note modules i have no idea right now oh the joy of front end tooling ah that's that's really strange that's really spanish node modules and your x no not going to give you that okay so something strange happened with pnpm maybe uh so i'm going to something went really wrong now so i'm going to delete it oh it really went wrong at this time reload the window this was uh the recommendations for visual studio code that's configured for this workspace uh circuit so i could see here just runner and here the nx console i already have these ones installed uh but this one i haven't installed it because i'm using wallaby instead yeah so but there was a button so that new new developers joining this this project can click a button to get all the recommended tools okay powershell died so something very strange happened let me just shut down some processes i have a lot of windows subsystem for linux running i don't even remember if that's normal for this machine let's try to completely restart vs code okay now we have a terminal i'll use no thanks i don't show again oh yes i'll decide the extensions okay delete my note module folder i'll actually also delete my log file for pmpm come on and now run pnpm install again something went wrong after i downloaded the latest major version since yesterday i think so now let's see how fast it is 1200 dependencies a lot of them cached downloaded starting to add them to the node modules here something is really hogging my machine right now i wonder what it is i definitely need to get some more ram i only have 16 gigabytes and front-end tooling will eat that up in that moment okay so now we have the ngrx component store and let's try to run that schematic again if there is one and now i can see my node monitors okay that's looking better so does it actually have schematics it does okay nice ngi okay it's not happy about that file strange wow it's really not happy about that one okay i have no idea what the ng ad schematic would do for component store that's unexpected yeah anyways this is this commit is at ngrx component store what did it do nothing absolutely nothing okay great thank you for all that trouble okay so now we have our let's get back to our library our data access library for the co2 emissions and it generated this module we don't need an angular module that's one of the benefits of njrx component store no stupid angular modules just services functions much better much better so [Music] let me fire up wallaby again here we go and what do we actually need okay so we need to remove this export and we can export just an empty object for now remove this one and let's create the hmm well we need an http client that we can trigger in our component store effects yeah let's create that first um so let's go back to the energy data service in denmark and find this api it's called co2 emission prognosis so i will start by creating the uh well let's say i have an http folder here i call it co2 emission prognosis http and it's an angular service so it's an injectable and might be missing some plug in here um remember then the name spaces myself and now this one can be this will be a stateless service so we can just provide it in the route so i have one instance for the whole application and calling it the co2 emission prognosis http this will be an internal service so it won't be in our public api file here and to call this api let's go let's go into the data set and we can then see here if we go to our chrome dev tools let's see if i can zoom in in that one as well go to network look for the ajax requests and submit another one i can see these two here they're slightly different for some reason oh yeah there's the number so this is uh it's using this c can ccan data provider or framework which is based on python and postgres but it has this web api on top of it so i can use the same api here so this is the url i want to call that in here start here for now then let's see what is the request it's a get request okay and does it have any get parameters yeah it has one query string parameter called sql or sql and it actually has a sql query inside of a query string parameter and yeah i i know this already because i'm using it at work because we we make software for the energy industry um at system mate so but so i'll have to call it with a sql parameter a query string with a sql password sql query like so so that's pretty strange but also in a funny way it's also very flexible because you can write arbitrary select queries here it's read only so i can't change anything but i can select and i can get the server to do the job of processing and mapping renaming all of that so there there is a table here it's called co2 imis prague for emission prognosis and it has these columns the date well we can see it here in the network tab if i go to the response preview you can see that there's a success true this will be false if there's an error it has a result property here and this is the field definition of the different each each this record array has objects each object or each column in this table actually has these fields with these postgres types so it's a time zone time stamp so that means you can take one here and see that it has the utc offset which is what we always want because this one is the local danish time but then i'll have to figure out oh which time zone were we in the denmark at this point so this might one is much better because it specifically it's a very specific moment in time so there's no ambiguity about this there's also the price area and so we had the two in denmark dk1 and dk2 and then there's the co2 emission so this is the grams of co2 equivalent greenhouse gases emitted per produced kilowatt hour of energy so this is the data i have available in this api so i'll just call this one get and it will return an observable and yeah i don't we we need to create a type but for now let's create one here um oh this is the name of this interface this one was the well we can map it to update this one we actually don't even want to download because i i don't want to use that one but i want the price area for now i'll just use this type pk1 or vk2 and the co2 emission it's a number we want to return a read-only array of these records read only and this is the observable from ng or sorry rxjs good now we need the implementation right um but first let's start setting up the test for this already now so i'll have a file called the same but then dot spec that yet in the end and it will have some string here and then call back grouping the test cases this is my test suite and now you can actually see wallaby running the tests here and saying all is good in this test file let me import the object under test.scut this is this http client and here instead of a string i can take the name of this class so that if i rename it it will also be renamed rather than having a hard-coded string here let's set up the first test case i don't really know what i want to test right now i know that we want a variable of the http client here for each test case and how would we get that we would do that let's do it in the before each book that's our setup for each test case so we'll use angular's test bed why because then it will set up we can set up um we can use angular's dependency injection system to set up the dependencies but we won't run compile components so it won't be slow so this is an angular core testing testbed and we just need the inject here don't have any dependencies yet so we will resolve this dependency store it in the htp variable so what can we do here we can call the get method right and it doesn't have any parameters right now um what would we do um i mean do we want to use async weight or uh i'm not i don't know how to use rx marbles for testing that's usually the way to go um but i don't know how i haven't invested the time to learn it properly yet so i'm doing other other things instead which is usually enough unless you're doing complex timing issues or something like that um so yeah what could we do we could do [Music] it is it an rx7 or is it already just take last or take first or what what is it called let's see well we can just how can is it still to promise let's use that for now this is good for for this one this would be an async function and we will if i could spell we would await oh it's not even a separate operator is it ah wait come on keyboard it's not my hands okay the result connect the result to the empty array and let's return an observable empty ring for now oh i want it from rxjs here we go and oops let's look here oh to equal to b is strict equal so it has to be the exact same array here we're saying it's an empty array any empty array not a specific one so this is actually a running test now look at what we have tested wow so this is the start and uh what do we have do we actually have um test coverage for this project let's see let's have a look one test running in 12 milliseconds and our test coverage is a hundred percent look at that everything is covered we're so good at this um yeah resolves an array that's it for now okay um add asp clan for the co2 emission crop gnosis peterson let's push this branch so we don't lose any work so yeah is this enough i mean we just basically right now to create the component store we just need some service that we can call uh that we can trigger through the effect so this is actually enough to get started with the component store so let's do that just for fun but of course this is not a proper implementation yet um but uh yeah an empty array is an array of this of these these data structures so this is actually good enough for now and let's get into the component store instead let's create a state here oh and i'm already starting to regret that i named this one htp i want to call it co2 forecast instead and luckily it wasn't exported or referenced by other files so i yeah this shouldn't be a problem renaming this folder because i want to group the files that will change together and that would be the component store and the http client it wouldn't be two separate http client or two separate component stores so grouping by type is usually not a good idea it won't serve you well so on the top levels you should group by the area or the domain or the logical group and that's what i'm trying to do here so let's do the component store this will be called the co2 forecast store what do we need we need first we need from angular core we need the injectable decorator factory yes it is not a decorator it's a factory as you can see here we're calling it with parameters and the factory returns a decorator that is then applied to the class very complicated it's angular after all co2 forecast store okay what does it need to do it shouldn't be provided so i shouldn't do this because we want it to be scoped on us like the right level of the application and this is one of the nice parts about component store we can provide it uh on a specific page for example a page component or even more locally on a let's say a chart component and whenever the page is is destroyed whenever the page is destroyed for example when you route a different page the page component will be destroyed and that will then if that page component is providing this component store the component store and all of its all of the subscriptions it's managing all the observables will also be uh to turn down so this is nice this is what we need to think about which scope should this component store have and this one is not specific to a component necessarily um this is for the data access the state that might be right now it's not shared but i'll build it as if it was a shared one because we might need to combine this data with some other data at some point so we'll find the right scope eventually and i should be when i talk about the code i should be sharing my screen again so here it is so yeah we need to find the right scope uh what else to create a component store we need to extend the components to our base class like so so we say extends component store and then we need shape of the state here so that will be called co2 forecast state always an object everything else in gerex uh so what do we need in here we need well for now let's just have these uh records it'll be in read only um property which is a read only array of this um this thing so actually let's extract this into a separate file now we need it in two files already so which the x part should have told us but it wasn't necessary until now so we need it in here in the http service now we have it and now we need it in our component store as well so let's import it there we go and i usually create this type with a name as let's do that and put it in the same file so the type um which is called co2 emission prognosis records is a read-only array of co2 emission prognosis records to have a fitting name hopefully and yeah i prefer to use read-only arrays read-only properties read only objects because then you're you're forcing yourself into thinking in immutable data which can always be only a pleasant surprise rather than having shared mute a mutatable state that can only give you nasty surprises okay so this is our state we have some records and we need to get them some from somewhere but yeah this is actually now we have a component stored that does nothing so um we need to before we can make an before we can update the state uh or parts of the state before we can create effects or subscribe or sorry before we subscribe to the observables we need to initialize it so how would we do that well we would have down here it doesn't have to be exported let's create a variable called initial state with the shape of this store state the store state has records so we can do an empty array as the initial state there is a super initial state so we're calling the constructor of the component store class from ngrx we're passing at the initial state um you might also do this at a later point in time but you have to make sure that you're initializing the state you could do this also by saying set state and pass it the full stage here so so we might also have done it like so any point in time but before anyone actually um before anyone i guess what is it like before you can call patch state or before you can what is it like subscribe to the selected state or something yeah you'll get a runtime error if you don't do this at the proper time so let's just initialize it here in the constructor this is the basics of an ngrx component store now we have a component store that has this internal state and this is really these records it's the internal state it's not what i want to expose so maybe i want to map this into something that's more useful to to my components we'll get to that soon enough for now what did we do we created this component store that's the primary thing here and how about adding some tests for this one better do it now or it will be later as in never to the describe block we need a string and then we have a group of test cases and this is the test suite we import the subject under test which is the store it's called not state it's called co2 forecast store so we use the name of this class as the description of this test suite and now we can add a test case here and now wallaby is happy running the test so we need some setup okay so here um so for component stores it's usually it's not very good to do what we did before with http client having a shared variable so here we can use this other pattern of having a setup function that could take some parameters and i don't want to add the type because that will change a lot but here we can say we want the store and again we will be using the testbed to inject this store resolve this dependency and then we will return an object of everything that we need for our tests and this will for now just be the store but now we have a problem because the i mean if we use this here if we call down here we'll see that we actually have a problem say we just want to expect that it hasn't but it has been in this result or a result so now we'll see what we will tell us here there's no provider for the co2 forecast store because it doesn't have a provider scope here defined in the application we would need to provide it in some component don't provide it in a module so here in the test we need to provide it in the test bed with our testing module so we'll add here providers or this store and now it's happy look at that it's green and the reason for calling it specifically this function rather than running it in a before each hook is that we need to set up some stop dependencies usually to be able to control to control the data flow in and out of this store and usually you'll have some logic that's initialized by the store or sometimes right away when it's created so so logic was effects will start processing immediately so we need to set up some like step up the htp client for example before that happens and then control what it's responding with so for the sake of the test and so to do that we need to we'll pass that into this setup function here so we'll cover the co2 forecast store in our test and we could take a look again at the coverage explorer or we could look at my wallaby tab here and the test touched by this the the class is touched by this file says that it has a 100 test coverage we can actually also see it here every line of code has a green meaning that there's a test exercising this part of the code and the test is passing so that's good we're not testing much but we have 100 test coverage and so now comes the question which side effects should we have and also importantly which public properties do we want to have from this store i mean for now we could uh just export or make it a public property for these records until we know what we need that could be a good starting point that would be an observable of these records should use the type i created by the way and with component store we can yeah we're extending this base class the component store that gives us three important methods one is select one is effect and the last one is updater and there's a few others for this one let's go with select and then we pass it a call back it takes the store state as a parameter and then we say okay which property of that state do we want to select that's the records so now we have a public observable of our records so if anyone subscribes to that they will receive the update for that every time there's new data so that is part of this do we have it covered now by tests no we don't see look here there's a white rectangle here and i could also say toggle uncovered code regions and it will tell me that it's part of the code it's not it's now highlighted in yellow this part of the code is this is wallaby doing this it's never touched by any test so it's not covered so there's no code uh being run by a test that actually touches this part of the code base right now so how could we test that we could say we want to uh oh i should name it dollar because that's the convention to use this uh hungarian notation and i want to add tests about this observable in particular emits records this is the basic test here how can we test that we could let's say it initially emits zero records make it async we could say we're creating the store so we run the setup and we extract the store from that returned object and then we say it has the records observable we will type into that we will take the first i i really need to figure out which extension i'm missing to do these auto imports getting really annoying and rxjr warriors luckily i know a lot of these by heart by now we'll take the first value and that will mean that this observable will create uh complete so now we can turn it into a promise so this will be our records wait the promise way i can't spell so expect records to equal empty array look at that okay so but for every green test it must have been red at first or we don't know if we're testing the right thing so what if we instead initialized it with a record here let's say there was a 180 grams of co2 per produced kilowatt hour at this point in time our these are let's see these are actually five-minute uh values as the property suggests um so but yeah it's the 19th of may and which minute are we in right now we're in danish local time it's 22 15 hello so this is the start of this five minute interval and i'm in two hours offset from utc and we need price area i'm in bk1 so this will be my record here oh it's a date okay so we'll construct it here so now what are we getting we're getting a failing test because this is what we're expecting this is what we're receiving an array with an object in it so we are testing the right thing here so if we adjust the implementation to our expectation which is an empty array initially now it's running so yay me oh michael you're saying it's finish notation uh okay so which one is hungarian which one is finish notation let's google it i can't remember now server rules and finish notations yeah um and what is hungarian notation it was used a lot a few decades ago oh okay yeah so hungarian is the prefix notation so you would call something uh array of unsigned 8 bit integers so that's the type in the start of the name and then it's a list of numbers or it's a long account number long as a data type right well as long as 64. no no that's oh longest integers 64 bits i think i don't even remember like who cares about data types today uh but yeah that's the hungarian notation so finish would be the prefix or the suffix and that's what you get with rxjs now there's a dollar in the end we used to put the dollar in front of jquery objects not too long ago and now this annoying habit of of dollars in the end of arctis observables and there is a story about this i wonder if it's um if it's mentioned here i actually don't want to open this article because it's on medium well if it's not behind the paywall i will read it yay ben lesh not behind the table there is a story i wonder if he is mentioning it here it actually used to be like i get that up here i don't know if you can see this but oh it's too small i don't want to change it uh let's go back to the editor so i called this one record dollars back in the day that would be record dollar and because dollar looks like a fancy s and there would be other symbols for i don't know let's say we had an observable of a librarian then there would be a special n number here or if it ended in an e they would if what one ends in an e um i don't know that's a siri uh it would have the euro sign because that's a fancy e so it's stupid things like that and now we're stuck with this stupid notation i thought we got rid of that when we left jquery behind but apparently not and so now we have to tell everyone all the time this is an observable it's very important it has a dollar look at that so is there the story of this finnish rxjs notation yeah here it is look at that mice people with the eurozone yeah this is so dumb but now yeah of course this is too strange and you don't want to fiddle around with to find all these special symbols and you saw even that the euro sign is not something you can just use here people observable zero javascript is not happy about the euro sign invalid character so it doesn't even work but of course rx rx was first done in dot net and i don't remember if this is something you can do in that net but yeah strange story strange convention i really don't see the purpose of it but it's what we do so i'll do it as well but i won't enjoy it you can't make me enjoy it don't force me yeah sidetrack sidetrack the story of the dollar sign the hungarian no sorry the finish notation of rxjs yeah where did we come from okay so we actually added the test here so yay me add another commit what what was that okay the commit work um i don't know just ignore it so i'll push this to the branch and uh we've been going now for an hour and a quarter uh but yeah let's uh let's do at least one more thing what should we do should we do an effect or should we do the hcp client what do you say i'll have a drink of water which one http client which strange sql in the query string or njx component store effect which one do we do the effect okay we have two people here voting christopher nice to see you uh voting for a fact we have michael as well voting for effect okay so we'll focus on the njrx component store so we had these three methods that we can use we saw one of them here this that select we can select from the store this store state we can actually also select from any observable here for example a global inject store or any observable for that matter from any service that we can inject so that was one of them and now first of all we need something to be able to update this records property we already did it once but that was only to initialize the state here so if there's an update over time what do we do we need an updater that's what we do we and let's make it private because i don't want uh a component to update this state i want an effect to do it only so oh yeah let's switch my view thank you i want to take a look at you sometimes and then i forget to add back to the code view here we go okay uh running going back cycling back a second here we have the select inherited method for selecting a property of our store state but we could also select any observable here to expose that or to combine it with the state maybe any effect will work here or sorry any observable will work here with the select method but what else now we want to change this record state we did it once here in the when we called the base constructor from the component store class we passed it the initial state but this only works once when we then want to change this property later we need an updater that's one of the ways to do it at least so say update records then we call this.update updater and uh this is an then a function and that function has this current state and then we need to return state so that will be another object here we'll spread the current state in and what would we do we would oh sorry it will be an updater and it will be past state and then the updated value and the updated value will then be let's say these records that has this this interface this type so put that in here then this will be the updated records that will be passed here uh but let's just just call it records because then we can spread the state as it is it doesn't have any other properties right now but it might at some point but we'll put in these new records here so this is shorthand syntax for like so so that's why i'm naming the local variable records because it makes it easier to map it into this new state object so that's what we're returning so this is how it looks it's kind of a a reducer for one action um with this on method in the inject store if you remember that so what this returns is a method that we can call so say here we have another value we can now call update records and we could give it either an observable of records or we could pass it just an array of of records so this is uh the the result of calling the updater here is we get a method that we can call with this value um and if it's an observable that we pass here and jerks will subscribe internally to that observable if it's a value it will just be past this value once and we will also be passed the current state so we will slab in this new array of records to replace the existing state which might not always be the best option but it's the most simple one so we needed this updater and it's a private one because i don't want components to update it i only want effects to update i want this to run in the background so then now we need an effect that's the third inherited method we have we have this select method we have the updater method and now we need this effect method so this one this will also be a method [Music] and let's call it load every this is updated every five minutes so i want to look for an update every minute should be it should be like so effect and this we need a call back here and this callback will be passed and observable of whatever we want so what would we want to trigger this well in this case well uh what would we want to pass to this http client would we want to let's go back to that uh that was not the client it's difficult with the zoom level uh which one is the service here this one is okay so this should should this have any parameters that's the question and yeah it should like it should say uh which which uh which records are we interested in which time and maybe which price area is it decay 1 or dk2 or is it both i actually want to return both but in a specific time so it should be for example from n2 something like that so that's what we need to pass in but right now let's just have one without the parameters for this to get going so yeah yeah but we need something to kick this off uh so it could be the [Music] very filter of turbo this would let's say from two and these are read-only properties so this is what we want we want a um an interval a time interval of date times so i think we can do it like so and now this will be an observable of this query filter so now we need to take that i know this is looking a little strange we'll have another look added let me just maximize here this view so we we get an observable here and we'll see where that comes from in a moment and now we need to do something we need to pick off an effect let's say the first time or any time a value is emitted we want to load the new record using the http client okay so we need the http client now co2 emission prognosis http yeah here it is so [Music] we take the observable the pipe and now we switch map why do we switch map because if if we're suddenly interested in another from and to date we're no longer interested in the previous request that didn't return yet so switch the switch strategy will then cancel the previous http request and send a new one instead to replace it so we will get the query filter which again would will be this type right something is wrong right now and yeah we'll we'll fix it um this.htp.get we would pass in the query filter here what is going on right now function not implemented switch map where did i get switch map from oh my bad i'm still missing this like autumn import i don't know what which extension i forgot okay so switch map of course comes from rxjs operators now it's happy thanks for the switch map explanation well you're welcome you're welcome um yeah we have these different we have these different options of switching which cancels in the when we're talking about http requests it will cancel the previous request whenever a new one arrives or is created we could do concat that would put them in like a serial mode like queue them up and when one finishes it will start the next one so that's also a way of avoiding timing issues but it might delay the response you were looking for and there's merge that is it will fire up all the requests you're making and they will you'll get a response uh maybe not in order and that's where the issue comes in because then you're getting a response for the old query parameters uh not the the most current ones so that's usually not a good strategy for http uh requests and we also have exhaust that won't start any new requests if there is a current one running right now and uh that might make sense for example if you have an issue of um excessive requests and you can't figure out why put in the exhaust map operator and it won't make new requests while you have some already in flight but yeah switch map is usually good when dealing with http requests let's get back to the code i for i remember this time okay so we won't be using this right now but [Music] we will pass it here at some point when we set up the hp client to accept that for now we will get back the result of calling this this http client so that is the read-only array of co2 emission prognosis records these objects and uh what else do we need to do yeah uh let's see let's see let's see your component star has this operator called tab response and that one that one goes uh let's see should you use it here that will force us to have uh like we'll have one for so we got the records everything went well okay what do we do with the records and then an error uh okay we had some error response here what do we do with that it will force us into handling these two cases because if we don't handle the error cases the rxjs chain will break so our effect will break as well so this is a good little utility from njerk's component store when dealing with http clients for example uh because if we handle it both of these cases everything will will be good uh so this would be an unknown thingy and we don't really care what the why it errored right now but what we will do is we will say update the records and we'll just put in a blank array here to to clear uh the our chart if there's an error i mean we might put in an error message and whatnot and log it but that's for another time okay so when things go well what do we do with the records we call our updater function update records and pass in the new records here that we got from the http client these co2 emission prognosis records so this is what we're using our updater for we call it if there's an error we clear the array if we get back new records we will put them in the the records property by using this updater so this is one way of updating the state there is actually another way another way let me just what happened there let me show you that one as well and we get the records then what well we can call catch state that is a partial update of the store state so we could say well we have the new records put them in the store please so that's another way then we don't need to create this updater at all we could comment this one out and we would be able to update the store state however one issue i see with this is now we don't have a central location for every state update going through for this property because right now you can see here i have one reference that's in the error case and if i use the the updater in the success case as well now i can see that the records are updated in two places where is that that's these two cases in in this http response so now i can both see what's the source of the updates and also okay so maybe i wanted to do some mapping or what do i know some validation something here well i shouldn't throw errors in an updater but maybe i wanted to change something here or log something and now i have a central place to do that so i will stick with the updaters because then i i have an overview of where that state is being updated because we might have many effects and observables and stuff updating okay so this is our effect one thing left to do this will return a function that we can call so so now this the the the result of calling the effect method is a method that we can then call from somewhere else in our service to pass it either a value of this query filter or an observable of this query filter and it returns a subscription we don't want to manage subscriptions ourselves but that's what the that the the njrx component store will actually do that for us so we don't have to i'm pretty sure we don't have to store that subscription ourselves i might be wrong i might need to look into this but i'm pretty sure it will be shut down when the service is shut down so just make sure we have the right provider scope okay but the last step here is now we have this effect but we need to initialize the effect would need to kick it off and we could do that for example here in the constructor we could say load records every minute and then we need to pass it either a value of or it could be an observable because what what would change these two values well if the user wanted to browse different days but that's not really the case for for a forecast we want to look at the future not the past but it could also if we had a filter of the what's called the price area right maybe the user selected now they wanted to go to dk1 instead of the dk2 price area and in that case we wanted an observable right now yeah now we're going to need a date library of some kind um but since we don't really have an implementation yet for for this http client anyways uh we're just passing random dates here it doesn't matter right now um so this is what we need to do to kick off the effect so but this is only one value so it would just call the effect or trigger it once because this you can see here this this value this object is turned into by inject component storage turned into an observable and we then pipe some additional operations on top of that observable so the switch map operation which triggers the call to http client or our own http client and then additionally we handle the error case if there's an error in this response we will clear the array of records if everything goes well we will stuff in the new records that are in from the response into our store so that components can then get those new records right so yeah this is a bit to wrap your head around but this is basically the three parts of the injects component store the selectors the updaters and the effects now we have triggered this effect once and this would mean in the application we would load it yeah every time the page is shown for the first time or when we navigate away and then back this effect would be triggered every time this service the store is initialized we would trigger this effect once but i did call it load records every minute for a reason because i want to not only kick it off or trigger it initially i want it to run every minute so how could we do that well this is one trigger whenever the query changes we want it to we want a new request to be sent uh but we could also say now we're getting into there might be a better way for ngrx or component star that i'm not aware of but i would do it's a timer or an interval let's look at the dogs because i wanted to run initially um yeah black lives matter look at the timer operator create creator function let's look at this one does it kick off initially where's the marble diagram no marble diagrams no they are gone oh no what do we do okay let's see here ah so this is the timer operator so the interval operator waits for the past delay before starting and that's not always what we want we want it to we might want it to start oh that's too much zoom sorry we might want it to start immediately and timer is a good use case for this yeah so we can say timer zero because that's the initial uh delay and then you can say the number of milliseconds for each time it's it's then triggered so i'll copy this into the code and then it will be every minute right so that would be 60 times a thousand so every minute every minute and initially this observable will emit so let's import the timer it's a creator function so it should be here in rsjs it is so now we have two source or we need to combine latest as well that's from operators no it's a static one yeah i forgot combined linux so this is saying we have two sources we have every time the query filter is changed which it doesn't right now but it will at some point and or every time or well and every time the timer emits a value we will get uh we switch map here will be called and we could this will be an array so we'll also have the type time of value but we can't really use that for anything but that would be a number and i think it starts with zero or one or something we can't really use that for anything right now but we can use this query filter and that will also be passed every time the timer emits because that's what the combined latest [Music] function does it takes it subscribes to both of these observables and every time one of them emits well after they have both emitted once then switch map will be called and then every time one of them updates it will take the latest value of both of them that has been emitted pass it onto to the switch map here so now we actually have a background sync every minute and initially we will um we will call this http client and we will stuff in these records so this is now our effect is triggered and well initially and every minute so that's that's good uh so let's just commit that load records every minute and initially let's look at what we have here okay there's one more thing the select method has you can pass it an object an object in the end which is an option and you can say debunks true so if you have a lot of state changes in your store and like they're happening within the same uh vm synchronous vm turn or maybe maybe the micro tasks as well i don't really recall but like if you have several sources of updates for example um yeah like these two will will combine latest will wait for both of them to trigger so timer will trigger immediately and this one will get a value from the query filter immediately as well but if if we had like if these two changed within at the same time the this component subscribing to this observable they would only be called once because we're basically adding a d-bounce operator i think it is a d-bounce async so it's kind of i think it's like what's it called like uh well it's using the vm turn to synchronize updates so that the the components will only receive one update per vm turn um the javascript vm so it's a good default to add i don't know if it has any significant performance issue i don't think it should so yeah so so i i add them usually by default so my selectors my observables here evans records okay so what we are missing is testing as we can see here wallaby is telling me by with the white rectangles that this part of the code is not being touched by any tests i could also say toggle the uncovered code regions and it will be highlighted here in yellow so this code is never being called as part of a test but yeah this is uh we won't do this today because it's getting late it's it will be 11 o'clock at night soon and it is now after dark indeed um but yeah this this would be a good uh case for rxjs marbles but i i don't i don't know how to use them basically so i have another way of testing it but um yeah we'll do that some other time to to get back to that magic um 100 test coverage because right now we're only at 80 80 and that's not good enough so i'll push my updates here and uh yeah that's that's what we did for today we created our component store we created an http client they don't actually do anything right now i mean every minute the effect will call the http client and it will just give it back an empty array but you can see that we're starting to make these connections between the services and we're setting up the triggers of course we don't have any components so if we started the app nothing would happen we're not even importing this library yet but yeah i'm trying to show how we can use tests instead to to think about how the code will interact at runtime and to test it of course so that we know it will always work as expected so christopher you're saying great stream thanks well thank you for for joining it was a one of the long sessions but i hope all of you learned something i started recently started using indirect component storm and i'm very happy i'm definitely going to replace the global the big store and the big effects because this is this is much nicer in my opinion and has a lot of of good use cases we can also use it for presenters for presentational components that's a different type of store though this one is for the one we created here is actually for uh well either it's for shared state or it's for like isolated state that's not might not be specific to one component so it could be shared maybe not like maybe not on the global level it's not something we need many places in this app but it might be something we need in for example two charts on the same page so it it is somewhere between a local scope and a global scope probably yeah probably a local scope because it's it's local to one page probably i mean right now it will be local to one chart the first one the very first one for this app there's also another type of component store where you create one specifically for a component so so whatever the component needs it will have an observable in the component store but this one is more for now something that can be used by multiple components and they they will have to map the observables themselves if they want it in another format another structure so christopher you have not used component store yet uh see if it is better fit for your project you use the global indirect store before a lot okay good news it's uh really easy to get started if you're already familiar with uh inject store and effects because it's it's basically a mini version of that it has some of the same concepts let's take a look here at the njx documentation and i can show you where you would want to read about this because of course there is the we have this state section and we can find here the component store and like we discussed in the beginning it's it doesn't have any dependencies on the other packages so it's standalone and yeah michael is saying he hasn't touched the global store since he began component store and i definitely agree every time i look at my store and effects now i'm thinking i should i should put that in a component store i hate that it's in the store and the effects so much code for so little being done i really i really like that my favorite part is probably there's no ng modules i had a really complicated setup of a lot of nx libraries and interdependencies of modules and you have to load the import that module in your module to use it in a component or in a service and that's just messy it doesn't feel right and it's a lot of a lot of um yeah a lot of complicated work for for doing very little okay so here the android component store um i should share my screen again and i'm talking about it so we're here on the ngrx io docs we we open up the state section went to the component store and uh i would suggest looking at the architecture here these four pages is what you need to get going so it talks about what we also saw how you can initialize the store state how you can lazily initialize it so that would be at some point you call set state and pass it a full state object here so that could be in here they're doing it in a component in its unit hook that really hurts my eyes i wouldn't recommend that but this is an example of a component store specifically for this component to the point that yeah you're actually like managing the state inside of the component i wouldn't do that that's that's not for my taste but you can do it um you can do it and and then this way you don't even have to subclass the component store but i i would do that it's easier to test because you don't need the component and the component shouldn't really be messing around with the low level apis like like here okay so that's initialization and lazy initialization then you have read that's the selectors that we also saw select from sorry this that select select the movies property for example the right that's the updaters and but you can also use setstate if you call setstate you have to pass it the whole like a full state object not patch status can be one or two properties and then the final one is my preferred one the updater like we saw here uh so it will be past the current state so it's a reducer function it will be past the current state and this new value whatever you're calling it with that will be a movie object in this case so here they are taking the existing movies array and appending this new movie into this array uh so yeah that's the updater and it would be called from here it's public so it would probably be called from inside the component which i wouldn't recommend i would recommend managing the effects the side effects inside of the component store class your subclass yeah that's the right section that's the updater set statement patch state and effects here we also saw that that's the side effect so that's similar to the ngrx effects but it's it's being set up inside of the same service and has this kind of um yeah you have to get get used to this this new it's a bit similar to the effects of injects effects but here instead of subscribing to the action stream you will be passed a value any value that you decide and you will then have to pass it in somewhere did they even call it no oh yeah here they're doing it inside of components no where are they calling this effect oh okay so they're actually ah this is very i really don't like this but you can do it this way as well uh yeah the effect it will get past um an observable of of some value which is kind of the parameters for your effect or the sources of updates for triggering this effect and then you go and do something here like fetching movies from a back end or something we did it for for the records and let's see michael is saying now you can add the store to your one file component no no no no no that's going too far i don't want my component to be a component store but yeah i'm trying to do single file components but components are about presentation not about states and definitely not about some external isolated state like the one we just created components can be concerned with local ui state and that's another use case for the the indirect component store a presenter service a presentational service for the local state of of one component that would be scoped to that component and it wouldn't have any side effects for triggering http clients that should be done in another service another component store so read these four architecture pages if you're already used to indirect store and effects then you'll you'll quickly get the hang of it and it's it's like it takes you maybe an hour to read it a few times and try to understand it um and then there's like there's some some longer usage examples here and there's four different use cases but there's still only one written about here that's the local ui state like i just described let's see if we can do some of the the other use cases later this year i would like to contribute and now that i've started using it and inside here of the component store versus store um alex or krishko has described like what's the compared to the global store and the facts what should you pick what's the trade-offs yeah so that's also that could be interesting alex also has a lot of talks about this this topic so so that's where you just go look but first of all if you know the store and effects look at those four architecture pages and you'll you'll you'll figure it out i did really quickly so i'm sure you can as well so that that's all for today for nx after dark and working on this energy insights angular app we started laying the foundation for our data access layer we created this http client that doesn't do anything yet but we set up the effect to trigger it um in our component store and we tested 80 of it so a good start but not good good enough we'll have to to continue some other day thank you for for joining and see you around thank you christopher and thank you michael and anyone else watching bye
Info
Channel: Lars Gyrup Brink Nielsen
Views: 723
Rating: undefined out of 5
Keywords: nx, angular, ngrx
Id: 4z6anukUjPk
Channel Id: undefined
Length: 114min 38sec (6878 seconds)
Published: Thu May 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.