So recently I needed to build a simple web component that would initially need to offer a basic contact form but have the extensibility to offer greater functionality at a later point.
Thus far, when using Angular 2+ I've been using bootstrap. But as flex-box support is much better than it was a year ago I decided to use the new "flex-layout" module with the Material component library that is maintained by Google. I really liked the original version of Angular Material so it seemed like a nice opportunity to take the new stuff for a spin before using it for anything bigger.
So first I should go over why I chose to use flex layout.
I wanted my contact form to have inputs for the first and last name on the same row for larger screens. However, on smaller screens, I wished the last name input to reflow under the first name input.
Whilst there are many ways of achieving this with CSS I opted to use flex layout to solve this one.
Getting the package
Now one big change with Angular Material is that the layout system has moved into a separate package.
This is great as whilst the Material components are a great library, you are now able to use the layout independent of the component library. For my scenario, it does, however, mean getting both:
as I was actually wanting to use the Material Components, still it's nice to have the choice.
Composing the masterpiece
So I ended up using the flex-layout module in exactly one place in my code
<div class="contact-full-width" fxLayout="row" fxLayout.xs="column" fxLayoutGap="10px"> <md-input-container class="contact-full-width"> <input mdInput placeholder="First name" name="firstname" [(ngModel)]="message.firstName" #firstName="ngModel"> </md-input-container> <md-input-container class="contact-full-width"> <input mdInput placeholder="Last Name" name="lastname" [(ngModel)]="message.lastName" #lastName="ngModel"> </md-input-container> </div>
The Layout API that the module provides was really intuitive and allowed me to achive what I wanted to do with minimal effort.
Looking at directives for container elements
By using this on the container element I was able to specify the direction in which child elements flow.
Whilst in isolation this is nothing special when combined with the fsLayout.xs directive we really being to see the power that we are given.
This directive is part of the Responsive API that flex-layout offers. The suffix of ".xs" relates to a break-point for mobile screen sizes.
This allowed me to achieve the exact thing I was looking to accomplish. For non-mobile screen sizes I was able to have my inputs flow from right to left but for smaller devices, the inputs would be stacked, flowing from top to bottom.
This offers a really cool way of specifying the gap between child elements. Whilst it is possible to do the same thing with the CSS last-child selector and margin-right property of the other elements, to the uninitiated this can seem a bit counter-intuitive.
Using this directive will apply the provided value to the margin-right property of all but the last child element of the container.
The following shows the child element's information in the Chrome debug tools.
As we can see the value that we placed on its container has been applied as an inline-style on the element. This would prevent any specificity issues that we may have due to styles defined globally. Whilst I don't like applying inline-styles myself, I'm fine with the library doing this as it is able to compute (and more importantly re-compute) based on the currently rendered UI and has the appropriate hooks into changes in the UI such as new elements being added or break-points being triggered.
Exploring the fxFlex directive
Now I actually didn't need to use this one at all as the default behavior of the child elements (the two text box inputs) was to take up an even amount of the space available in the parent element. But I wanted to see what the effect would be by explicitly setting it to 50(%).
It was really cool to see the results:
And on smaller screen sizes we get:
So the fxFlex directive was detecting changes to the size of the viewport and then dynamically updating the UI as the flex-direction of the element changes from row to column. This is much easier than setting the max-width on small screen sizes and then resetting it and setting the max-height on larger screen sizes using CSS and media queries.
Material and the Build Optimizer flag
So once I had finished creating my simple form application I decided to package up my app for deployment.
As I was using the angular-cli I created a production build. However, when I inspected what had been created I was horrified to find that the files created for my 1 form, 5 field app totaled a size of over 1.1MB!
I then used source-map-explorer to inspect what I had generated and found the following output:
One of the main culprits was the Material Components library which totaled nearly 0.5MB. The promised tree-shaking and minification of the production built setting didn't seem to be working.
As my app was pretty small I had made it a one module app. I decided to break out my Material specific dependencies into a separate module but still no improvement.
Lastly, I found the following GitHub issue that suggested using the new "--build-optimizer" options when building i.e:
ng build --prod --build-optimizer
After doing this I was relieved to see that my total bundle size went down to just under 0.5MB more than a 50% reduction on the previous attempt.
Now to be clear, there is an issue that is currently opened with the Material library so the standard "--prod" setting should work in the near future.
The "--build-optimizer" setting uses the npm package @angular-devkit/build-optimizer during AOT builds to remove Angular specific metadata and reduce duplication relating to polyfills generated as part of the transpilation process as well as other optimizations.
Whilst this package is still pretty new (v0.0.14) at the time of writing, it's nice to see the amount of effort going into improving the build process. After all, this is something that can help improve the experience for every user that would use our applications.
After all this my basic feelings are:
- For many scenarios, flex-layout with its responsive API is really going to be a great tool for harnessing the power of flex-box.
- The build process optimization story of Angular seems to be ever-changing and progressing, which is great, but something I'd take into account when starting new projects or working out time-scales.