Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Meteor route


May 09, 2021 Meteor


Table of contents


Routing

Now that we've created a post list page (which was ultimately submitted by the user), we'll also need to add a separate post page to provide users with comments on the corresponding posts.

We want to be able to access each individual post page via a fixed link, in the form of http://myapp.com/posts/xyz http://myapp.com/posts/xyz xyz is _id identifier), which is unique for each post.

This means that we need some routes to see what the path is in the browser's address bar and display the correct content accordingly.

Add an Iron Router package

Iron Router is a routing package developed specifically for Meteor Apps.

Not only does it help routes (set paths), it also helps filter (assign jumps to those paths), and even manage subscriptions that control what data the path can access. ( Note: Iron Router was developed by Tom Coleman, one of the authors of this book, Discover Meteor.) )

First, let's install this package from Atmosphere:

meteor add iron:router

Terminal terminal

This command is to download and install the Iron Router package into our app so we can use it. Note that you may need to restart your Meteor app before you can use the package smoothly (you can stop the ctrl + c and meteor to start it again).

The vocabulary of the router

In this chapter we will be exposed to the different functions of many routers. I f you have some practical experience with rails-like frameworks, you may already be familiar with most of these lexical concepts. But if not, here's a quick vocabulary for you to learn about:

  • Route rules: Routing rules are the basic elements of a route. Its job is to tell the app what to do and what to return when they access a URL in the app.
  • Path: The path is the URL to access the app. It can be static /terms_of_service or dynamic /posts/xyz or even contain query /search?keyword=meteor
  • Catalog: Part of the path, separated by a forward slash /
  • Hooks: Hooks can be executed before, after, or even while the route is in progress. A typical example is to detect whether a user has this permission before displaying a page.
  • Filter: A filter is similar to Hooks, a global filter defined for one or more routing rules.
  • Route Template: The Meteor template that each routing rule points to. If you don't specify, the router will look for a template with the same name by default.
  • Layout: You can imagine the layout of a digital photo frame. They contain all the HTML code placed in the current template, and they do not change even if the template changes.
  • Controller: Sometimes you'll find that many of your templates are reusing some parameters. In order not to duplicate your code, you can have these routing rules inherit a Routing Controller to contain all the routing logic.

For more information about Iron Router, check out the full documentation above GitHub .

Route: Map the URL to the template

So far, we've used some fixed templates ( such as . {{> postsList}} to lay out for us. So while the content of our app can be changed, the basic structure of the page has changed: a header, which is below the list of posts.

Iron Router is responsible for what <body> HTML and body tags, and let's get rid of this shackle. So instead of defining the contents of the label ourselves, we'll specify the router to a layout {{> yield}} that contains the label.

This {{> yield}} automatically render the appropriate template corresponding to the current line (from now on, we will specify this particular template called "route templates"):

Meteor route Layouts and templates

We'll start building our {{> yield}} and adding the hashtags. First, main.html from the main.html <body> put its contents in their common layout.html client/templates/application folder).

main.html and cut the content and it should look like this:

<head>
  <title>Microscope</title>
</head>

client/main.html

The newly created layout.html will now contain the app's outer layout:

<template name="layout">
  <div class="container">
    <header class="navbar navbar-default" role="navigation">
      <div class="navbar-header">
        <a class="navbar-brand" href="/">Microscope</a>
      </div>
    </header>
    <div id="main" class="row-fluid">
      {{> yield}}
    </div>
  </div>
</template>

client/templates/application/layout.html

You'll notice that yield replaced the postsList helper.

When you're done, our browser tab displays Iron Router's default help page. This is because we haven't told the / do with the / URL, so it just renders an empty template.

Next, we can restore the previous root / map to postsList template. Then we create a /lib the root and create router.js it:

Router.configure({
  layoutTemplate: 'layout'
});

Router.route('/', {name: 'postsList'});

lib/router.js

We have accomplished two important things. First, we've told the router to use the layout template we layout as the default layout for all routes.

Second, we have defined a routing postsList and mapped to /

/lib folder

All the files you put in the /lib folder make sure they are loaded first (perhaps except for the smart package) while your app is running. This is a good place to place auxiliary code that you need to be ready to use.

Note, however, that because the /lib /client /server this means that its code will exist on both the client and the server.

The name of the routing rule

Here we first clear some ambiguity. W e have a routing rule called postsList and we also have a template postsList What's going on here?

