Good morning. Over the past couple of weeks, as you know, I've been working on the next version out the Evented Mind site. And as part of that project, I've built a number of packages, some of which I'll open source once I finish the site itself.
And so in this episode, what I'd like to do is show you a little bit around the user interface of the new Evented Mind. It's not the entire project, but you'll see some of it. And then more importantly, jump into the code and take a look at how I'm using the new router project controllers and classes.
So you can see on the homepage here that I have a list of episodes over on the right. And then on the left, I have a sidebar where I can put navigation content like the Home link. And down at the bottom, I have a footer and maybe some messaging.
And then if I click on one of the items, you can see on the left I have the title to free up space on the right side for the video and for content. And then I have the description and an Edit link. Then on the right, I have the actual content. And if I edit a post, I can update it. I'll update it again. Click the Save button, and be routed back to the Show page for the post.
Another interesting feature is that previously, when I reloaded this page, you would see a blank page at first. And then when the data comes off the wire, you would see it loaded. And that presents a problem in that it's a little confusing to users if they have a slow connection, because they might think that there's no content on the page.
But another problem is that if you go to a URL that doesn't exist, like bogus post, previously there was no way to know whether or not the data actually existed and so whether this was actually a good URL, because we weren't waiting on a subscription. So I'm going to show you in the new router project how we fixed that problem.
The first thing you'll notice is that it's really easy to create new routes. I just called a router.map function, and I pass in a function where I can define my routes. And you'll notice that router.map is declared outside of the Is Server Is Client blocks, and that's because the router can run on both the client and the server.
Inside my route map, I create my first route, which is called Home. And it just has the root path. And so all routes have a given name. And we can use that name in path for helpers in the HTML. For example, over on the right, you can see that I have a link, and I'm using the path for Helper saying I want this path to be the path for the Home route.
And I can also specify a controller if I want, and an action, which would be a method on that controller. And if your project is relatively small, you can put all of these options like Template and the things that we'll see in controller in a second right here directly in the route. But because Evented Mind is getting sort of large, I've split this out into separate functionality.
Down on line 9, you can see that the Post route doesn't have any options. And that's because the URL is just post and the template name is also post. And so all I have to do is say this.routepost, and the router intelligently figures out the right path and the right template to render.
Then down at the bottom, you see that I've declared a server route just to show how it works. I give it a path, just like I give my client routes, except I've provided a new option called Where and said that I really want this route to run on the server. And then I've provided a Handler function directly instead of using a controller. And inside it, I just write to the response stream, hello from the server. And what's neat about this is over in the HTML, you can see that we can use the Path For helpers for server side routes as well.
I've scrolled down a little bit so that we can look at the controllers and the rest of the code on the client and the server side. So in the server, I'm just publishing out all of my posts with a simple cursor on the post collection. And down on the client, I create a namespace for subscription so that I can access the result of media.subscribe call. And so I'm subscribing to all posts and assigning that to the post's attribute of subscriptions.
And then I'm creating an overall application controller that I want all of my child controllers to inherit from. And so that's so that I can provide a master layout, which we'll look at in a second, and then something called a Loading template and a Not Found template.
Then beneath that, you can see I have my first main controller for posts. So I've called it Post Controller, and I inherit from Application Controller. Inside the controller, I specify that I want routes that route to this controller to use the Post template. And I can provide a data attribute or data property that has one or more properties inside of it. And so you can think of this as helpers on a template.
So inside of data, I provide a post property, and I say I want to find all the posts and sort them by publish dat in descending order. Then if we scroll down just a little bit more, I have another controller called Post Controller, which also inherits from Application Controller.
And this does a lot more interesting stuff. The first thing you'll see is that I'm including some functionality from another controller called Form. And this gives me the form processing logic so that when I click the Save button, I get the values of the form, and I can send those up to the server.
The second interesting thing that it does is it waits on the post subscription to be ready before rendering. And so this way, if data hasn't come off the wire completely yet-- in other words, my post subscription is not marked as ready-- the render will automatically render the loading template. And then if data is not found, the data that gets returned from my data function, I'll show the Not Found template automatically.
So inside my data function and inside any of these actions, I have access to some properties on the controller instance-- for example, the Parameter property. And you can see that my path had a slug variable in it. And so whatever it gets passed in through the URL, we'll get pushed on to this Params object, and I can access it inside my functions here.
Then I have two actions. And I just created one controller for both of these actions as opposed to two separate controllers. Because basically, for both the Show and the Edit action, I do the same thing. I just render a different template. And so I still want to get the post. I still want to wait on the post subscription. And I want to show the Not Found template if you can't find the post and a Loading Indicator if the data is still coming off the wire. But in the Show action, I render the Post Show Template. And then the Edit function, I render the Post Edit template.
And so one of the cool things about these controllers is that I can add one or more event maps. And I can even give including controllers the opportunity to add their own event maps to this controller. And so the On Form Changed method will take the values from the form-- including all the properly handled checkboxes and radio buttons and things like that-- the original post, which was our data context, and then the form element itself so that I can reset it if I want to or do anything else with it after I've submitted the form. And this is where I can do something like a post update and set the value as equal to what comes off the form.
And then in the Callback function, if everything goes well, what I can do is call the Go method of router and just pass the name of a route and a Parameters object. And so this will automatically route me to the posts and then the slug that comes off of the Post object itself.
So next, let me jump over to the HTML and show you some neat features there. So one of the first things you'll notice is that the Body tag is completely empty. The router will automatically render into the body. But you can also specify a more precise location for the router to render if you'd like.
And then down below, I have a template called Layout. And this is the master layout, a similar concept to layouts in Rails or in Ember if you're used to that. And so inside of the layout, I can have various blocks of HTML that I want on all my templates, but I can also yield content to the child template. And so I'll show how this works from the child template in a second. But what you're seeing here is that I want to yield the page class from whatever the child template says that it should be. And so this lets me set a page class for the entire page depending upon which child template is rendered.
And then I have some standard navigation that I'm going to show on all the pages. And then I'll give an opportunity for the child template to provide some sidebar content by yielding the sidebar. And so these are called Named Yields. Similarly, down at the bottom, I can yield some content for the footer. And then the final content is just an unnamed yield or the main yield or an anonymous yield. And this yields the main content.
So I've scrolled down to the Post Show template to give you a sense of how this works hand in hand with layouts. You can see that the first line here is a Content For expression. And I'm saying that I want this content to be for the page class and the template, and its value is just going to be post. And so this lets me style the overall page.
And then down below that, I've got a Content For block where I provide some HTML that I want to put into that part of the layout. And so that's why the title, the description, and the Edit Post link appears over on the left instead of in the main body of the article. And then down at the bottom, I just yield the main content.
So this was just a quick tour of the new router project and to give you a sense of the new design for Evented Mind. And hopefully you enjoyed it. And in upcoming episodes, I'll cover the technologies in more detail.
Also, if you're interested in participating in the router project or in any of the technologies that you've seen in these episodes, let me know. I'd love to have contributors. And to do that, you can just send me an email at firstname.lastname@example.org. And I would love to have you. So have a great weekend.