2

I’m writing a web application in Perl. It has a form containing contact information, and is currently laid out something like this:

$form_htm = <<EOF
<input value="$in{firstname}" name="firstname">
<input value="$in{surname}" name="surname">
...etc...
EOF

Later on I print $form_htm (amongst other things) to display the webpage.

The above is working for me, and the fields are interpolated as expected. However, I’d like different organisations who use this application to be able to have a different layout for their fields. Some fields won't apply to some organisations, and some organisations will want different sizes (on the web page) for fields than others. So, I thought I’d store the HTML for the form in a database (different versions in different records for different organisations). But because the HTML is not stored in the script, the fields (like $in{firstname}) are not interpolated, so the variable names themselves are ending up on the web page, (which is not quite the look I’m after).

Any recommendations on how I should get these variables to be evaluated in this context? (First thing that came to mind is eval(), but that seems aimed at evaluating expressions, not variables mixed in with a lot of other text.)

Or can you suggest a better approach to allow for different form layouts for different organisations?

Edit: I have also posted this question on Perl Monks, but would like to get other opinions.

Terry
  • 176
  • 9
  • 2
    What you want is called a template system. [Template::Toolkit](http://template-toolkit.org/) is one of them, but there are many others. Look at http://stackoverflow.com/q/14236318/1331451 for example. – simbabque Nov 03 '16 at 21:37
  • 1
    Also, you're not telling us what kind of technologies you are using. Is this a CGI application with CGI.pm, or is it maybe something more modern like a Dancer app? The more modern approaches work very well with template engines. – simbabque Nov 03 '16 at 21:38
  • 1
    http://www.perlmonks.org/?node_id=1175188 – choroba Nov 03 '16 at 21:57
  • Thanks for all that, @simbabque. My application uses CGI.pm and some getcgivars() sub that I picked up years ago. Probably should be just using one of those, but haven't tidied that up yet. – Terry Nov 03 '16 at 23:53
  • Thanks @choroba, yes I posted this question elsewhere and I have now added an update to my original post to reflect that. – Terry Nov 03 '16 at 23:53
  • Side note: please always declare cross-posting. It gives readers a chance to see if you are still looking for an answer, and/or to see if what they would post has already been said elsewhere. If you can link from there to here, that is good too. – halfer Nov 04 '16 at 12:44

1 Answers1

3

Here's some bad ways and some good ways to handle this.

The Bad Ways

Stick HTML in your code.

my $form_htm = <<EOF
<input value="$in{firstname}" name="firstname">
<input value="$in{surname}" name="surname">
...etc...
EOF

This was a very common thing to do. It has major problems. First, it welds together the logic of the program with what is displayed. There's either only one way to display it, or your code gets increasingly complicated trying to allow multiple ways to display it. You're running into this problem.

Second, it means in order to change how it looks you need a developer involved. You can't have separate UX people working on the HTML and CSS layout, they also need to know Perl or get a Perl developer involved. This makes it very expensive and slow to make otherwise simple HTML changes.

Third, with the code to generate the HTML scattered around it makes it difficult to figure out how each page comes to be. If you want to change a form in a page, you have to hunt around in the code to find how it's generated.

Fourth, it's slow. Every single page has to be generated by the server.

Stick code in your HTML.

<%class>
has 'amount';
has 'name';
</%class>

Dear <% $.name %>,

    We are pleased to inform you that you have won $<% sprintf("%.2f", $.amount) %>!

Sincerely,
The Lottery Commission

<%init>
die "amount must be a positive value!" unless $.amount > 0;
</%init>

This what PHP and Mason do. It's better than sticking HTML in your code, but it still has major problems. It can be used well, but it encourages you to put WAY too much logic into the templates. This scatters the logic around making it hard to understand what's going on. Code in the templates is hard to document and test. It also mixes code into the templates which again blurs the line between UX person and developer.

The Good Ways

MVC: Model-View-Controller

Model-View-Controller provides a clear delineation between the data and logic (the "model") and how it's displayed (the "view"). These are connected with a bit of code to get data from the model and send it off to the view as variables (the "controller").

This is one of the most successful ways of putting together a web site. Ruby On Rails and Catalyst are built around this.

I'd recommend Dancer. It's much smaller and easier to understand than Catalyst. Since it provides less it's easier to adapt an existing program to use it. It provides the view and controller, the model is up to you.

There's two good ways to put together the view.

An HTML Template.

The usual way is to put your entire HTML page in a template, much like we saw before, but instead of it having its own code for getting data it's instead passed data to use. It has a limited language for manipulating that data.

In Perl this is generally Template Toolkit.

Dear [% name %],

It has come to our attention that your account is in 
arrears to the sum of [% debt %].

Please settle your account before [% deadline %] or we 
will be forced to revoke your Licence to Thrill.

The Management.

Now UX people can manage HTML templates on their own. They still have to learn a template language, but it's at least a well documented one. The limited language discourages putting too much code in the templates creating a sort of firewall between the View and the rest of the code.

But all the work to format the template still must be done on the server side. And UX people still have to learn a special template language. Which brings us to the best way.

Pure HTML, CSS, and Javascript.

In this mode, instead of processing a template on the server side and inserting variables into it, the HTML has Javascript which requests the data it needs and displays it as it sees fit. This has great advantages.

It completely divorces the view from the rest of the code. This gives the UX folks great control over how the application behaves. They decide what data each page needs. They decide how it's manipulated and displayed. Developers can focus on providing the data.

The other advantage is using Javascript. Javascript has become the defacto programming language that web designers must know. UX folks don't need to learn a special template language, they use the same HTML, CSS, and Javascript they've always used.

Finally, this means the bulk of the formatting work is done on the client side reducing the load on your server.

The main disadvantage is this requires a major re-architecture of an existing system. Instead of producing HTML pages, your server code now produces JSON for the Javascript to manipulate via REST requests.

Hybrid Template + Javascript

A good hybrid for an existing system is to use templates, but instead of using a special template language, use Javascript. Simple scalar variables like names and amounts can still be provided as template variables, but larger units like lists and hashes are injected into the template as JSON.

For example, this template...

var people = [% people %]

would be provided with people = encode_json(\@people)' and it might expand to:

var people = ["Jack", "Jill", "Jane", "Joe"]

Then the UX person can do whatever they want with this Javascript people array.

On the downside, this still gives the developers too much control over how the site works, because they decide what templates are used and what data they're given, and it still means the templates have to be expanded server side.

On the upside it lets UX people manipulate that data in ways they're comfortable with, it enforces a clear separation between the view and the rest of the code, and you can convert existing code to use these sorts of templates a piece at a time.

halfer
  • 19,824
  • 17
  • 99
  • 186
Schwern
  • 153,029
  • 25
  • 195
  • 336
  • 1
    An impressive response, Schwern! Thank you! My situation is, I'm the only person who works on this project, and I don't really know JavaScript. What option would you suggest for quickly & easily getting this working in my form? I'm not using Dancer or similar. And I have things like: – Terry Nov 04 '16 at 01:48
  • 2
    @Terry I recommend you learn Javascript. It's ubiquitous now, it will be very useful. It's not so different from Perl, the basic data structures will be familiar. There's [tons of tutorials out there](http://www.w3schools.com/js/default.asp). As for "I'm the only person who works on this project" it's important to leave open the possibility that other people can work on the project. Else you will *always* be the only person who works on that project. – Schwern Nov 04 '16 at 01:51
  • @Terry The quickest path forward is to replace your inline HTML with [Template Toolkit](http://template-toolkit.org/). Then look at replacing pieces of your routing code (ie. "the user asked for search.html, what do I do?") with something like Dancer. – Schwern Nov 04 '16 at 01:53
  • Can Template Toolkit handle this kind of complexity: The "$dis{}" hash contains "disabled" if the field is disabled, otherwise "". – Terry Nov 04 '16 at 01:56
  • @Terry Yes, that's simple variable expansion. You'd probably invert the data structure so pass in a hash like `my $vars = { firstname => { class => "foo", value => "bar", is_disabled => 0 } }; $tt->process('thing.html', $vars);` and the template would be something like ``. I've deliberately removed all traces of HTML from the data provided. That's why `firstname.is_disabled` is a boolean rather than a string "disabled". – Schwern Nov 04 '16 at 02:17
  • OK - thanks again Schwern. Is that the kind of syntax for use with Template::Toolkit, or what? – Terry Nov 04 '16 at 03:12
  • @Terry Yes. You should have a look at their docs. I linked to their web site in the answer. – Schwern Nov 04 '16 at 06:21