Instructor: Here I have a new project generated with Angular CLI. This is what you usually get when you start it up the first time. What we want to do is to dynamically style our application based on the client that logs in.
First of all, let's have a look here at our app component. What I want to do here is, to first of all, remove the whole links section below, to have a bit of a cleaner view, and add here a class, AppTitle. If you want to style this AppTitle, you have different options.
You can go, of course, in that AppComponent SAS file, or you can go to the global file and add here the rule. Let's say here, caller.black. In this case, it wouldn't change much. If I change it to red, you can see how it gets applied to our theme here.
This is all good and nice, but what if you want to have different kind of themes? For instance, I want to have different kind of styles. If I copy this here, I want to have client A styles and another one which would be Client B styles. Similarly, for Client A, I'm telling it the color should be red. For Client B, the color should be blue.
Now, so far these are stand-alone SAS files, which aren't actually being touched by Anglular CLI. We have to go to that Angular JSON file here and change the configuration. If you scroll down a bit, you see here your project and the architect section, the build section, the options. In there we have that styles part. You can see here the main styles, CSS, SCSS file is referenced. We can similarly reference also ours.
What we can do here is say Client A styles and similarly, Client B styles. Since we changed the configuration, let's restart our application. You see our application refreshes, but what happens is actually not what we expected. The title now changed to blue, which is that color which was specified in Client B styles.
The reason here is that of all of these SAS files which will actually be injected into our document, B is actually overriding what A specified, and is overriding what style specified.
To change this, we can actually change this notation here to a different syntax, so just a more elaborate syntax which gives us a couple of nice options. First of all, we have here an input. Input will actually be our file. Then we also want to give it a bundled name, since it's no more dynamically generated, and we don't want to have that hashing at the end. We'll see later why. I name this Client A. Then, of course, we can do the same for Client B.
The final piece that is missing here is that we need to tell the CLI to not include this into the default HTML file. We use that inject false property which we add obviously to both parts. To see whether this works, let's compile our application in production. We use ng build --prod.
If you go to our build folder now -- let's go up to the disk folder, DynCSS -- you can now see that we get a Client A CSS file with our correct CSS in there, Client B. You can also see the default styles which has also the hash appended to it. These two are perfectly suited to be added at runtime to our application, which is the next step we are going to look at.
To dynamically load the CSS at runtime, let's go back to our app component. What you're going to do here is, we are going to add some buttons. Here "Load Client A theme" and now we add here a click handler which says, "Load Theme."
We give it a parse and that will be "Client A.CSS" and the same, of course, for Client B. We also have to define this function, of course. Let's go to that TS file, into our app component. Let's add here that Load Theme. Here, we will get a CSS file which is a string. Inside here, we need to dynamically load it. Usually, what we have here is our head section.
Inside here, you have a link which is rel="stylesheet". It has maybe, type="text/CSS". Then, we will have href which will then point to our CSS file. For instance, Client A.CSS. This is exactly what you are going to do now dynamically.
In order to add something to the head section, we need access to the document object. We could actually directly reference that globally, so we write "document." and so on. But we want to make it a bit cleaner, if you want, by importing the corresponding token from angular/common.
Here we get a document token. Now, we can actually inject that into our constructor. That allows us also to mock that out during testing, for instance. We use the inject token document. We can actually use that below here.
The first thing that you want to do is to grab an instance here off that head section. Let's say headElement= this.document.getElementByTagName('head'). Potentially, that getElementByTagName returns multiple elements. It's an array but there is usually just one head element in our HTML file. We can see if you grab that first one here from that array.
Our next step is to create such a link section. We say const newLinkEl = this.document.createElement. This is type link. Now we add a different kind of property. We add here the rel = 'stylesheet,' newLinkEl.href. Here we can directly add here that CSS file.
Finally, we need to add it to that head element. We can say appendChild to that newLinkEl. Let's save this. Let's refresh your application. We see the two buttons. If I click the button, we will get an error. It seems like that Client A.css file is not present.
The problem here is that during development, if you go to that network panel and we refresh, you will see we get a styles.js file. All of our styles will be embedded inside here. Therefore, it's not possible to dynamically fetch them at runtime.
What we can do is to go to that angular.json file and there's a property called extractCss. I will set that to true. Again, we need to reboot our application. Once the application boots, you can see the styles file disappeared. If you go to CSS, we now see we get the styles.css file, actually.
If I click here, you can also see that Client A gets injected here when I click Client A theme. You can also see that this one changed to red. We dynamically now added a theme at runtime which obviously gets immediately interpreted by the browser. We see the effect here, having that font here red.
We can a little optimize that script or that section in that we don't want to append each time we click that button back and forth, but just if we don't have it already. What we want to do here is add an id. Call it "client-theme." That's something we are going to do here.
Before actually adding it, we can try to get a reference. We use getElementById, client-theme. If the existing element here is present, we actually want to set the href to the new one. Here, TypeScript complains because this is an HTML element. We need to cast it as HTML link element so TypeScript will be fine as well.
If that is existing, we just set href and of course, we do what we did before. We add that whole new link tag to our head section. Again, let's try how this works. If we use Client B, we get the blue one. If we use Client A, we get the red one.