Skip to content

Instantly share code, notes, and snippets.

@andrewfritz86
Last active August 29, 2015 14:07
Show Gist options
  • Save andrewfritz86/ffcfaa174b30a0e0e746 to your computer and use it in GitHub Desktop.
Save andrewfritz86/ffcfaa174b30a0e0e746 to your computer and use it in GitHub Desktop.
Using JS templates to dynamically render HTML

First, I am going to take a look at the constructor function that we will use to build StudentView objects. Here is the function

function StudentView(model){
  this.$el = $('<p>');
  this.model = model;
}

We define two attributes on the object from the constructor function. First, we define the element ($el) attribute, and assign it to a jQuery object that will create a new paragraph element for us.

Second, we define a 'model' attribute for the object. This attribute comes from the parameters, as we will pass each StudentView instance a javascript object that serves as our model.

From here, I think it helps to jump ahead a bit and look at one of the StudentView instances we'll create. Here is an example, featuring the lovely and talented Dewey.

var v1 = new StudentView({name: "Dewey", age: 36, color: "blue"});

We can see here that we're creating a new StudentView instance, pointing the variable v1 at it, and passing it a simple JS object as our 'model' argument. This concept was confusing for me, since the model is an attribute of the StudentView object, yet we can call 'name', 'age' and 'color' getter methods on the model attribute of that object. Cue Jared inserting a mindexplode.gif.

Now let's look at the HTML that we'll be using to render each StudentView instance.

 <script type="text/template" id="student-template">
    <div class="student">
      <img src="">
      <h1><%= name %></h1>
      <p>
      <% if(age < 21){ %>
        young<%= age %>
      <% } else { %>
      </p>
      old <%= age %>
      <% } %>
    </div>
  </script>

Here we see the triumphant return of our old friends, the crying clown hats. In this case, rather than indicating embedded ruby, we are letting the browser know to interpret anything contained within those hats as Javascript. Since we know that the content between the hats will by dynamic, it makes sense that the words we see here are simply attributes of the models we are passing to our StudentView instances.

Now let's move back to our script and check out the prototype for our StudentView object. This is where things start getting interesting.

StudentView.prototype = {
  template: _.template($("#student-template").html()),
 
 
  render: function(name, age){
    var temp = this.template(this.model);
    this.$el.html(temp);
 
  },
  // attach even listeners
  init: function(){
    this.render();
    $("#students").append(this.$el);
    this.$el.on("click", this, this.changeColor);
  },
  changeColor: function(e){
    console.log(this);
    var view = e.data;
    $(this).css({background: view.model.color});
  }
}

We can see here that we've defined four methods for our StudentView objects. These are defined on the prototype because we'll want all instances of the object to have access to these methods. Here is my (likely incorrect) interpretation of each of these methods.

###template

  template: _.template($("#student-template").html()),

I think the template method is pretty confusing. There is a lot going on here, and it's only one line. One could call it deceiptively simple.

First, we are using the underscore library to call .template on the underscore object. What is the point of this?

.template is passed a string of HTML. We grab the string we want by grabbing a jQuery selector for the div in question, and then calling .html on that div, resulting in a big string of HTML that is literally the contents of that div. Then we pass this to the template function. And what does it give us back? a FUNCTION. This is key for dumb people like yours truly. When we call our .template method on a StudentView object, the result is a function that expects values for the keys we've defined in our string interpolation in our HTML (name, age, color). For rendering text via interpolation, name and age are the only attributes that are needed right now. Basically, when we defined the template method, we created a function expecting a bunch of key/value pairs, which is exactly what a javascript object is, which is exactly what the model is that we pass to each instance of the StudentView object. Or maybe not, what the hell do I know?

###render Wow, this is getting long. Let's move on to the render method.

  render: function(){
    var temp = this.template(this.model);
    this.$el.html(temp);

Right. First, we assign the results of calling the template function on the model attribute of our object in question (this), to a variable we call temp. Remember, template is a function expecting some key value pairs, and what does it return? A nice string of HTML for us. Hooray!

Next, we use our $el attribute getter method to grab a jQuery object (remember, it returns a new p element), and via some bitchin' (shout out to Evan) method-chaining, we can assign the HTML for that paragraph to our temp variable, which we all remember is a nice string of HTML that our lovely template method spat out for us. Right? Right.

#intermission Here is a picture of Nick Cave a long time ago, (older than Phil, even), looking exactly like Jared did on presentation day when he changed the way we all look at how we live our lives and the goals we set for ourselves.

jrad

###init

  init: function(){
    this.render();
    $("#students").append(this.$el);
    this.$el.on("click", this, this.changeColor);
  },

Moving on, let's remember that we've now created a new paragraph element, but we haven't appended it to the DOM yet, and we most certainly haven't attached any behavior to it yet. What a shame! First, we call .render on the object to make sure it's all ready to be appended if the paragraph element, etc, haven't been created yet.

Next, we use jQuery to reach down into the DOM, grab the div with the 'students' id, and append the 'el' attribute of our object (aka the paragraph we created when we called .render) to it.

Last, we use jQuery's .on method to attach a a click event to our objects 'el' attribute (again, the paragraph we rendered). We'll call the changeColor method on our object when we click, so I suppose we should define that function first. (Also, notice that we pass a selector argument to .on, so there are 3 arguments instead of 2. Ask Larkin if you are confused, he will help you.)

###changeColor

  changeColor: function(e){
    console.log(this);
    var view = e.data;
    $(this).css({background: view.model.color});
  }

Lastly, let's look at our changeColor method. First, we log 'this' to the console, just to make sure that the function is being called when we want it.

Next, create the variable view, and point it at the result of calling the data method on e. What is e you ask? (you probably didn't ask, because you're not an idiot like I am). E is the 'event' itself, in this case our mouseclick. E is an object that we can call methods on. The data method returns our StudentView object.

Finally, it's time to method-chain ourselves into oblivion and change the background color of the element.

We use jQuery to grab 'this'. Remember, 'this' is no longer simply our StudentView object! It is now simply our .el attribute, aka our new paragraph. Why? Because when we called the .on method in our init function, we called it on the 'el' attribute or our StudentView object. We 'went down a layer', and so now 'this' refers to our 'el' attribute. BOOM

Anyway, we call the .css method on our object, and simply change the background to the color attribute of our model, which we all remember(joke) is an attribute of our object. BOOM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment