• Meteor
  • Tutorial

Rendering Components

We've already seen how materialization works, which is the process of taking some inputs and converting it into reactive DOM elements. In this video we're going to look at the render method of component and how the rendering process works with components specifically. The component rendering model has evolved quite a bit, even in just the past couple of weeks. And so in these videos, I'll show you which commit I'm working off of, and that'll give you a sense of how up to date or out of date the particular video is.

So in this case, I'm working off of this commit, 7F855B1. And that's what we'll be looking at for the render method. OK, we have the standard Hello World! application here. And if I right click and take a look at the page source and scroll down, we can take a look at the template code like we've seen before.

I wanted to show you this code because it's an example of how we call UI materialize with a component as the first parameter. In this case, the component is the UI body. We'll create our own component and pass it into UI materialize. And the goal of this video is to see what happens.

Inside the UI package in the file render.js, I'm taking a look at the materialize function. And if you look down in this if statement here, you can see what happens if the first parameter is of type component. And the way that this function determines that is by seeing whether the passed in object has an instantiate function on it. If it does, it calls UI render and passes the component as a first parameter and any parent component, like document.body, as a second.

And then whatever it gets back, it calls insert and passes in the DOM of the component instance as the first parameter. So let's go next and take a look at what UI render does. The UI render method takes a kind as a first parameter. And so this would be like our template, our hello template, or another custom component that we might create. The first thing this function does is checks to make sure that we haven't already initialized the component. And if we have, then it'll throw an error.

Otherwise, it'll call the instantiate method on the kind, and that creates a new instance object. Next-- and this is probably the most important line-- we get the content. And the way that we get the content is by checking to see whether the component has a render method, and if it does, by calling it. So whatever gets returned from calling the render method, we then pass into this materialize function like we've been doing already. And that gets passed in as the first parameter.

Now we just glossed over this line here because all it's doing is creating a new DOM range and assigning it to the DOM property of the component instance. And then finally down here, the remove method gets assigned to the instance. And this allows us to destroy the component. So the key thing to note here is that this content can be anything that we would pass in as a parameter to the UI materialize function.

If you've already seen the UI materialize video, you know that we can pass a number of different types, including a component, a function, any string or primitive type, or HTML tags. So we can apply that knowledge in creating our own render method on a component. So let's go do that now.

Really quick, before we create our own component, I wanted to one more time show you the compiled result of our template. In this case, it's the hello template. And you can see over in the right that this template gets compiled into a component that has this render function. And you can see the render function returns an array of objects of various types, like the results of calling HTML raw, then an actual function that calls into the space bars runtime, and then another HTML raw corresponding to the various parts of the template.

So this is an example of what the space bars compiler would produce for us and what a compiled render function would look like. So now let's just write our own. I've gotten rid of the stock JavaScript and created a new component called my component that extends the base UI component object. And we'll create a render method on this. And for now let's just return a primitive, a string. And we'll say "hello world." And we'll print to the console that the render method has been called.

And then if I go over into the browser, let's call the UI materialize method, and we'll pass in my component as the first parameter and then the document body as the second. Great. So you can see in the browser that the "hello world" text gets appended to the document object model, to the body, and our render method called console log string gets printed out.

Now, instead of returning a string, why don't we do something a little bit more complicated. If you recall from the UI materialize episode, we can pass in functions. And that automatically makes them reactive. So let's return from our render function another function. And what we'll do is get a value from session and just return the value.

And if there's no value, why don't we set a default of some default value. And then usually it just has a good pattern. I'll create a variable called self and assign it to this so that we have reference to it inside of this function. And to make things a little less confusing, let me get rid of the Hello World template here so we can just work with our own. Great.

So if I come over into the browser, let me call materialize on my component. And we'll materialize it right into the document body. Great. So now I have some default value. And because I returned a function from the render function, if I set a different value in session, the DOM element is rerendered.

Next I could also return another component. So that's a pretty cool idea. So for example, I might return a template. So template.hello doesn't exist because we got rid of it. Let's just go back and recreate it quickly. And so if I jump over to the browser and we call ui.materialize again, you can see that the hello template gets rendered into the page. And that's because we returned the template hello from our render function in our custom component.

We can also return HTML tags. We haven't talked about those in detail yet, but it's pretty easy to figure out. I can just return, say, a new HTML element. Let's just make it an anchor tag. And I can pass some attributes and then some content. And so maybe instead of passing no attributes, in this case I'll give it an href. And then we'll materialize the component again.

And now you can see that we have an anchor. And if I inspect the element, you can see that it's an anchor tag, and it has an href attribute. So this is a really flexible and powerful system for creating user interface components and rendering them. And recapping what the UI render method does, if we pass in a component, it first initializes the component by calling the instantiate method and then grabs the content of the component by calling its render method. And then it passes the results of that right into the materialize function like we're used to, and then returns the instance to the caller.

So most of the time we won't create components from scratch. We'll just use the regular templating system to create them. But in the future, you may want to create a custom component to do something that the regular template engine doesn't allow you to do with some custom JavaScript logic. And so this should give you a better sense of how the rendering process works with custom components.