By default, Iron Router specifies a template with the same name for this routing rule. I f the path parameter is path specified, it also specifies a path with the same name based on the name of the routing rule. For example, in the settings above, if we path parameter, /postsList automatically get the postList template.

You may wonder why we need to start with routing rules. T his is because some of Iron Router's features require routing rules to generate link information for the app. One of the most common is {{pathFor}} Spacebars helper, which needs to return the URL path of the routing rule.

We want the home page to link to the post list page, so in addition to / static / URL, we can also use Spacebars helper. Although they work the same, this gives us more flexibility, and if we change the mapping path of the routing rules, the helper can still output the correct URL.

<header class="navbar navbar-default" role="navigation">
  <div class="navbar-header">
    <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a>
  </div>
</header>

//...

client/templates/application/layout.html

Very basic routes

Wait for the data

If you're deploying the current version of the app (or start using the link above), you'll notice that the list will be empty for a while until all posts appear completely. This is because the first time you load a page, you don't have posts is complete, when you've crawled the post's data from the server, for the post to appear on the page.

This should have a better user experience, such as providing some visual feedback to let the user know that the data is being read so that the user can continue to wait.

Fortunately, Iron Router gave us an easy way to implement it. Let's put the subscription on the return of waitOn

Let's posts subscription from .js main.js the routing file:

Router.configure({
  layoutTemplate: 'layout',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.route('/', {name: 'postsList'});

lib/router.js

We're talking about every route to the site (we only have one now, but we'll add more right away!). We've all subscribed posts subscription.

The key difference between this and what we did main.js which should now be empty and removable, is that Iron Router can now tell when the route is ready -- that is, when the route gets the data it needs to render.

Get A Load Of This

If we just show an empty template, we know that postsList route is ready and can't do anything. Fortunately, Iron Router brings its own method of slowing down the display of the template, displaying a loding load template before the route call template is ready:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.route('/', {name: 'postsList'});

lib/router.js

Note that because we have a waitOn level, this only happens once the user first accesses your app. After that, the data has been loaded into the browser's memory and the router does not need to wait for it again.

The last piece of puzzle is to load the template. W e'll use the spin package to create a handsome animated loading screen. Add meteor add sacha:spin and then create the loading template in the client/templates/includes folder: loading

<template name="loading">
  {{>spinner}}
</template>

client/templates/includes/loading.html

Note {{> spinner}} is a template spin in the spin package. Although this part comes from outside of our app, we can use it just like any other template.

This is a great way to wait for your subscription, not only for the user experience, but also because it ensures that the data is immediately reflected on the template. This eliminates the problem that the underlying data must be available before the template that needs to be processed is rendered, which often requires complex solutions.

Wait for a subscription to the post

First contact responsiveness

Responsiveness is a core part of Meteor, and while we don't really have access, our loading templates give us the opportunity to get in touch with the concept.

Redirecting to a load template is fine if the data has not been loaded, but how does the router know when the data is loaded and then the user should redirect back to the original page?

What we just said is a reflection of responsiveness, but don't worry, you'll soon learn more about it.

Route to a specific post

Now that we've seen how postsList template, let's establish a route to display the details of a post.

Here's a problem: We can't continue to define routing rules and path mapping separately, because there may be thousands. So we need to establish a dynamic routing rule and have the routing rules show the posts we want to see.

First, we'll create a new template that simply renders the same template we use in the post list.

<template name="postPage">
  {{> postItem}}
</template>

client/templates/posts/post_page.html

We'll add more elements to this template in the future (such as comments), but now it's just going to be the shell for placing the . . . {{> postItem}}

We're going to create another routing rule, this time the URL path /posts/<ID> to postPage template:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.route('/', {name: 'postsList'});
Router.route('/posts/:_id', {
  name: 'postPage'
});

lib/router.js

This special :_id tells the router two things: First, to match any route that conforms to the /posts/xyz/ ("xyz" can be any character). Second, whatever is inside the "xyz", it is placed params _id the property.

Please note that we only use _id for convenience only. The router has no way of knowing whether you _id through an actual computer or just through random characters.

We're now routed to the correct template, but we're still missing one _id show through the _id of this post, but the template doesn't have a clue. So what if we solve this problem?

Thankfully, the router has a clever built-in solution: it allows you to specify a data source. T hink of the data source as a delicious cake filled with templates and layouts. Simply say, it's your template to fill in:

Meteor route

In our case, we can get the _id from the _id and use it to find our posts to get the right data source:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

Router.route('/', {name: 'postsList'});
Router.route('/posts/:_id', {
  name: 'postPage',
  data: function() { return Posts.findOne(this.params._id); }
});

lib/router.js

So every time a user visits this road by the rules, we find the right post and pass it on to the template. Keep in findOne returns a post that matches the query, and id provide an id as an argument, which can be {_id: id}

In the data method of the routing this to the currently this.params path in path : prefix).

