JavaScript Templating
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeHere's the goal: use a JavaScript template to render a new RepLog <tr>
after we successfully submit the form. The first step is to, well, create the template - a big string with a mix of HTML and dynamic code. If you look at the Underscore.js docs, you'll see how their templates are supposed to look.
Now, we don't want to actually put our templates right inside JavaScript like they show, that would get messy fast. Instead, one great method is to add a new script
tag with a special type="text/template"
attribute. Give this an id, like js-rep-log-row-template
, so we can find it later:
// ... lines 1 - 54 | |
{% block javascripts %} | |
// ... lines 56 - 66 | |
<script type="text/template" id="js-rep-log-row-template"> | |
// ... lines 68 - 80 | |
</script> | |
{% endblock %} |
Tip
The text/template
part doesn't do anything special at all: it's just a standard
to indicate that what's inside is not actually JavaScript, but something else.
This is one of the few places where I use ids in my code. Inside, we basically want to duplicate the _repRow.html.twig
template, but update it to be written for Underscore.js.
So temporarily, we are totally going to have duplication between our Twig, server-side template and our Underscore.js, client-side template. Copy all the <tr>
code, then paste it into the new script
tag.
Now, update things to use the Underscore.js templating format. So, <%= totalWeightLifted %>
:
// ... lines 1 - 54 | |
{% block javascripts %} | |
// ... lines 56 - 66 | |
<script type="text/template" id="js-rep-log-row-template"> | |
// ... lines 69 - 79 | |
</script> | |
{% endblock %} |
This is the print syntax, and I'm using a totalWeightLifted
variable because eventually we're going to pass these keys to the template as variables: totalWeightLifted
, reps
, id
, itemLabel
and links
.
Do the same thing to print out itemLabel
. Keep going: the next line will be reps
. And then use totalWeightLifted
again... but make sure you use the right syntax!
// ... lines 1 - 54 | |
{% block javascripts %} | |
// ... lines 56 - 66 | |
<script type="text/template" id="js-rep-log-row-template"> | |
// ... lines 72 - 79 | |
</script> | |
{% endblock %} |
But what about this data-url
? We can't use the Twig path
function anymore. But we can use this links._self
key! That's supposed to be the link to where we can GET info about this RepLog, but because our API is well-built, it's also the URL to use for a DELETE request.
Great! Print out <%= links._self %>
:
// ... lines 1 - 54 | |
{% block javascripts %} | |
// ... lines 56 - 66 | |
<script type="text/template" id="js-rep-log-row-template"> | |
</script> | |
{% endblock %} |
Rendering the Template
Gosh, that's a nice template. Let's go use it! Find our _addRow()
function. First, find the template text: $('#js-rep-log-row-template').html()
:
// ... lines 1 - 2 | |
(function(window, $) { | |
// ... lines 4 - 24 | |
$.extend(window.RepLogApp.prototype, { | |
// ... lines 26 - 121 | |
_addRow: function(repLog) { | |
var tplText = $('#js-rep-log-row-template').html(); | |
// ... lines 124 - 129 | |
} | |
}); | |
// ... lines 132 - 149 | |
})(window, jQuery); |
Done! Our script
tag trick is an easy way to store a template, but we could have also loaded it via AJAX. Winning!
Next, create a template object: var tpl = _.template(tplText)
:
// ... lines 1 - 2 | |
(function(window, $) { | |
// ... lines 4 - 24 | |
$.extend(window.RepLogApp.prototype, { | |
// ... lines 26 - 121 | |
_addRow: function(repLog) { | |
var tplText = $('#js-rep-log-row-template').html(); | |
var tpl = _.template(tplText); | |
// ... lines 125 - 129 | |
} | |
}); | |
// ... lines 132 - 149 | |
})(window, jQuery); |
That doesn't render the template, it just prepares it. Oh, and like before, my editor doesn't know what _
is... so I'll switch back to base.html.twig
, press option
+enter
or alt
+enter
, and download that library. Much happier!
To finally render the template, add var html = tpl(repLog)
, where repLog
is an array of all of the variables that should be available in the template:
// ... lines 1 - 2 | |
(function(window, $) { | |
// ... lines 4 - 24 | |
$.extend(window.RepLogApp.prototype, { | |
// ... lines 26 - 121 | |
_addRow: function(repLog) { | |
var tplText = $('#js-rep-log-row-template').html(); | |
var tpl = _.template(tplText); | |
var html = tpl(repLog); | |
// ... lines 127 - 129 | |
} | |
}); | |
// ... lines 132 - 149 | |
})(window, jQuery); |
Finally, celebrate by adding the new markup to the table: this.$wrapper.find('tbody')
and then .append($.parseHTML(html))
:
// ... lines 1 - 2 | |
(function(window, $) { | |
// ... lines 4 - 24 | |
$.extend(window.RepLogApp.prototype, { | |
// ... lines 26 - 121 | |
_addRow: function(repLog) { | |
var tplText = $('#js-rep-log-row-template').html(); | |
var tpl = _.template(tplText); | |
var html = tpl(repLog); | |
this.$wrapper.find('tbody').append($.parseHTML(html)); | |
// ... lines 128 - 129 | |
} | |
}); | |
// ... lines 132 - 149 | |
})(window, jQuery); |
The $.parseHTML()
function turns raw HTML into a jQuery object.
And since we have a new row, we also need to update the total weight. Easy! this.updateTotalWeightLifted()
:
// ... lines 1 - 2 | |
(function(window, $) { | |
// ... lines 4 - 24 | |
$.extend(window.RepLogApp.prototype, { | |
// ... lines 26 - 121 | |
_addRow: function(repLog) { | |
var tplText = $('#js-rep-log-row-template').html(); | |
var tpl = _.template(tplText); | |
var html = tpl(repLog); | |
this.$wrapper.find('tbody').append($.parseHTML(html)); | |
this.updateTotalWeightLifted(); | |
} | |
}); | |
// ... lines 132 - 149 | |
})(window, jQuery); |
Deep breath. Let's give this a shot. Refresh the page. I think we should lift our coffee cup ten times to stay in shape. Bah, error! Oh, that was Ryan being lazy: our endpoint returns a links
key, not link
. Let's fix that:
// ... lines 1 - 54 | |
{% block javascripts %} | |
// ... lines 56 - 66 | |
<script type="text/template" id="js-rep-log-row-template"> | |
// ... lines 69 - 71 | |
// ... line 77 | |
</script> | |
{% endblock %} |
Ok, refresh and try it gain! This time, let's lift our coffee cup 20 times! It's alive!!!
If you watch closely, it's even updating the total weight at the bottom.
I love it! Except for the massive duplication: it's a real bummer to have the row template in two places. Let me show you one way to fix this.
I have this error when I send the request
`
VM40780:8 Uncaught ReferenceError: trans is not defined