Showing posts with label 12.0.0. Show all posts
Showing posts with label 12.0.0. Show all posts

Thursday, 1 July 2021

Faster angular universal with pwa

This article speaks about an interesting side-effect of mixing angular universal with progressive web application (pwa) - and that's a massive performance boost!

The Setup

--
Let's start by creating a standard angular-cli application (v12.1.0 in my case). I enable SSR (angular universal) on that.

universal

Once this is done, let's quickly check if SSR is working as per our expectation.

run universal

Node server fires up on port 4000 and I check for my webpage's source.

pre view-source

Wonderful! Nothing fancy so far, and everything works great! CLI application works and universal works.

The next step is to add PWA support.

pwa

No extra configurations so far, and that's how it will be. Let's build our universal application once again and serve it on localhost:4000.

But wait!!! Now when I view my webpage source on the browser, I get this:

post view-source


So does this mean that my SSR is broken?

The answer is Nope! My SSR is perfectly fine :-) Let's run a quick check to prove this. Open up POSTMAN and run a GET request against http://localhost:4000. And voila! I see my server-side-rendered page once again!

postman


So what is happening here?

The conclusion and explanation

--
The reason why we do not see the server-rendered content on the browser source is that, the index.html is now cached by service-worker at the browser. The browser does not need to wait for the server to render the content any more, and it simply serves the cached version, and Angular takes over thereafter, like a normal SPA. Open up ngsw-config.json and verify that index.html is one of the cached resources.

ngsw-config.json


The search-engines/crawlers, on the other hand, would see your website just the way POSTMAN sees it, and will continue to have the entire generated HTML for search-engine-optimization. So this way, you gain the performance boost via service-worker caching, without losing the advantages of angular universal!

Note (and a small exercise): If you remove index.html from ngsw-config.json and re-build your universal, you would see that the server-rendered content is back on your browser source, since the HTML is now no longer being cached!

Cheers -:)

Wednesday, 11 September 2019

Angular - custom lookup form component

@angular/cli - 12.0.0

Most of our application requirements are fulfilled using standard HTML elements. However, we may sometimes need to stretch ourselves when we are working for large enterprise applications. In such cases, reusability becomes a strong factor which determines ease of development. Today's use case is one such custom requirement.

This article speaks about creating an Angular component which acts as a lookup - an input field with a search button. The button opens up a modal window, which shows a list of values from which the user may select one. Additionally, this new component should also angular-form-ready, which means, it should be capable enough to be registered as a template-driven or reactive form element.

Let's first talk about the component itself.

The GUI

app.component.html


The app-lookup component takes in a couple of inputs - formConfig and lookupConfig. A sample implementation is shown below:

app.component.ts - sample config

When user clicks on the search button, a modal window opens with data fetched from server (in my case a simple promise). Use selects a record and clicks select. The data gets populated on the input form.

lookup modal

lookup form

Submitted form

If allowUnlistedValue is set as true, the user will be able to enter a value which does not exist in the list, and the value will be accepted. If not, then the form element will return empty value.

So this is all about the component.

Now let's see how to set it up!

We need to extend this component so that Angular recognizes this as a valid form element. Since this element is of input type, I have implemented the interface ControlValueAccessor. This interface needs to implement 3 methods - writeValue, registerOnChange and registerOnTouched.

The writeValue method is the one which is responsible to writing your value to the view from your model.
The registerOnChange is used to propagate changes from your model to the view. This is how the form knows that one of its members has changed its value.
The registerOnTouched is used to let the wrapper form element know that one of its elements has been marked as touched.

ControlValueAccessor interface implementation


Error Handling

Question: Now that we are done with setting up the value read/write operations, how about error handling? How do we let the wrapping angular form know if our custom component has an error and needs to be marked as invalid?

Answer: To do this, we inject an instance of NgControl into the component, which gives us the FormControl instance. It is this form-control object which sets the errors in the component conditionally. Remember, it is a custom component, so everything needs to be set by us :-)




Note: The ErrorStateMatcher has nothing to do with this implementation, but it is more for the material input component. ErrorStateMatcher marks the component in red when it has an error.

As a result, if allowUnlistedValue is set as false, the user will not be able to enter a value which does not exist in the list, and the component (and subsequently the form) will be marked as invalid. This is demonstrated by disabling the Submit button when form is invalid!

Error handling


Do note: A much more in-depth set of instructions for creating custom form elements can be found in a blog by Pascal Precht.

And there you go. You have your own angular component ready to be used in a form! You can download the source code from GitHub and play around with the configurations.

Cheers!
Follow me on Twitter or connect with me on LinkedIn.