I've completed the basic leaderboard app and read further documentation and finally decided to make my own app using this tutorial as a guide: http://meteorcapture.com/publishing-data-from-an-external-api/
my current code seems to work up until the point of passing data back to the client. I can't seem to get data from the server. Even though I have my subscribe and publish all set up.
I've cut down and simplified my code but to reduce points of error:
MyMp = new Mongo.Collection('mymp');
if (Meteor.isClient) {
Session.setDefault('searching', false);
Tracker.autorun(function(){
if(Session.get('postcode')){
var twfyHandle = Meteor.subscribe('twfySearch', Session.get('postcode'));
Session.set('searching', ! twfyHandle.ready());
}
});
Template.searchForm.events({
'submit form': function(event, template) {
event.preventDefault();
var postcode = template.$('input[type=text]').val();
if (postcode) {
Session.set('postcode', postcode);
}
}
});
Template.body.helpers({
mymp: function() {
return MyMp.find();
},
searching: function() {
return Session.get('searching');
}
});
}
if (Meteor.isServer) {
Meteor.publish('twfySearch', function(postcode){
console.log(postcode); // this received ok
var self = this;
var mp = {first_name: 'Test Name', party: 'Labour'}
self.added('mymp', Random.id(), mp);
self.ready();
});
}
Templates in my HTML file:
<body>
<h1>Get Details on your MP and Constituency</h1>
<h2>Enter your post code below</h2>
{{> searchForm }}
{{#if searching }}
<p>Searching...</p>
{{else}}
<div class="">
{{> twfyResults }}
</div>
{{/if}}
</body>
<template name="twfyResults">
{{ mp.first_name }}
</template>
<template name="searchForm">
<form>
<input type="text" name="postcode" id="postcode" />
<input type="submit" value="Search" />
</form>
</template>
I'm passing a postcode to the server and the server populates a basic JSON object 'mp' under a publish method and makes it ready().
This is where it fails. Although my console.log() calls show that the server is getting the postcode fine and creating the mp object. The client is not getting anything back!
UPDATE:
I have managed to manually run in the browser console MyMp.findOne() and it returns the object the server created. However, this object seems inaccesible to my template. Also the 'mp' object itself doesnt exist.
I've realised THREE errors in my code.
I assumed the template object used to access the data sent back had the same name on the frontend as it did in the server (mp). Instead I should have been trying to access the helper name "mymp".
This was fixed by changing the twfyResults template to reference the helper method:
<template name="twfyResults">
{{ mymp.first_name }}
</template>
My helper for the twfyResults was in the wrong context. So I rewrote my helpers like so:
Template.body.helpers({
searching: function() {
console.log(this);
return Session.get('searching');
}
});
Template.twfyResults.helpers({
mymp: function() {
return MyMp.findOne();
}
});
But the above alone wasn't enough. I also had to change the "mymp" helper to return just one result as in this case only one result would ever be returned. That way I could access my objects variables in the above way. So my helper was changed to findOne() instead of just find as seen above.
I have really weird problem and totally no clue how to solve it.
I am trying to build very simple fullpage.js and meteor.js implementation. The code below works, but after a few minutes, it breaks. When I open a new incognito window it works for a few minutes again, but when I refresh the page I get the following error 3 times (one for each document in collection).
Exception in queued task: Error: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
at Error (native)
at DOMRange.detach (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:512:24)
at DOMRange.setMembers (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:471:12)
at DOMRange.addMember (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:540:10)
at http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2541:32
at Object.Tracker.nonreactive (http://localhost:3000/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:513:12)
at Object.Blaze.Each.eachView.onViewCreated.eachView.stopHandle.ObserveSequence.observe.addedAt (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2528:17)
at Object.cursor.observe.addedAt (http://localhost:3000/packages/observe-sequence.js?2fd807ea171ead273b9e6458607cb226012d9240:339:19)
at LocalCollection._observeFromObserveChanges.observeChangesCallbacks.addedBefore (http://localhost:3000/packages/minimongo.js?e8806aa7782b729b2517ebc0cd10b321667f1427:3840:28)
at Object.LocalCollection._CachingChangeObserver.self.applyChange.addedBefore (http://localhost:3000/packages/minimongo.js?e8806aa7782b729b2517ebc0cd10b321667f1427:3777:56)
Meteorpad: http://meteorpad.com/pad/RT4HwXmXW6wbghNhK/Fullpage
main.html
<head>
<title>Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
{{>fullPage}}
</body>
<template name="fullPage">
<div id="fullpage" class="section">
{{#each products}}
<div class="slide"><h2>{{name}}</h2></div>
{{/each}}
</div>
</template>
client/app.js
Template.fullPage.rendered = function () {
$('#fullpage').fullpage();
};
Template.fullPage.helpers({
products: function () {
return Products.find();
}
});
server/app.js
Meteor.startup(function () {
if (Products.find().count() === 0) {
var Product1 = Products.insert({
name: "1"
});
var Product2 = Products.insert({
name: "2"
});
var Product3 = Products.insert({
name: "3"
});
}
});
common.js
Products = new Mongo.Collection('products');
Adding a <div> around your {{#each products}} may solve your problem.
I'm not familiar with fullpage.js, but I was getting the exact same error message with the maazalik:malihu-jquery-custom-scrollbar package. The plugin operates by copying and pasting elements into a new div, which can cause problems if the first child of that element is an {{#each}} or {{#if}}. Adding a <div> solves that.
You can find more info in this github issue.
Could you try adding else in the each function to see if it solves anything?
<template name="fullPage">
<div id="fullpage" class="section">
{{#each products}}
<div class="slide"><h2>{{name}}</h2></div>
{{else}}
{{! does this help? }}
{{/each}}
</div>
</template>
I need to display data to all clients using Meteor.publish/subscribe.I did one sample example in that example what i am did is 10 records are inserted at that time of server startup and these 10 records are trying to display to all clients.The Problem is The data doesn't shows the clients.I didn't have any idea because i am new to meteor JS.So Please see the below code and suggest me how to do?
HTML Code :
<head>
<title>DataApp</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
{{#each messages}}
{{messages}}
{{/each}}
</template>
ANd JS Code is :
Messages = new Meteor.Collection("messages");
if (Meteor.isClient)
{
Meteor.subscribe('messages');
}
if (Meteor.isServer)
{
Meteor.startup(function ()
{
// code to run on server at startup
for(var i = 0 ; i <= 10 ; i++ )
{
console.log("EXE"+i);
Messages.insert({ name: "EXE"+i });
}
});
Meteor.publish('messages', function() {
return Messages.find();
});
}
Firstly, you are missing a template helper on your template main, so make your Meteor.isClient look like this:
if (Meteor.isClient) {
Meteor.subscribe('messages');
// this is the template helper that is referenced by {{messages}}
// in your template 'main'
Template.main.messages = function () {
return Messages.find();
};
}
Secondly, the handlebars in your html don't make sense, try this instead:
<template name="main">
<!-- inserts value of property 'name' of each object in 'messages' -->
{{#each messages}}
{{name}}
{{/each}}
</template>
They way I'm testing this is a simple for loop in the template to run through the elements available to the client and display them in a list.
I insert the elements through a text input identified by #query.
When I enter an element, it displays for a brief instant, and a console log that prints out Links.find().fetch() shows that the element exists, and then shortly afterwards, the element is seemingly automagically removed making any successive calls to Links.find().fetch() yield an empty list. Is this a bug within Meteor? Or is it expected behaviour and bad implementation?
UPDATE
Another weird development, I added setTimeout(function(){Links.find().fetch()},3000); to the server side to try and track what was going on. With this line, the inserts work correctly for a while, and then crashes with these errors: http://i.imgur.com/CUYDO67.png
. What is going on?
Below is my template file myapp.html
<head>
<title>myapp</title>
</head>
<body>
{{> search_bar}}
<br>
{{> list_of_links}}
</body>
<template name="search_bar">
<h1>Playlist</h1>
<input id="query" type="text" placeholder="Enter Query Here"/>
</template>
<template name="list_of_links">
<ul id="item-list">
{{#each my_playlist}}
{{> link_item}}
{{/each}}
</ul>
</template>
<template name="link_item">
<li class="link">
<div class="link-title">{{youtube_link}} {{sess}}</div>
</li>
</template>
And here follows myapp.js
//Setting up a collection of urls
Links = new Meteor.Collection("links");
if (Meteor.isClient) {
//"Subscribing" to server's published data
Deps.autorun( function(){
Meteor.subscribe( "links", Meteor.default_connection._lastSessionId);
});
//Nuke database helper function -- debugging
Template.list_of_links.clean = function(collection) {
if(collection) {
// clean items
_.each(collection.find().fetch(), function(item){
collection.remove({_id: item._id});
});
}
}
//Songs from session
Template.list_of_links.my_playlist = function () {
return Links.find();
};
Template.search_bar.events({
//http://stackoverflow.com/a/13945912/765409
'keypress #query' : function (evt,template) {
// template data, if any, is available in 'this'
if (evt.which === 13){
var url = template.find('#query').value;
//Find a nicer way of clearing shit.
$("#query").val('');
Links.insert({sess:Meteor.default_connection._lastSessionId,youtube_link:url});
var cursor = Links.find();
cursor.rewind();
console.log(cursor.fetch());
//Add to database.
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Meteor.publish("links", function( sess ) {
return Links.find({sess: sess}); //each client will only have links with that _lastSessionId
});
//Making sure permissions are correct
Links.allow({
insert: function (userId, doc) {
return true;
}
});
});
}
That kind of behavior is expected when user doesn't have enough privileges to create a document. The insert function creates a local copy of the doc instantly (thanks to latency compensation), and then sync it with the result of server operation. If that operation fails, the temporary document is purged from client's Minimongo.
Have you created proper rules with Collection.allow? That's the first place to look for the cause.
I would like the ability to load templates dynamically without explicitly specifying the template.
As an example:
<template name="foo">
</template>
where 'foo' is the template, I would like the ability to load it dynamically by calling some method:
Meteor.render(Meteor.loadTemplate('foo'));
Is this possible?
Here's how to dynamically render templates, as of Meteor 0.9.4 - 1.0. All other answers were obsolete at the time of this writing.
Let's say you're editing a bunch of records, or creating a new one, and want to render either the update template, or the new template, based on some Session variables.
There are two ways to do this:
1) This is the officially recommended method for Meteor 0.9.4 or newer - it uses Template.dynamic:
<template name="records">
{{> Template.dynamic template=whichOne}}
</template>
<template name="recordUpdate">
...
</template>
<template name="recordNew">
...
</template>
Template.records.helpers({
whichOne: function () {
return Session.get('edit') ? 'recordUpdate' : 'recordNew'
// note that we return a string - per http://docs.meteor.com/#template_dynamic
}
});
2) This works in various Meteor versions, but isn't recommended officially because it's unclear that the template is chosen dynamically:
<template name="records">
{{> whichOne}}
</template>
{{! Note how "whichOne" is indistinguishable from a constant template name... }}
{{ ...like "recordUpdate" or "recordNew" below. }}
<template name="recordUpdate">
...
</template>
<template name="recordNew">
...
</template>
Template.records.helpers({
whichOne: function () {
return Session.get('edit') ? Template.recordUpdate : Template.recordNew
// note that we return a Template object, not a string
}
});
To pass a data context to the template, use:
{{> Template.dynamic template=whichOne data=myData}}
Meteor 0.9.x New API
Dan Dascalescu pointed out Meteor now has built-in dynamic templates! This is nice because you do not need to include the extra code as seen in previous versions.
{{> Template.dynamic template=template [data=data] }}
For Meteor 0.8.x Legacy
Dynamic Template Without Data: Boris Kotov's updated Blaze (0.8.0) answer is on the right track (taken from the latest docs), but it doesn't work as-is for me. I got the following to work:
{{> dynamicTemplate name=myDynName}}
<template name="dynamicTemplate">
{{#with chooseTemplate name}}
{{> template}}
{{/with}}
</template>
Template.dynamicTemplate.chooseTemplate = function (name) {
return { template: Template[name] };
};
I hope there is a simpler solution, but I needed to wrap the Template in a JSON as shown. Maybe this will help someone else to move forward.
Dynamic Template With Data: If you have and want data to be dynamic, be sure to make a helper method that can react. Be sure to do a Session.set() somewhere to see the effect.
// Inside "myContainingTemplate"
{{> dynamicTemplateWithData name=myDynName data=myDataHelper}}
<template name="dynamicTemplateWithData">
{{#with chooseTemplate name}}
{{#with ../data}}
{{> ..}}
{{/with}}
{{/with}}
</template>
Template.dynamicTemplateWithData.chooseTemplate = function (name) {
return Template[name];
};
Template.myContainingTemplate.helpers({
myDataHelper: function () {
Session.get('myReactiveKey');
}
});
You have found Meteor.render but what you are missing is the template loading.
In the docs it mentions that you can call Template.foo() to return the HTML for a template.
http://docs.meteor.com/#template_call
Putting that together you access the template foo or any other using bracket access so:
var templateName = "foo";
var fragment = Meteor.render( function() {
return Template[ templateName ](); // this calls the template and returns the HTML.
});
Then fragment is your Reactive fragment, so that your template can continue to receive live updates. Your fragment now needs placing in the web page (I use jQuery, so this example does as well):
$("#htmlnode").html( fragment );
$("#htmlnode") is just a node in your DOM where you want the template rendered. And you now have the rendered content in your web page.
I'm just doing it like this, no jQuery required:
EDITED
Template.mainContent.showContentFromRouter = function() {
return Template[Meteor.Router.page()]();
};
In this case I'm using the Meteor Router, and return whatever template that I choose to (from the Router), but you could just do this:
Template.mainContent.showDynamicContent = function() {
return Template['someTemplateYouveDefined']();
};
Update for blaze:
https://github.com/meteor/meteor/wiki/Using-Blaze#templatefoo-is-not-a-function-and-does-not-return-a-string
Dynamically render a template with a given data context
Old:
{{dynamicTemplate name="templateName" data=dataContext}}
Template.foo.dynamicTemplate = function (opts) {
return Template[opts.name](opts.data);
};
New: (Notably, in Blaze, keyword arguments to inclusion or block helpers are bundled into a single object which becomes the new data context)
{{> dynamicTemplate name="templateName" data=dataContext}}
<template name="dynamicTemplate">
{{#with chooseTemplate name}}
{{#with ../data}} {{! original 'data' argument to DynamicTemplate}}
{{> ..}} {{! return value from chooseTemplate(name) }}
{{/with}}
{{/with}}
</template>
Template.dynamicTemplate.chooseTemplate = function (name) {
return Template[name];
}
By the way, I don't really played with it, but this is what I took from the new blaze docs. So I think it should be the way to do it ;)
From https://github.com/meteor/meteor/wiki/Using-Blaze
{{> post}}
Template.foo.helpers({
post: function () {
return Template[this.postName];
}
});
Template inclusions now search the namespace of helpers and data for template objects, so it's easy to programmatically choose which template to use. This is a powerful feature, and will allow patterns like assigning one template as a helper of another so that it can be overridden.
Meteor 0.8.x Legacy
Using Joc's answer as a guide,
I've achieved similar using http://docs.meteor.com/#template_call, but using a helper instead, as suggested by the docs:
When called inside a template helper, the body of Meteor.render, or other settings where reactive HTML is being generated, the resulting HTML is annotated so that it renders as reactive DOM elements
My client.js looks a bit like this:
Template.myPageTemplate.helpers({
dynamicTemplate: function() {
// conditional logic can be added here to determine which template to render
return Template.myDynamicTemplate();
}
});
and my html looks like this:
<template name="myPageTemplate">
<h1>My Template</h1>
{{{dynamicTemplate}}}
</template>
<template name="myDynamicTemplate">
<h1>My Dynamic Template</h1>
</template>
Based on hillmark's answer, this is the easiest it could get:
Template.main.template = function() {
if (some_condition) {
return Template.A();
} else {
return Template.B();
}
};
With the corresponding .html
<body>
{{> main}}
</body>
<template name="main">
{{{template}}}
</template>
<template name="A">
<h1>Template A</h1>
</template>
<template name="B">
<h1>Template B</h1>
</template>
Edit
Doesn't work in Meteor 0.8.0
for me the easiest way was to just create a function get_dynamic_template, so something like:
var a= get_dynamic_template(template_name,data);
which returns what can be rendered as a normal variable {{a}}
The code for this function is quite simple:
var get_dynamic_template = function(template_name,data)
{
return function(){
return new Handlebars.SafeString(
UI.toHTML(
Template[template_name].extend({data: function () { return data; }}))
);
};
}
This would handl dynamic templates both with and without data:
(requires Blaze/ Meteor 0.8)
{{> dynamicTemplate name=templateName}}
<template name="dynamicTemplate">
{{#with chooseTemplate name }}
{{#if ../data}}
{{#with ../data }}
{{> .. }}
{{/with}}
{{else}}
{{> this}}
{{/if}}
{{/with}}
<template name="dynamicTemplate">
template javascript:
Template.dynamicTemplate.chooseTemplate = function (name) {
return Template[name];
};