ostrio:flow-router-meta

v2.2.0Published 10 months ago

support support

Reactive meta tags, JavaScript and CSSs

Change meta tags on the fly in Meteor.js apps via flow-router-extra API. This package can create meta tags, script and link tags as well.

Features:

  • 👷‍♂️ 100% tests coverage;
  • 🎛 Per route, per group, and default (all routes) meta tags;
  • 🎛 Per route, per group, and default (all routes) scripts;
  • 🎛 Per route, per group, and default (all routes) link, like CSS files.

Various ways to set meta, script and link tags, ordered by prioritization:

  • FlowRouter.route() [overrides all below]
  • FlowRouter.group()
  • FlowRouter.globals
  • Head template <meta/>, <link/>, <script/> tags [might be overridden by any above]

ToC:

Install:

meteor add ostrio:flow-router-meta

Note: this package implies ostrio:flow-router-title package.

Demos / Tests:

ES6 Import:

1import { FlowRouterMeta } from 'meteor/ostrio:flow-router-meta';
2// This library implies ostrio:flow-router-title package, and both can be imported in single line:
3import { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta';

Usage:

You need to initialize FlowRouterMeta and FlowRouterTitle classes by passing FlowRouter object. Right after creating all your routes:

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2import { FlowRouterMeta, FlowRouterTitle } from 'meteor/ostrio:flow-router-meta';
3
4FlowRouter.route('/', {
5  action() { /* ... */ },
6  title: 'Title'
7  /* ... */
8});
9
10new FlowRouterMeta(FlowRouter);
11new FlowRouterTitle(FlowRouter);

Set CSS and JS per route:

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2
3// Set default JS and CSS for all routes
4FlowRouter.globals.push({
5  link: {
6    twbs: {
7      rel: 'stylesheet',
8      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
9    }
10  },
11  script: {
12    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'
13  }
14});
15
16// Rewrite default JS and CSS, for second route, via controller:
17FlowRouter.route('/secondPage', {
18  name: 'secondPage',
19  action() {
20    return this.render('layout', 'secondPage');
21  },
22  link: {
23    twbs: {
24      rel: 'stylesheet',
25      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/css/bootstrap.min.css'
26    }
27  },
28  script: {
29    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.2.0/js/bootstrap.min.js'
30  }
31});
32
33// Unset defaults, via controller:
34FlowRouter.route('/secondPage', {
35  name: 'secondPage',
36  action() {
37    return this.render('layout', 'secondPage');
38  },
39  link: {
40    twbs: null
41  },
42  script: {
43    twbs: null
44  }
45});
46
47// Rewrite default JS and CSS, for route group:
48const group = FlowRouter.group({
49  link: {
50    twbs: {
51      rel: 'stylesheet',
52      href: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/css/bootstrap.min.css'
53    }
54  },
55  script: {
56    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha/js/bootstrap.min.js'
57  }
58});
59
60group.route('/groupPage1', {
61  name: 'groupPage1',
62  action() {
63    return this.render('layout', 'groupPage1');
64  }
65});

ldjson:

This method uses special property named innerHTML which set script's content instead of attribute. This method and property can be used in the any other case when you need to set script's contents.

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2
3FlowRouter.route('/fourthPage', {
4  name: 'fourthPage',
5  title: 'Fourth Page title',
6  script: {
7    ldjson: {
8      type: 'application/ld+json',
9      innerHTML: JSON.stringify({
10        '@context': 'http://schema.org/',
11        '@type': 'Recipe',
12        name: 'Grandma\'s Holiday Apple Pie',
13        author: 'Elaine Smith',
14        image: 'http://images.edge-generalmills.com/56459281-6fe6-4d9d-984f-385c9488d824.jpg',
15        description: 'A classic apple pie.',
16        aggregateRating: {
17          '@type': 'AggregateRating',
18          ratingValue: '4',
19          reviewCount: '276',
20          bestRating: '5',
21          worstRating: '1'
22        }
23      })
24    }
25  },
26  action() { /*...*/ }
27});

Use function as value:

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2
3FlowRouter.route('/routePath', {
4  name: 'routeName',
5  meta: {
6    url: {
7      property: 'og:url',
8      itemprop: 'url',
9      content() {
10        return document.location.href;
11      }
12    }
13  },
14  link: {
15    canonical() {
16      return document.location.href;
17    }
18  }
19});

Use function context:

Read about data hook.

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2
3FlowRouter.route('/post/:_id', {
4  name: 'post',
5  waitOn(params) {
6    return [Meteor.subscribe('post', params._id)];
7  },
8  data(params) {
9    return Collection.Posts.findOne(params._id);
10  },
11  meta: {
12    keywords: {
13      name: 'keywords',
14      itemprop: 'keywords',
15      content(params, query, data = {}) {
16        return data.keywords;
17      }
18    }
19  },
20  title(params, query, data = {}) {
21    if (data) {
22      return data.title;
23    }
24    return '404: Page not found';
25  }
26});

Other examples:

Set only name and content attributes on meta tag:

1FlowRouter.route('/routePath', {
2  name: 'routeName',
3  meta: {
4    name: 'content'
5  }
6});

Set only rel and href attributes on link tag:

1FlowRouter.route('/routePath', {
2  name: 'routeName',
3  link: {
4    rel: 'http://example.com'
5  }
6});

Set multiple attributes on meta tag:

1FlowRouter.route('/routePath', {
2  name: 'routeName',
3  meta: {
4    uniqueName: {
5      name: 'name',
6      content: 'value',
7      property: 'og:name',
8      itemprop: 'name'
9    }
10  }
11});

Set multiple attributes on link tag:

1FlowRouter.route('/routePath', {
2  name: 'routeName',
3  link: {
4    uniqueName: {
5      rel: 'name',
6      sizes: 'value',
7      href: 'value',
8      type: 'value'
9    }
10  }
11});

Bootstrap configuration:

1import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
2
3FlowRouter.route('/routePath', {
4  name: 'routeName',
5  meta: {
6    // <meta charset="UTF-8">
7    charset: {
8      charset: 'UTF-8'
9    },
10
11    // <meta name="keywords" content="Awes..">
12    keywords: {
13      name: 'keywords',
14      itemprop: 'keywords',
15      content: 'Awesome, Meteor, based, app'
16    },
17
18    // <meta name="description" itemprop="description" property="og:description" content="Default desc..">
19    description: {
20      name: 'description',
21      itemprop: 'description',
22      property: 'og:description',
23      content: 'Default description'
24    },
25    image: {
26      name: 'twitter:image',
27      itemprop: 'image',
28      property: 'og:image',
29      content: 'http://example.com'
30    },
31    'og:type': 'website',
32    'og:title'() {
33      return document.title;
34    },
35    'og:site_name': 'My Awesome Site',
36    url: {
37      property: 'og:url',
38      itemprop: 'url',
39      content() {
40        return window.location.href;
41      }
42    },
43    'twitter:card': 'summary',
44    'twitter:title'() {
45      return document.title;
46    },
47    'twitter:description': 'Default description',
48    'twitter:site': {
49      name: 'twitter:site',
50      value: '@twitterAccountName'
51    },
52    'twitter:creator': {
53      name: 'twitter:creator',
54      value: '@twitterAccountName'
55    },
56    'http-equiv': {
57      'http-equiv': 'X-UA-Compatible',
58      content: 'IE=edge,chrome=1'
59    },
60    robots: 'index, follow',
61    google: 'notranslate'
62  },
63  link: {
64    // <link href="https://maxcdn.bootstrapcdn.com/..." rel="stylesheet">
65    stylesheet: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css',
66
67    // <link rel="canonical" href="http://example.com">
68    canonical() {
69      return document.location.href;
70    },
71
72    // <link rel="image" sizes="500x500" href="http://example.com">
73    image: {
74      rel: 'image',
75      sizes: '500x500',
76      href: 'http://example.com'
77    },
78    publisher: 'http://plus.google...',
79    'shortcut icon': {
80      rel: 'shortcut icon',
81      type: 'image/x-icon',
82      href: 'http://example.com'
83    },
84    'icon': {
85      rel: 'icon',
86      type: 'image/png',
87      href: 'http://example.com'
88    },
89    'apple-touch-icon-144': {
90      rel: 'apple-touch-icon',
91      sizes: '144x144',
92      href: 'http://example.com'
93    },
94    'apple-touch-icon-114': {
95      rel: 'apple-touch-icon',
96      sizes: '114x114',
97      href: 'http://example.com'
98    },
99    'apple-touch-icon-72': {
100      rel: 'apple-touch-icon',
101      sizes: '72x72',
102      href: 'http://example.com'
103    },
104    'apple-touch-icon-57': {
105      rel: 'apple-touch-icon',
106      sizes: '57x57',
107      href: 'http://example.com'
108    }
109  },
110  script: {
111    twbs: 'https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js',
112    d3: {
113      src: 'https://d3js.org/d3.v3.min.js',
114      charset: 'utf-8'
115    }
116  }
117});

Running Tests

  1. Clone this package
  2. In Terminal (Console) go to directory where package is cloned
  3. Then run:

Meteor/Tinytest

# Default
meteor test-packages ./

# With custom port
meteor test-packages ./ --port 8888

# With local MongoDB and custom port
MONGO_URL="mongodb://127.0.0.1:27017/flow-router-meta-tests" meteor test-packages ./ --port 8888

Support this project: