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 bees 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 ponents 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 $pile
-calls).
Are there any options that I am missing? Do you know about any best-practices or style-guides for this?
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 bees 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 ponents 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 $pile
-calls).
Are there any options that I am missing? Do you know about any best-practices or style-guides for this?
Share Improve this question edited Dec 7, 2016 at 16:31 mhatch 4,6156 gold badges42 silver badges65 bronze badges asked Nov 29, 2016 at 13:44 H WH W 2,5963 gold badges24 silver badges45 bronze badges 2- why not just use ng-disabled?? – harishr Commented Dec 1, 2016 at 15:44
- create a custom directive on the pattern of ng-formly, it would help you greatly... – harishr Commented Dec 6, 2016 at 7:41
4 Answers
Reset to default 3Make a template and use it with 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/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/api/ng/directive/ngTransclude
What's preventing you from wrapping the top section up in it's own template? This works when the dynamic content is above OR alongside the static content.
User-info-template.html
<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>
User-view.html
<user-info></user-info>
<p> Authorization-level: </p>
{{ctrl.authlevel}}
Admin-view.html
<user-info></user-info>
<p> Authorization-level: </p>
<input ng-model="ctrl.authlevel"></input>
This approach works when the dynamic content is a child of the static content:
User-info-template.html
<div>
<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>
<!--Note that this dynamic piece is a child, so (as you noted)
the above approach won't work-->
<dynamic-piece></dynamic-piece>
</div>
Dynamic-piece-user.html
<p> Authorization-level: </p>
{{ctrl.authlevel}}
Dynamic-piece-admin.html
<p> Authorization-level: </p>
<input ng-model="ctrl.authlevel"></input>
I believe the ponent-based approach is flexible enough to acmodate for most situations. However, it might be overkill for some things. I (as a newbie angular developer) tend to start out using ng-ifs, and I break things down into ponents when they get too plicated. I suggest you do the same. Once you get the hang of refactoring ponents into different ponents, it's really a smooth workflow.
Use the custom directive and pass data-type (user or admin) as a separate attribute. Here is an example using custom directive where the html structure of the form changes according to the value passed in the "data-type" attribute. So the input field will be shown only for data-type='user' or you can also check if authlevel value is available and show input field accordingly.
example:
https://plnkr.co/edit/YXkYgh73Kfn94O8wvA55?p=preview