Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Colin Oakley
Colin Oakley

Posted on • Edited on

     

templating - from html to macros

Whenever I am pairing with other developers we always seem to hit a similar topic; abstraction.

There is ever a clear-cut answer to that question, as it requires the context of the code and the state of the application.

Overly abstracted code can make changing it difficult and hard to comprehend whereas no abstraction can lead to bloat and repetition.

Below is an example of atext input form GOV.UK Elements.

The examples below usenunjucks.

<divclass="form-group"><labelclass="form-label"for="ni-number">        National Insurance number<spanclass="form-hint">            It's on your National Insurance card, benefit letter, payslip or P60.</span></label><inputclass="form-control"id="ni-number"type="text"name="ni-number"></div>
Enter fullscreen modeExit fullscreen mode

Form elements always seem a very clear example of where we should do an abstraction - they have set structure and usually are used multiple times on a site.

When we look at making a reusable form element, we do it with the composition of the makeup of our HTML in mind.

There are a couple ways we could break this out, the first would be so that each of the parts of the code would have its own macro - this would mean each part would be usable within other form elements.

This would look like this:

  • form-group wrapper
  • label
  • input

Using nunjucks we'd end up with something like this:

{%macroinput(id,name,value='')%}<inputclass="form-control"id="{{id}}"name="{{name}}"type="text"value="{{value}}">{%endmacro%}{%macrolabel(for,label,hint)%}<labelclass="form-label"for="{{for}}">{{label}}<spanclass="form-hint">{{hint}}</span></label>{%endmacro%}
Enter fullscreen modeExit fullscreen mode

This would allow us to compose our UI like this,

{%from"forms.html"importfield,label%}<divclass="form-group">{{label('ni-number','National Insurance number','It\'s on your National Insurance card, bene...')}}{{field('ni-number','ni-number',niNumberValue)}}</div>
Enter fullscreen modeExit fullscreen mode

This allows label and field to be composed to produce other form elements, they could also be wrapped in another function to produce a form element, I'll come to this later. We could also look at moving the form-group div to a macro.

This could also be done as a single macro.

{%macrotextInput(id,name,label,hint,value)%}<divclass="form-group"><labelclass="form-label"for="{{id}}">{{label}}<spanclass="form-hint">{{hint}}</span></label><inputclass="form-control"id="{{id}}"name="{{name}}"type="text"value="{{value}}"></div>{%endmacro%}
Enter fullscreen modeExit fullscreen mode

This encapsulates both the previous macros in one call, this makes it less composable but also has the full element.

The second method will quickly become bloated if we need to add on additional options for example styling, input type or additional HTML properties.

We could also make a hybrid of the two methods, though as we start to do multiple layers we start to make our code less readable and we need to be more mindful when we make changes.

{%macrotextInput(id,name,label,hint,value)%}<divclass="form-group">{{label(id,label,hint)}}<inputclass="form-control"id="{{id}}"name="{{name}}"type="text"value="{{value}}"></div>{%endmacro%}
Enter fullscreen modeExit fullscreen mode

This in hindsight feels like the right balance, textInput is encapsulated but the label can be re-used across different macros.

My final macro has the following inputs

NameDescription
namesets the name of the input
idsets the id of the input, and the for of the label
labelsets the text of the label
hintsets hint text within the label
valuesets the value of the input
errorsets the error message
maxlengthsets the max length of the input
classesobject used for styling elements

This has various states, including optional fields and error states. This after a couple of iterations seemed like the right level of abstraction in the application context, though I think there is more to do with moving the label to its own macro and being called within the element.

{%macrotextInput(name,id,label,hint,value,error,maxlength,classes)%}{%ifnotid%}{%setid="input-"+name%}{%endif%}<divid="{{id}}-form"class="form-group{%iferror%} form-group-error{%endif%}"><labelfor="{{id}}"><spanclass="{%ifclasses.label%}{{classes.label}}{%else%}form-label-bold{%endif%}">{{label}}</span>{%ifhint%}<spanclass="form-hint">{{hint}}</span>{%endif%}{%iferror%}<spanclass="error-message">{{error}}</span>{%endif%}</label><inputclass="form-control{%ifclasses.input%}{{classes.input}}{%endif%}"id="{{id}}"{%ifmaxlength%}maxlength="{{maxlength}}"{%endif%}name="{{name}}"type="text"value="{{value}}"></div>
Enter fullscreen modeExit fullscreen mode

Whenever you make a deliberate decision to try and make re-useable code there is always going to be a trade-off in functionality, readability, and maintenance.

Keeping things self-contained gives the ability to refactor it later with minimal change.

Feel free to add how you'd do this, or message me ontwitter

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Front-end developer in UK Government. community things frontendne.co.uk.
  • Location
    Newcastle, England.
  • Joined

More fromColin Oakley

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp