Posted on • Originally published atMedium on
Conditional Templates for Angular Components
tldr;
One of the many benefits of working in Nx workspaces is the ability to share code between applications. In an Angular application, that means being able to share components between multiple applications. The problem is that different applications may have different design systems, even though the functionality is the same. I recently ran into this issue, and wasn't sure the best route to go. In my situation, our old design system was based on (highly customized) Bootstrap. Our new app uses Tailwind. So I was a little stuck on what to do next. I first thought that I could just throw the Tailwind classes in the HTML mixed in with the Bootstrap classes. This would technically work, because if the app doesn't include Bootstrap styles, then it would only take the Tailwind styles and vice versa. But my worry was that the HTML templates would get out of control with extremely long class lists. But I realized there had to be a way to conditionally swap out the HTML for a template. That's what I decided to try and do, using theenvironment.ts
file.
Component Outline
For this example, let's create a demo card component we can use. We'll make two HTML templates, one with some Bootstrap card styles and one with some Tailwind card styles.
Bootstrap:
<!-- card-bootstrap.component.html --><divclass="card"style="width: 18rem;"><img[src]="imageUrl"class="card-img-top"alt="..."><divclass="card-body"><h5class="card-title">{{ cardTitle }}</h5><pclass="card-text">{{ cardDescription }}</p><a[routerLink]="url"class="btn btn-primary">Go somewhere</a></div></div>
Tailwind:
<!-- card-tailwind.component.html --><divclass="p-20 bg-purple-100"><divclass="bg-white rounded-lg shadow-lg w-full md:w-1/2"><imgclass="rounded-t-lg"[src]="imageUrl"alt="..."><divclass="p-6"><h2class="font-bold mb-2 text-2xl text-purple-800">{{ cardTitle }}</h2><pclass="text-purple-700 mb-2">{{ cardDescription }}</p><aclass="text-purple-600 underline text-sm hover:text-purple-500"[routerLink]="url">Go somewhere</a></div></div></div>
Now that we have the two templates, let's look at the component's TypeScript file.
// card.component.ts@Component({selector:'app-card',templateUrl:'',styleUrls:['card.component.scss']})exportclassCardComponent{@Input()cardTitle:string;@Input()cardDescription:string;@Input()url:string;@Input()imageUrl:string;// other component logic}
Now in this example, I intentionally left thetemplateUrl
in the decorator blank. Normally, it would have the valuecard.component.html
. But our component has two templates that we made above. Next up we'll figure out one way to change between the two template files.
Conditionally Changing the Template File
Depending on how long you've been using Angular, you may or may not be familiar with theenvironment.ts
file. This file is used at build time for your Angular application. You can read a little bit more about the difference between build time and runtime configuration in Angular appsin this article. In short, the values in the environment.ts file can be used throughout the application, but if any values change the app needs to be built again for the changes to take place. When an Angular app is generated by the CLI, there is anenvironment.ts
file that is used for local development by default, and anenvironment.prod.ts
file which is used when the app is built for production environments. When the files are used is determined in theangular.json
file.
Now that we have briefly covered what theenvironment.ts
file is and how to use it, we'll talk about using it for our scenario. I figured I could use thisenvironment
file to determine which template the app should use when building the app. If I added a value to the file, such asuseTailwindTemplate
, I could use that to determine which template to use. Let's look at the component's TypeScript file again.
// card.component.tsimport{environment}from'src/environments/environment';@Component({selector:'app-card',templateUrl:environment.useTailwindTemplate?'card-tailwind.component.html':'card-bootstrap.component.html',styleUrls:['card.component.scss']})
Now the value oftemplateUrl
, is set based off the value of the attribute in theenvironment
file. To test it out, you can build the application with theenvironment.useTailwindTemplate
attribute set totrue
and open the app in your browser. You should see the Tailwind card on the page. If you set the value tofalse
, you should see the Bootstrap card on the page.
Conclusion
The example here is simple, and potentially excessive for a simple card component. But imagine your component has some complicated logic that you don't want to repeat in multiple components. Any time you have multiple components with the same logic that needs to be repeated, the two components get out of sync quickly. In those cases, this may be a good option. In our case, we will be using this option to use Tailwind in one app and Bootstrap in another. Also, it will allow us to incrementally convert the Bootstrap app to Tailwind. The logic can remain in a single TypeScript file, while being able to use multiple templates.
This is just one way you could conditionally swap out the template file for a component. For some other ideas, you cancheck out this Twitter thread.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse