Question:
How do I prevent Code-Duplication in similar HTML-Templates?
Description
I have several different Templates for the same view/directive which I want to change depending on the environment. The templates are mostly identical but contain some parts which need to be changed depending on the environment.
Example:
A view to enter userdata might look like this to an admin:
<p> Username: </p>
<input ng-model="ctrl.username"></input>
<p> Firstname: </p>
<input ng-model="ctrl.firstname"></input>
<p> Lastname: </p>
<input ng-model="ctrl.lastname"></input>
<p> Authorization-level: </p>
<input ng-model="ctrl.authlevel"></input>
However I need to show basically the same view to a user without allowing him to change his Authorization-level:
<p> Username: </p>
<input ng-model="ctrl.username"></input>
<p> Firstname: </p>
<input ng-model="ctrl.firstname"></input>
<p> Lastname: </p>
<input ng-model="ctrl.lastname"></input>
<p> Authorization-level: </p>
{{ctrl.authlevel}}
I would like to prevent duplicating the code for both templates.
Possible solutions:
ng-if
Obviously, I could use ng-if
to exchange html-blocks within a template depending on conditions. However, this does not scale well. The above example is simple, but imagine I have 5-10 different versions of a template. The code becomes increasingly hard to read with the number of versions of this template. Also, I would ideally like to prevent shipping the code for the admin-view to the user, which I can't if it is contained in the same html-file.
custom directives
I could wrap every HTML-node which needs to be replaced in its own directive. This would keep the main-template clean and I could exchange the HTML-Template for the directive depending on the environment. However, all those additional directives would cause a ton of boilerplate-code.
(This seems to be closest to Angular2's components though)
third-party-library angular-blocks
I found angular-blocks which seems to be tackling the issue I want to solve quite well . However, it doesn't seem to be very popular and looking at the implementation I am concerned that this might cause performance issues on large applications (due to several nested $compile
-calls).
Are there any options that I am missing? Do you know about any best-practices or style-guides for this?
ng-include
I have a similar situation in my project where I use ng-include
along with a couple ternaries or ng-switch
es to achieve something similar to what you're looking for.
Put your first code block in a file and call it something like userTemplate.js
. But change your authlevel section to account for the variation. In this case, I'd use ng-switch
(but sometimes a ternary is all you need):
<p> Username: </p>
<input ng-model="ctrl.username"></input>
<p> Firstname: </p>
<input ng-model="ctrl.firstname"></input>
<p> Lastname: </p>
<input ng-model="ctrl.lastname"></input>
<p> Authorization-level: </p>
<div ng-switch="ctrl.authlevel">
<p ng-switch-when="user">{{ ctrl.authlevel }}</p>
<input ng-switch-when="admin" ng-model="ctrl.authlevel"></input>
</div>
Then everytime you need this chunk of code, you can use it in another view with ng-include
. For example, in a user edit form:
<form class="user-form" ...>
<div class="basic-info" ng-include="/path/to/userTemplate.js"></div>
<input type="submit" />
</form>
The nice thing with this approach is that with a little forethought and careful design, you can make these templates so versatile that they can be used for creating new resources or viewing or editing existing resources.
One option would be to use ng-switch
, and in the new angular 1.6 release candidate there is a option called ng-switch-when-separator
.
https://code.angularjs.org/1.6.0-rc.2/docs/api/ng/directive/ngSwitch.
With it you can give multiple options to one ng-switch-when
:
<div ng-switch="$ctrl.view">
<p> Username: </p>
<input ng-model="ctrl.username"></input>
<p> Firstname: </p>
<input ng-model="ctrl.firstname"></input>
<p> Lastname: </p>
<input ng-model="ctrl.lastname"></input>
<p> Authorization-level: </p>
<input ng-model="ctrl.authlevel" ng-switch-when="version1|version2|version3" ng-switch-when-separator="|"></input>
<span ng-bind="ctrl.authlevel" ng-switch-when="version4|version5" ng-switch-when-separator="|"></span>
</div>
Another cool option could be Multi-slot transclusion (I have never used it) https://docs.angularjs.org/api/ng/directive/ngTransclude
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With