Building Modular Angular Apps with the Nx Standalone Project Setup

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey folks so let's together walk through this tutorial of leveraging the annex standard project setup together with anger so what are we going to learn this tutorial so first of all we are going to understand what such a standalone project setup is about it is basically a single product workspace just similar to what the angular CLI generates for you we're going to explore how to surf the app build the app and what other things come with an NX workspace setups just built-in linting prettier configs as well as just tests or Cypress tests we're also going to see how to leverage code generation to facilitate the creation of new components and even scaffold entire libraries and talking about that we're also going to have a look how to modularize your code base together with NX so let's go so as the first step we create a new NX workspace and we can do that by using npx Create NX workspace I usually append here at latest to make sure I grab today's version it's not being cached by npm then let's call this my NG app and I could just then hit enter and go through the process of answering a couple of questions choosing anger is my framework and so on now to kind of shorten that a bit I can directly give it the preset flag here and name it angular Standalone so this will now ask me specific questions for the NX standard on Project setup with angular as its framework note that you could also pass the dash dash pm to provide a different package manager now we are going with npm here but you can totally also use yarn or pmpn so this first question here we get with stone sheet format we want to choose I'm just going with plain CSS now because it's not really relevant to the tutorial would you like to use Standalone components in your Android application now if you confirm this you will use the angular Standalone API rather than using NG modules so definitely let's go with that one would you like to add routing sure would you enable distributed caching to make your CI faster now let's confirm this as well so what this does is it allows you to distribute the NX cache remotely and it is specifically interesting in CI but we're going to talk about this a bit later so for now let's just confirm this and we'll now get our workspace configured so if you open up this workspace now in vs code or whatever editor you prefer this is a structure that we will get so first of all you can see already that we have a source folder at the root here which is very similar to what angular CLI would generate for you so you can see here the apps folder there's the app component and a couple of other things that we told an X during the setup to generate such as the routes here you can also see this end-to-end folder up here and this is a Cyprus based end-to-end test setup that has already been configured to be run for our application here for our main application furthermore there is prettier setup for you there is just already set up for you and there's also linking and specific also anger linking configured for this workspace now what might be particular here specifically for you if you compare to an angular CLI setup is that first of all you see that NX Json now the NX Json is the metadata file that helps NX understand your workspace better and so one part here is for instance what operations are cachable and we'll talk about caching later but this helps greatly reduce the computation time needed for building testing and relenting your workspace for instance there might also be defaults for your generators further down here where you can specify like what style you want to use for the application generator and similarly also for the library generators and you could add more to these another particularity is there is no angularjson so an X compared to angular uses single project Json files for potentially multiple projects in the workspace compared to one gigantic angularjson and this gradient also helps avoid merge conflicts which is especially happening when you have multiple team members working on the same project in terms of the structure this is very similar to the angular Json and if you're coming from that you should immediately be feeding at home here so rather than an architect's node here we have a Target notes in an X which specifies basically the targets that you can run for this application which can be here to build the surf there's some extraction for i18n there's the linking the test task there's even a surf static that just uses an HTTP server to serve your pre-built angular application even if you look inside this build task here for instance you can see it's very similarly structured to what anger CLI uses so there's an Executor which would be the Builder for an angularjson file and this one directly refers to the angular DAV kit Builder that the default angular CLI setup also uses plus the options that come with it so how do we serve our application now well if we go to the package.json at the root of our workspace we have already a couple of scripts there so we could simply Run npm start npm run build or npm run test and what behind the scenes will happen is it will invoke NX to serve build and test application so instead of going via npm we could also just use NX directive now I don't have an X installed globally so what I do is I prefix it with npx which would directly use the locally installed NX version in this workspace so I can just do npx NX Surf and this would now serve my application since there is just one in this workspace obviously it would directly serve this one and if you go to localhost 4200 we then see our application show up in the browser so the syntax we switch the NX commands work is very similar to what the angular CLI does so it is NX then the Target that you want to run and optionally the project that you want to run now in such a setup that we have here so which is a standalone NX setup there's just one project to run right now and so we can omit that and NX would use that default project directly out of the box so knowing that we can now also run linting testing for our workspace so we can again use npx and X test for instance and this would use to just set up for our application and run it here directly and similarly we can also run the linting and this would now run the angular linting and yes linting directly on the workspace and we can also invoke the end-to-end testing with Cyprus that we mentioned before now for Cyprus here you can see that end to end folder and this is kind of like its own project because you can see here there is a project Json now the project here specifically is named end-to-end and has a Target end Trend so meaning if you run this test we run MPX NX the end-to-end Target on the end Trend project you're obviously free to rename this to something different what here would happen is now our application gets bootstrapped automatically and falling to that the Cypress tests are ran against that project you can obviously pass the ad dash dash watch flag to run Cypress in an interactive mode so you can directly inspect the test run which is especially helpful in case of Errors now we've run those tasks individually but you can also run multiple of them at the same time and so for that NX comes with a run many command and then we can give it a dash dash Target or just short Dash T and mention here friends we want to run the build the test and the Lin task and we could potentially add more here and so if you hit enter it will run these now in parallel as much as it can for the entire workspace you can also notice here already a difference in that some of the tasks for instance the linking and the tasking of our application have already run before and so they didn't execute again but rather they got pulled out of the cache and this is one feature that an X comes with that for instance the angular silai lags is the ability to Cache a computation for instance if I rerun all of these commands again none of them would be run because we didn't change any crucial inputs such as the source files or environment variables and so all of them got cached and therefore are extremely fast so an X also comes with co-generation capabilities so not just to generate the initial product setup as we did with the create Annex workspace but also on the go for instance to generate new components to even generate entire libraries or just augment an existing project with let's say store book or Tailwind support now to understand what capabilities there are we can leverage NX plugins the NX plugins are those that contain these type of generators executors or even automated code migrations now for angular in particular we can find these plugins if we go to the packages and at the root and here we can for instance see the annex angular plugin which is just an npm package containing these generators and executors that are helpful for angular development further down here we also see packages for Cypress for just JavaScript and typescript support linting and so on so to understand what generators come with our Annex angular plugin we can just use the NX list at NX angular command and this will Now list all the executors that are built into this so this would be the equivalent builders in the angularjson file or also all the generators that we can use with this NX angular plugin so if you would want to generate a component we could just use the NX generate command or short just G give it the name of the plugin which in this case is ADD and NX slash angular colon and the name of the generator so this case would be component and then we give it a name of the component which let's just call this hello world we can pass a flag such as standalone to create a standalone component rather than one that needs to be attached to an NG module and we can pass further Flags such as also the dry run to just see what would happen if we run this component generator so here you can see we'll place that component in its own folder Direct in our application now if we run this without the dry run it would generate this component for us now you can find all these options that you can pass to this generator here directly on our docs but there's also possibility to install the NX console plugin to have a more straightforward way of exploring these especially a more visual way so if you would generate the same component with NS console we can just click here generate say component from that Annex angular plugin and then give it the name again here which is hello world we specify here a project which would be myng app which is the main project that we have in this workspace you can see a live dry run window being displayed down here and now we can provide some further options such as giving it a standalone flag again you see the dry run live down here in this window and once you're good to go with this you just hit run here and now it will generate a new component here in our source folder app hello world and if you open this up you can see this is a standalone component that can directly then be imported in another Standalone component and be used on the your angle application so the next important step to know is obviously how do I build my application such that I can then deploy it to some server if you go back again to our package.json you can see there is already a build script linked here as an npm script but there is also the project Json where you have to build Target directly Linked In Here and So based on what we learned previously we can run the build script just by using npx and X build and this would now create a production build of our application and place that in that this folder up here so you can see that this folder here my NG app and this contains now the bundles of our application that we can then just copy to some static web hosting and serve it to our users from there awesome so with this you now learn the basics of using an X in such a standalone basically single project mode with angular you learn how to create initial product setup you learned how to serve the application you learned about the annex Json and how the project Jason is kind of slightly different to the angular Json how caching potentially works as well as how we can build it and then ship it to our user base now you could stop here but there's obviously much more to explore so I would like to go a bit more ahead if you want to stick around with me and show you how NX can provide even more value to your anger development by modularizing your code base with local libraries adding some boundary rules and finally then ship it again to our users so if you're ready stick around and let's go ahead with the second part of this tutorial so when you use the angular CLI to develop angular applications what often happens is that a lot of the features are co-located in this single application and so ideally you have them structured by folders that correspond to your domain areas of your app and have them all situated in here now this can lead up to being very monolithic in terms of the code structuring and also folder boundaries are not really as strong as you might sometimes want them to be now in the next we suggest a different approach and this is particularly because NX allows you to create so-called local libraries which come with a public API that allows for a much better control in terms of defining boundaries so what is private to that library and what in turn you want to expose to other libraries or to your application so let's have a look again what I would want to do is generate new libraries and so I can do that by running again NX generate to add Annex angular and this time I give it the lib generator name now the name of my library here the first one would be products I also want them to be placed in a specific directory now I could have them all at the top level here so one folder for products and all the other lives I want to create or just co-locate them somehow and organize them in one or multiple folders now in this case I'm just going to go with a modules folder I also want to have the Standalone API to be used with these libraries and ultimately I also want to give it a simple name flag which generates a more compact version of the component names we can again use the dry run just to make sure that generates exactly what we want to have and what you can see here is as expected to decrease this modules folder it would have the products folder and in there it would generate some initial components and some additional setup so let's remove the dry run and generate this Library you can now see here we got this products Library it also comes with its own just config its own eslint config meaning that you can also then run linting and testing for just this product Library it also has some product components here generate for us that we can use as a starting point or just remove them and generate new ones again potentially using the generators that come with an X it also has such an indexed file and this is basically the public API for your library so here you control what components or other types utilities you want to expose to other libraries and your application and so this gives you a very good control point of what should actually be just private and what should be exposed now let's also generate another Library that we call orders and we might potentially also want to have another one that is called UI and this is supposed to be some shared utility library for IUI components for instance so I'm also placing them in a subfolder shared which specifically communicates to other team members that this is supposed to be shared across libraries and potentially multiple applications even so what we got now is the following structure here we can also explore them in NX using the NX graph either within vs code here using NS console or you just run NX graph and then you can navigate here through this port and hit this show all projects and you would now see the structure of this application now right now these are not connected to our application at all and are simply products that live within the same workspace let's now connect them to our application and import them when we generated all these projects what happened as well is that here the TS config base got updated and in particular here the section about the typescript path mappings with some alienses that directly point to this index.s files that I mentioned earlier so this now gives us the advantage of being able to directly import these libraries into our application by just referring to these typescript path mappings rather than relatively navigating to the index TS file which obviously in the long term helps with maintainability of our code base so let me go to our application and we already have here a routes file configured and so what I would like to do here is specify a couple of routes and import our libraries over the routing mechanism obviously you could just also import the component directly if that better suits your specific needs and so first of all we would have here that the initial path that directly just maps to the annex welcome component next I would like to import the products and orders libraries so again we can say pass products and that will direct the load to component because we used here the Standalone API and so I directly import here from my products library that we generated before the product component and similarly I can do the same for our orders component and I would rather import this from the orders Library here and obviously the component here is named orders component let's also remove here a static import that vs code accidentally pulled in now if we again use the graph we should be able now to see the difference if we refresh in that we now have our libraries connected to our application you also see here the information about whether this is a static link which means we would directly import the component into another component for instance or whether it be lazily or dynamically loaded which is indicated here with this dotted line and if you click on it you can also see where exactly this is happening on the other side the model shared UI is still not referenced anywhere and therefore it's not connected to our application so to finish up our lazy loading setup let's go also to the app component and remove here the direct reference to the NX welcome component and it's also go to the corresponding template here and remove the components that in the end we just have the router Outlet left now if we surf our app and if we then navigate to our localhost for task 200 at the root we still see the component the welcome component being loaded but if we now go to products it should load out the products component which just prints product works and similarly or this components should also work so by motorizing our code base with these local libraries we already got a much stronger boundary between these features and so if we properly separate our applications based on their domain area or business context this should give us a much nicer isolation held with maintainability so compared to folders this is already much stronger however nothing prevents me from really importing from any other libraries granted they re-export the components that I want to import and this could sometimes even happen accidentally because vs code just auto-complete some import based on some util function that multiple workspace products might have so for this exact purpose NX introduces a concept of so-called boundary rules and they are implemented as lint rules such that you can Define which of these products can depend on which other projects so how you define these boundary rules really is up to you and NX is really flexible such that you can add your own system now what has proven to work well is by adding kind of like two Dimensions the the first being which type of project can depend on which other type of project so project type is basically a library that is of type feature implementing an entire use case or a library being of type UI because it hosts maybe order components that are being used in these feature Libras for our order use case flows it can also be a shared Library let's say our design system that is shared across multiple of these projects and the second dimension is usually the scope of the project so whether this belongs to the orders domain or the products domain or whether this is of scope shared such that it can be reused across multiple projects now to classify these projects an X has a mechanism of tags so if we go for instance here into our modules orders file here and open up the project.json there is a tags array that you can specify and these are really arbitrary strings so again we can now represent these two Dimensions we can say this is of type feature and of scope order again these are just strings that you can Define you don't even have to use a colon but you could use a dash or something else that works better for you similarly we can go to our products project Json and add here that this is of type feature and a scope products and our shared UI Library would be of type UI and scope shared so this is a classic example of a library that is potentially usable within all the various projects that we have so now that we have classified these products we can Define the actual rules and this is done at the root level eslint file here in this ESPN RC based Json so there you can already see there is a rule defined for this module boundary eslint rules that comes with an X and so we can add here further rules first of all let's specify that the source tag of type feature can only depend on libraries that also have time feature or we want also to have the type UI be able to be imported in a feature Library so these as you can imagine are the top level user flow implementation Library so they depend on potentially a lot of those other types of libraries for instance if you would have another library of data access for data fetching or some logic libraries or utilizers you might also want to allow a feature library to depend on this country though for instance if we Define the type dimension for our UI Library we might want that a UI Larry can depend on another UI library of course but we might not want to it be able to depend on a feature library because the relationship should be rather vice versa so the UI libraries tend to be more at the leaf sign of our project graph tree so now that we have defined the type Dimension we can go ahead and Define potentially our scope dimension so we can say here of scope orders should be able to depend obviously on another scope orders libraries it should potentially also be allowed to depend on-scope products because maybe the orders Library need to needs to import the product lists to visualize in the recent orders that you have taken but the products might not necessarily need to be able to depend on the orders now again these are just constraints that we impose here in this demo tutorial and what we also want to have is probably that it can depend on scope share because maybe we want obviously that type of scope to depend on shared UI design components and similarly we can now also Define the one for our products and so for products we would want it to only be able to depend on products because we don't have any further domains here and finally there's the leaf node here which is our scope shared which should only really be able to depend on scope shared and so once you have these rules in place let's test them let's go to our products component library and try to import orders which in theory should be prevented by this rule so if I go to my products component and this again lives here in this products library and I try to import here the orders component you can already see these squiggly lines and if I hover here you can see that it indicates that a project tag was called products can only depend on Libs tagged with scope products or scope shared and so this is really nice if you have the linting extension installed so you get it right away when you import a component obviously on CI this will block you as well because you might potentially run a command that runs linting for instance I can do run many Dash T lint This would run linting for all the different projects here and for one of the five targets it's failed which is specifically the import that we did here in our products component Library so that's it congrats you should now have a pretty deep understanding of how to leverage such an NX Standalone project setup together with angular now obviously there is more to potentially learn and some next steps can be for instance to explore recipes about adding Tailwind how to store book integration can actually be really beneficial for let's say developing our shared UI component design library that we used in our demo but also how to speed up CI because one of the next steps would be to set up a CI system and run NX builds in the most efficient way possible also make sure to first of all subscribe to our YouTube channel we publish such videos regularly so you don't want to miss out on those auto connect to our Annex Community slack so there's a lot of people hanging out there helping each other which could especially be handy if you run this tutorial and have some further questions that come up so hope you really enjoyed this tutorial and you learned a lot and I'll see you potentially in one of our next videos
Info
Channel: Nx - Smart Monorepos - Fast CI
Views: 28,642
Rating: undefined out of 5
Keywords:
Id: ZAO0yXupIIE
Channel Id: undefined
Length: 25min 53sec (1553 seconds)
Published: Tue Jun 13 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.