I'm attempting to create a simple project for rapid prototyping using Meteor, Meteor Router, and Bootstrap.
Here's the directory Structure
meteor-prototypes/
|
|--- .meteor/
|--- prototypes/
| |
| |--- example/
| |
| |--- example.coffee
| |--- example.css
| |--- example-index.html
| |--- example-more.html
|
|--- prototypes.coffee
|--- index.html
|--- smart.json
|--- smart.lock
The example folder represents a single prototype, reachable at (for example) http://localhost:3000/prototypes/example/. Ideally, you would be able to add another prototype to the project simply by duplicating example/ with a new name (e.g. new-example) and visiting http://localhost:3000/prototypes/new-example/.
The problem with this is that Meteor, by default, searches the entire project directory for HTML files and loads them all. What I need to do is check which prototype we're viewing based on the URL (through Meteor Router) and load only the .html files in that folder (e.g. example/).
Is there a way to tell Meteor to load only .html files in a specific subdirectory? Or another way to accomplish this?
For those curious, or in case it helps, here are what each of the files mentioned in the directory structure above contain:
index.html
<head>
<title>desktime-prototypes</title>
</head>
<body>
{{ renderPage }}
</body>
<template name="home">
<h1>We have the following prototypes available:</h1>
<ul>
<li>Example</li>
</ul>
</template>
prototypes.coffee
if (Meteor.isClient)
Meteor.Router.add
'': 'home'
'/:prototype': (params) ->
return params
'/:prototype/:page': (params) ->
return params[1]
if (Meteor.isServer)
Meteor.startup ->
# code to run on server at startup
/prototypes/example.coffee
if Meteor.isClient
Template.example.greeting = ->
return "Welcome to prototypes."
Template.example.rendered = ->
# This function will fire when this specific template gets rendered,
# Great place to fire jQuery plugins, or anything else that needs
# to happen when the DOM is ready.
Template.example.events
'click input' : ->
# template data, if any, is available in 'this'
alert 'Button clicked!'
prototypes/example/example-index.html
<template name="example">
<h1>Welcome to the example prototype!</h1>
{{> example-more }}
</template>
Great question...two things:
(1) meteor-router currently lacks the server side rendering you need (though it's close).
(2) HTML file names are completely irrelevant to the routing system. The folders in which they live matter insofar as the order they're loaded, but the names do not match to routes the way you expect.
To achieve what you're looking for you can (1) use links in the app for routing but don't change the URL in the address bar and expect that to work yet, and (2) change the template names of the various html files in your /prototypes folder to match the prototype you're wanting to demo. Below is an example:
HTML:
<body>
<h1>All Prototypes</h1>
{{>proto}}
<div>
{{renderPage}}
</div>
</body>
<template name="landing">
LANDING
</template>
<template name="proto">
{{#each items}}
{{name}}
{{/each}}
</template>
Javascript:
if (Meteor.isClient) {
Meteor.startup(function () {
Meteor.Router.to("/landing");
});
Meteor.Router.add({
'/landing': 'landing',
'/prototypes/:page': function (proto) {
return proto;
}
});
Template.proto.items = function () {
return [{ name: "Prototype 1", id: "prototype1" }, { name: "Prototype 2", id: "prototype2" }];
};
}
Prototype 1 HTML:
<template name="prototype1">
<h1>Prototype 1</h1>
</template>
Prototype 2 HTML:
<template name="prototype2">
<h1>Prototype 2</h1>
</template>
Is there a reason you want multiple prototypes inside one Meteor project? Do they share code? If not, why not just use one Meteor project per prototype? And then what you could do is create a command line util yourself that does something like
meteor_proto example1
meteor_proto example2
which creates Meteor projects but just pre-populates them with the files you want (you could create your ideal prototyping project and put it somewhere, then just have your command line copy the contents of that folder over while after doing a meteor create command or something).
This would be a nice feature for Meteor to have by default, actually.
Related
I am a newbie in Meteor. I am developing an app having a login page that must redirect to certain pages as per the login id.There are certain click events which opens up html pages.I have the hard code data in the pages to check the flow now.I have the html pages as well designed, but I am not able to link them for click events and login. Please help.
Here is one way to do it:
In your HTML file, something like this:
<head>
<title>Duckbilled Platypus</title>
</head>
<template name='layout'>
{{> banner}}
{{> yield}}
</template>
<template name="banner">
<h1 class="chocolatefont">Platypi of the World Unite! (Duckbilled, that is)</h1>
<hr/>
</template>
<template name="main">
<div id="templateMain" name="templateMain">
<h2>RAVES</h2>
<p>No Raves yet</p>
<h2>RANTS</h2>
<p>No Rants yet</p>
<h2>RANDOM</h2>
The Legend of NFN Oscar
<br/><br/>
NFN Oscar's donut
<br/><br/>
Alliteration Station ("Ben's Bizarre Bazaar")
<br/><br/>
Boomeranging Telescopic and Kaleidoscopic Phrase Mazes
<br/><br/>
Acrostics
<br/><br/>
Homonym Homie
</div>
</template>
...and then add whichever templates you want for the pages you want to route to; in my case, it's one for each "href" referenced in the anchor tags (nfnoscar, nfnoscarsdout. etc.)
The "yield" (which means, "insert here whatever the router says corresponds to the URL") requires Iron:Router, which you say you already have.
In your JS file, something like this:
Router.configure({
layoutTemplate: 'layout'
});
Router.route('/', {
name: 'main',
template: 'main'
});
Router.route('/nfnoscar', {
name: 'nfnoscar',
template: 'nfnoscar'
});
Router.route('/nfnoscarsdonut', {
name: 'nfnoscarsdonut',
template: 'nfnoscarsdonut'
});
Router.route('/alliterationstation', {
name: 'alliterationstation',
template: 'alliterationstation'
});
Router.route('/btakpm', {
name: 'btakpm',
template: 'btakpm'
});
Router.route('/homonyms', {
name: 'homonyms',
template: 'homonyms'
});
Router.route('/acrostics', {
name: 'acrostics',
template: 'acrostics'
});
Now, whichever link is clicked, the corresponding page is loaded by means of the "yield" and the Iron Router routing.
You can see this particular app and how it works when you click the links, etc., at my "sandbox" Meteorsite here.
I have a page header template, which has a title variable like so:
{{> pageHeader title="questions"}}
<template name="pageHeader">
<h1>{{title}}</h1>
</template>
This works fine. But I use i18n to set my titles, like {{i18n 'title'}}.
How can I use this in the template call? When I use this it doesn't work:
{{> pageHeader title="{{i18n 'title'}}"}}
Not yet on master branch of meteor but on develop: https://github.com/meteor/meteor/issues/5066
If you update to the 1.2 release candidate you can already use this feature.
Update to the rc:
meteor update --release METEOR#1.2-rc.10
Use the nested sub-expressions:
{{> pageHeader title=(i18n 'title')}}
You can resolve the i18n in your .js file.
Your template would be
{{> pageHeader title=i18nTitle}}
and you'd have a helper that would solve for the i18n
Template.xxx.helpers({
i18nTitle: function() {
return i18nMethod('title');
}
});
I'm new to both meteor and JS.
I created a meteor app like this:
cd /tmp/
meteor create hello
cd hello
In hello.html I wrote this
<body>
<h1>Welcome to Meteor!</h1>
{{> t1}}
{{> t2}}
</body>
Then I added these templates:
<template name='t1'>
<span id='t1'>hello</span>
</template>
<template name='t2'>
<span id='t2'>world</span>
</template>
Then I wrote syntax inside of hello.js to get text from the DOM:
Template.t1.onRendered( function() {
mytext = $('span#t1').html() });
According to the debugger-console in my browser,
the above syntax works okay.
The debugger tells me that mytext == 'hello'
Question: How to share the mytext value with other parts of my Meteor app?
As written, mytext is stuck inside my anonymous function.
For example how would I make this call work:
$('span#t2').html(mytext)
??
You'll want to reactively watch your variable. An easy out-of-the-box solution for this is to use the Session variable. You can do it pretty easily like so:
Template.t1.onRendered( function() {
Session.set('mytext', $('span#t1').html());
});
Then in your second template, define and reference helper that returns that variable:
<!--html-->
<template name='t2'>
<span id='t2'>{{getMyText}}</span>
</template>
//js
Template.t2.helpers({
getMyText: function() { return Session.get('mytext'); }
});
The Session variable is reactive, so when the first template renders and sets the value of Session.mytext, the helper's result will be invalidated and the second template will update.
Say I have the following html.
<template name="Layout">
<div>{{name}}</div>
{{> yield}}
</template>
<template name="Home">
<div>Home Page</div>
</template>
However, data is now passed to Layout. How do I pass data in my home.js file to Layout? (In this case, I want to pass a name to {{name}})
you can use paths in data to refer to called data as in {{../name }}
also Template.parentData([numLevels])
http://docs.meteor.com/#/full/template_parentdata
The most typical way you share data between your .js files and your templates is via 'helpers'. You can create a simple helper to return the data to your Layout template.
In your JS file, put this anywhere:
Template.Layout.helpers({
name: function() {
return myVar;
}
})
If you use something like Iron Router, you can have a more 'global' data context for the route (page) you are on. When you are on a 'route' you can simply do this.myVar if its in the route data context.
Hope this helps.
I've a tab bar on a responsive app I'm building:
<template name="tabNav">
<nav class="bar bar-tab">
<a class="tab-item" id="groups-nav" href="{{pathFor 'groupsList'}}">
<span class="icon icon-star-filled"></span>
<span class="tab-label">Groups</span>
</a>
<a class="tab-item active" id="games-nav" href="{{pathFor 'locationSet'}}">
<span class="icon icon-list"></span>
<span class="tab-label">Games</span>
</a>
<!-- more code -->
</template>
The pathFor 'groupsList' works on desktop, but not on mobile. You can try it out here: pp-groups.meteor.com.
This is a prototype only and doesn't use any real data. All of my views code is available here: https://github.com/stewartmccoy/pp-groups/tree/master/groups/client/views
These are my defined routes:
Router.map(function() {
this.route('layout', {
path: '/',
template: 'getLocation',
layoutTemplate: 'getLocation',
yieldTemplates: {
'tabNav': {to: 'footer'}
}
});
this.route('locationSet', {
path: '/locationSet',
template: 'locationSet',
layoutTemplate: 'locationSet'
});
this.route('groupsList', {
path: '/groupsList',
template: 'groupsList',
layoutTemplate: 'groupsList'
});
});
Why doesn't the pathFor work on mobile? (It at least doesn't work in Xcode iOS simulator or on iPhone Mobile Safari or Chrome).
The push.js component is causing the issue. You can still use Rachet with Iron Router by disabling push.js. Per rachet's documention you can disable push by adding a data-ignore tag to your HTML link.
<!-- Use data-ignore="push" to prevent the push.js interception -->
<a href="http://www.google.com" data-ignore="push">Google<a>
Routing issue:
Removing the ratchet package fixed it for me. Looks like ratchet uses it's own way of linking between templates which is incompatible with iron-router. Removing ratchet removes the UI elements, but the routing works on mobile: http://pp-groups-fixed.meteor.com. You could use a strictly UI library, like bootstrap to make the UI elements, or maybe even just the ratchet's UI components. If you want to fully use ratchet, you will most likely have to forgo IronRouter.
Other things to fix:
Layout Templates
When using meteor and iron-router, a layout template is a template with common elements, with a {{> yield}} placed where you want the regular templates to show up.
You actually only have one real layout template in your code, in groups.html there is a layout template named layout, and it is unused.
In your code, regular templates are being misused as layout templates, because they don't have {{> yield}} in them. Also, the tabNav template is being placed using iron-router, yet you have already included it in each template with {{> tabNav}}.
So, you can simply get rid of the layout template code in your iron router, and your app will still function:
Router.map(function() {
this.route('layout', {
path: '/',
template: 'getLocation',
// layoutTemplate: 'getLocation',
// yieldTemplates: {
// 'tabNav': {to: 'footer'}
// }
});
this.route('locationSet', {
path: '/locationSet',
template: 'locationSet',
// layoutTemplate: 'locationSet'
});
this.route('groupsList', {
path: '/groupsList',
template: 'groupsList',
// layoutTemplate: 'groupsList'
});
});
A better way is to take out all the common code, the header, general structure of the page, tab bar, and put it in a layout template. Add a {{> yield}} where you want the page template to render. Refer to this layout template in your router as layoutTemplate.
Another sidenote, iron-router automatically looks for the template with the same name as the route, if no template is defined. So if you are writing this.route('groupsList', ... you don't need to write template: 'groupsList' as well.
Data
Your past-game.js file should be named get-location.js. Yes, the name itself doesn't matter, but that is getLocation's complimentary code, not postGame's. Same with scheduled-games.js and locationSet. Look at the Template.templateName.helpers to see how the code corresponds.
Of course, ideally this data should be in a collection. For now, instead of creating the data as arrays with var, you could create a seperate file with your data as global variables. Simply define as PastGames = [...], then use the template helpers to return the data you need.