I'm struggling to load templates when using Flow Router. If I include my templates in /client/app.html -
<body>
{{> applicationLayout}}
</body>
<template name="applicationLayout">
<header>
{{> Template.dynamic template=header}}
</header>
</template>
<template name="mainMenu">
<ul>
<li>who we are </li>
<li>what we do</li>
<li>where we work</li>
</ul>
</template>
And my routes in /lib/routes.js -
FlowRouter.route( '/terms', {
action: function() {
BlazeLayout.render( 'applicationLayout', {
header: 'mainMenu',
} );
},
name: 'termsOfService', // Optional route name.
});
Then everything works fine. But I want to split out my templates. So I create /imports/ui/components/main-menu.html
<template name="mainMenu">
<h2> {{currentPage}} </h2>
<ul>
{{#each pages }}
<li> {{ page.headerTitle }} </li>
{{/each}}
</ul>
</template>
I remove the template from /client/app.html and then add the following import to the top of my /lib/routes.js
import '/imports/ui/components/main-menu.js';
Now I get the following error -
/Users/aidan/.meteor/packages/meteor-tool/.1.3.2_4.1s3pnfi++os.osx.x86_64+web.browser+web.cordova/mt-os.osx.x86_64/dev_bundle/server-lib/node_modules/fibers/future.js:267
throw(ex);
^
Error: Cannot find module './main-menu.html'
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:78:1)
at meteorInstall.imports.ui.components.main-menu.js (imports/ui/components/main-menu.js:5:1)
at fileEvaluate (packages/modules-runtime/.npm/package/node_modules/install/install.js:141:1)
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:75:1)
at meteorInstall.lib.routes.js (lib/routes.js:1:1)
at fileEvaluate (packages/modules-runtime/.npm/package/node_modules/install/install.js:141:1)
at require (packages/modules-runtime/.npm/package/node_modules/install/install.js:75:1)
at /Users/aidan/Code/edu-finance/.meteor/local/build/programs/server/app/app.js:239:1
at /Users/aidan/Code/edu-finance/.meteor/local/build/programs/server/boot.js:283:10
at Array.forEach (native)
Exited with code: 8
Your application is crashing. Waiting for file change.
I can't work out what's going on here. How do I split out my templates?
UPDATE
So I've managed to get somewhere by putting my includes in /client/app.js. This is displaying things as I expected. I'm not sure this is the best way to structure everything. And I'd still love to know what the problem was trying to do the imports in /lib/routes.js
Related
The site I'm currently working on is built in Drupal 7. I have one form that requires user input so I'm attempting to build it with VueJS. The form is contained all within one template file (.tpl.php) and all the content is provided in this template file or via the VueJS Javascript (nothing is coming from the CMS).
The issue I have is that the Vue components are not rendering on the front-end, but when I copy the code into a JSFiddle they do, so I'm guessing it is an issue with the interaction between VueJS and Drupal. Here is a screenshot of my markup when inspecting...
Here is the code from the .tpl.php file...
<div id="app">
<form>
<div>
<label for="year">Per Year</label>
<input type="radio" name="frequency" id="year" value="year" v-model="frequency" checked>
<label for="month">Per Month</label>
<input type="radio" name="frequency" id="month" value="month" v-model="frequency">
</div>
</form>
<ul class="plans">
<template id="plan-component">
<h2 class="plan-name">{{ name }}</h2>
<h2 class="plan-cost">{{ price }}</h2>
<h2 class="plan-tagline">{{ tagline }}</h2>
Choose this plan
</template>
<li>
<plan-component :frequency="frequency"
name="Basic"
tagline="Basic tagline"
price-yearly="Free"
price-monthly="Free"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Rec"
tagline="Rec tagline"
price-yearly="3"
price-monthly="4"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Team"
tagline="Team tagline"
price-yearly="4"
price-monthly="5"
></plan-component>
</li>
<li>
<plan-component :frequency="frequency"
name="Club"
tagline="Club tagline"
price-yearly="5"
price-monthly="6"
></plan-component>
</li>
</ul>
</div>
..and the code from my JS file...
Vue.component('plan-component', {
template: '#plan-component',
props: ['frequency', 'name', 'tagline', 'priceYearly', 'priceMonthly'],
computed: {
'price': function() {
if (this.frequency === 'year') {
return this.priceYearly;
} else {
return this.priceMonthly;
}
}
},
methods: {
makeActivePlan() {
// We dispatch an event setting this to become the active plan
this.$dispatch('set-active-plan', this);
}
}
});
new Vue({
el: '#app',
data: {
frequency: 'year',
activePlan: {name: 'no', price: 'You must select a plan!' }
},
events: {
'set-active-plan': function(plan) {
this.activePlan = plan;
}
},
});
And here is the JSFiddle which outputs the components correctly - https://jsfiddle.net/2xgrpLm6/
What browser are you using? <template> tags are not supported in IE.
Another idea is to make sure you are never using fragment components (meaning wrap everything inside your template with a div like so:
<template id="foobar">
<div>
CONTENT HERE
</div>
</template>
Lastly, have you turned on Vue debug mode? Before you instantiate your Vue instance, set Vue.config.debug = true and see if you get console errors then.
Try moving the <template id="plan-component">...</template> code outside of the Vue instance. I.e., such that it is not contained within <div id="app">...</div>.
This has solved a similar problem for me in the past, though I'm not sure if it applies here.
For anyone having a similar issue, the solution was simple. After Jeff suggested turning on Vue debug mode (and downloading the Dev version of Vue JS instead of minified - https://vuejs.org/guide/installation.html) the console gave the error [Vue warn]: Cannot find element: #app.
The issue was that Drupal was loading my scripts in the <head>, before <div id="app"> was loaded in the DOM. As such #app couldn't be found. After outputting the scripts before the closing <body> tag all was sorted. See here for more information [Vue warn]: Cannot find element
I was after some advice please on the best way to set up data contexts for a MongoDB database with Iron Router.
To explain I'm working on a fairly basic film reviews project, and deployed to Modulus at http://reviews-48062.onmodulus.net/
This returns a list of reviews, but would like to use routes to create other pages. I've installed Iron Router locally and have a local MongoDB collection (called tasks) with some data in it.
Have re-written some code to include routing information. This works successfully in displaying the routes, but doesn't seem to pull in any data into the {{each}} statement.
My JS code is as follows:
// define Mongodb collection
Tasks = new Mongo.Collection("tasks");
// set up home route and database query
Router.route('/', function () {
this.render('Home', {
tasks: function () { return Tasks.find({}, {sort: {title: 1}, limit: 10}); }
});
});
// define routes
Router.route('/one');
Router.route('/two');
Router.route('/three');
The HTML code is:
<head>
<title>Iron router sandbox</title>
</head>
<body>
</body>
<template name="Home">
{{> Nav}}
<h1>Home</h1>
<p>This is a test</p>
{{#each tasks}}
<li>
<strong>{{title}}</strong>
<p>Directed by {{director}}</p>
<p>{{review}}</p>
<p>Available on: {{format}}</p>
</li>
{{/each}}
</template>
<template name="One">
{{> Nav}}
<h1>Page One</h1>
<p>Some more text.</p>
</template>
<template name="Two">
{{> Nav}}
<h1>Page Two</h1>
<p>A bit more text.</p>
</template>
<template name="Three">
{{> Nav}}
<h1>Page Three</h1>
<p>Even more text.</p>
</template>
<template name="Nav">
<ul>
<li>
Home
</li>
<li>
Page One
</li>
<li>
Page Two
</li>
<li>
Page Three
</li>
</ul>
</template>
My understanding is that the data context is set up for the "Home" template, so not sure what's wrong with the code.
Any advice much appreciated.
tasks is not a valid property to send to that object. You need to set the data context for IronRouter using data instead. Change tasks to data, then set up a helper on your template to get tasks data:
Template.Home.helpers({
tasks() {
return Tasks.find({}, {sort: {title: 1}, limit: 10});
}
});
You can return a data context with i-r but it needs to be named data:
Router.route('/', function () {
this.render('Home', {
data: function () {
return { tasks: Tasks.find({}, {sort: {title: 1}, limit: 10}); } };
});
});
This will return a data context to your template and then the tasks key will contain a cursor of tasks.
I am trying to get my head around setting different master route templates for account holders and guests. I cannot seem to render different templates (two different nav bars) dependent on whether a user has logged in or not. I have set up a template for guests
//guest.js
<template name="unregistered">
{{>navUnreg}}
<div class="container">
{{>about}}
</div>
</template>
<template name="navUnreg">
<nav class="navbar navbar-default">
<div class="container">
<ul class="nav navbar-nav">
<a class="navbar-brand" href="#">CabStack</a>
</ul>
<ul class="nav navbar-nav navbar-right">
{{>loginButtons}}
</ul>
</div>
</nav>
</template>
This is the generic about page for users that are not logged on. I then have the template for Account users that have logged in which is -
//UserLoggedIn.js
<template name="cabMaster">
{{>cabNav}}
<div class="container">
{{>yield}}
</div>
</template>
I initially tried to place an if/else statement into a Router configure -
//config.js
Router.configure({
if(Meteor.user()){
layoutTemplate: "cabMaster"
}
else{
layoutTemplate: "unregistered"
}
});
However I keep getting thrown an error. I have already used the Router.onBeforeAction on the userLoggedIn.js page to restrict access to routes until a profile page is filled out which is
//Access rights for logged in users
function userNoAccess(){
if(!Meteor.userId()){
alert("restricted until profile page completed");
this.render(“profile”);
return pause();
} else {
this.next();
}
}
//Do not allow access to nav links until user has filled out ‘profile’ page.
Router.onBeforeAction(userNoAccess, {
only: ["dashboard", "edit_dashboard", "help"]
});
So
I did think about placing the userLoggedIn route in the Accounts.onLogin() function that I have in my client.js file but I want to keep all routes in one place. Does anyone have any suggestions as to how I might achieve this. Thanks
There are several ways to go about this. You don't have to do everything in the router for example. The choice of navbar can happen right in your template:
<template name="navBar">
{{#if currentUser}}
{{> navReg}}
{{else}}
{{> navUnreg}}
{{/if}}
</template>
This is quite convenient when you're trying to alter little parts of your layout without switching out the whole layout.
I suggest you look at using controllers for dealing with authenticated and unauthenticated routes. They make your routing code much more maintainable. I've written a basic tutorial that you might find helpful.
As the title says, this.render does not render a template it's provided with. This is the code in router.js:
Router.configure({
layoutTemplate: 'main'
});
Router.route('/', function(){
this.render('postsList');
});
The file containing the layout template, main.html:
<template name='main'>
<div class='container'>
<header class='navbar'>
<div class='navbar-inner'>
<a class='brand' href='/'>MyApp</a>
</div>
</header>
<div id='main' class='row-fluid'>
{{> yield}}
</div>
</div>
</template>
And the file containing the postsList template which is passed to this.render() function
<template name='postsList'>
<div class='posts'>
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
So when I go to localhost:3000/ the page displays only the main template and not the postsList template. However, there is no error, unless I completely remove Router.route(...), at which point it will display the standard 'route not found' error.
Also, I tried not using the global template, but a route template, by removing Router.configure(...) and adding this.layout('main') to Router.route(...). The browser then displays nothing.
Your code is perfectly fine. I also came across this issue. The iron:router package seems to be missing the ejson dependancy.
Add the ejson to your app and it should work.
meteor add ejson
I'm sure when iron:router is updated this will be resolved.
I have an application built with Meteor that uses Iron Router. My layout uses multiple yield templates and I'd like to pass through different data to each one.
It successfully passes through tasks to the tasksList template, but doesn't pass through selectedTask to the taskDetail template.
Is it possible to have multiple data sources and is this the right way to go about it? And if yes, then why is it not working?
Thanks in advance! :-)
Router.map(function() {
this.route('tasksList', {
path: '/',
layoutTemplate: 'layout',
template: 'tasksList',
yieldTemplates: {
'taskDetail': {to: 'rightTemplate'}
},
data: {
tasks: function(){ return Tasks.find() },
selectedTask: function() { return Tasks.findOne() }
}
});
});
<template name="layout">
<section class="wrapper">
<div class="left-pane">
{{yield}}
</div>
<div class="right-pane">
{{yield 'rightTemplate'}}
</div>
</section>
</template>
<template name="tasksList">
<ul>
{{#each tasks}}
<li>{{detail}}</li>
{{/each}}
</ul>
</template>
<template name="taskDetail">
{{#each selectedTask}}
<div>{{detail}}</div>
{{/each}}
</template>
You are returning selectedTask as a single object (with findOne), but in the taskDetail template, you use {{#each selectedTask}}{{detail}}{{/each}}. What happens if you simply have {{detail}} as the body of that template?
Sorry, both those methods work for me now. I must have had a wrong template name or something similar.
You can have multiple data sources as shown in the examples above.