More about data sources

By setting the template's data source, you can control the value of this template helper.

This work is usually done implicitly {{#each}} automatically sets the corresponding data source to each current item that is iterating:

{{#each widgets}}
  {{> widgetItem}}
{{/each}}

Of course, we can also use the #with to explicitly operate, it is like simply saying "take this object and provide it to the template application below". For example, we can write something like this:

{{#with myWidget}}
  {{> widgetPage}}
{{/with}}

Therefore, the same effect can be achieved by passing the data source as an argument to the template call, so the previous block of code can be rewritten as:

{{> widgetPage myWidget}}

To learn more about data sources, we recommend reading our blog posts.

Use dynamic routing Helper

Finally, we'll create a new Comment button and point to the correct post page. We can do something like <a href="/posts/{{_id}}"> pattern, but it's a little more reliable to use route Helper.

We've named the postPage so we can use the helper for the hashtag: {{pathFor 'postPage'}}

<template name="postItem">
  <div class="post">
    <div class="post-content">
      <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
    </div>
    <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss</a>
  </div>
</template>

client/templates/posts/post_item.html

Route to a separate post page

But wait, how exactly does the router know exactly where to get the xyz path from xyz /posts/xyz After all, we didn't _id to it.

Iron Router proved smart enough to discover it on its own. We tell the router to postPage routing rule, and the router knows that some parts of this _id that's how we path

Therefore, the router will look for this information in this the context of the context of the {{pathFor 'postPage'}} _id And in this example, this object corresponds to a post, which is where we're looking for _id property.

Alternatively, you can clearly specify where to look for by passing _id F or example, {{pathFor 'postPage' someOtherPost}} In practice, we'll use this mode if we want to get a link to the previous one or the latest in the list of posts.

To see if it's working properly, let's browse the post list page and click on one of the "Discuss" links. You should see something like this:

Meteor route A separate post page

HTML5 pushState

What we need to know here is that these URL changes are due to the fact that HTML5 pushState is being used .

The router accesses the interior of the site by processing URLs clicks, which prevents the browser from jumping out of our app, not just to change the state of the app as necessary.

If everything works, the page should change instantaneously. I n fact, sometimes things change too quickly and may require some type of transition page. This is beyond the scope of this chapter, but it is an interesting topic.

The post could not be found

Let's not forget that routing works in two ways: changing the URL of the page we visit can also show a new page where we change the URL. So we need to fix the situation when a user enters the wrong URL.

Fortunately, Iron Rounter can solve notFoundTemplate option.

First, let's set up a new template to display a simple 404 error message:

<template name="notFound">
  <div class="not-found jumbotron">
    <h2>404</h2>
    <p>Sorry, we couldn't find a page at this address. 抱歉,我们无法找到该页面。</p>
  </div>
</template>

client/templates/application/not_found.html

We then point Iron Rounter to this template:

Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
  waitOn: function() { return Meteor.subscribe('posts'); }
});

//...

lib/router.js

To verify this error page, you can try randomly entering a URL http://localhost:3000/nothing-here

But wait a minute, what if someone http://localhost:3000/posts/xyz a URL in a format xyz _id post? Although it is a legitimate route, it does not point to any data.

Fortunately, iron Rounter .js solve this problem intelligently enough if we add a special dataNotFound hook at the end of the route.js

//...

Router.onBeforeAction('dataNotFound', {only: 'postPage'});

lib/router.js

This tells Iron Router not only in the case of illegal routing, but postPage routing, data displays a "cannot find" page whenever the data function returns a "falsy" (such as null false false,undefined, or empty) object. undefined

Added a template that the page could not find

Why "Iron"?

You may want to know the story behind the name "Iron Router". According to Iron Router author Chris Mather, because meteors are mainly made up of iron elements.