I am using Meteor for Windows. I am trying to run a function in breadcrumb.js which is located in /client/lib
and I keep getting this error. ReferenceError: initBreadcrumb is not defined. I am a Java programmer and I am not sure if I am just trying to call intiBreadcrumb() wrong or if there is just something weird with Meteor that I am not used to.
I reference it in index.js like so:
if (Meteor.isClient) {
// counter starts at 0
Session.setDefault('json', "Hello");
Template.div1.created = function() {
initBreadcrumb();
}
}
Here is the breadcrumb.js code:
var breadcrumbList;
var crumbItems = [];
var variableCrumb;
function initBreadcrumb() {
var breadcrumb=$('<div>');
breadcrumb.addClass('breadcrumb');
breadcrumbList=$('<ul>');
breadcrumbList.addClass('breadcrumb_list');
breadcrumbList.appendTo(breadcrumb);
// Always add homepage icon
var homeIcon=$("<img>");
homeIcon.attr('alt','Homepage');
homeIcon.attr('src','../../public/images/home.png');
addCrumb('index.html', homeIcon, 'breadcrumb_first');
breadcrumb.prependTo($('body'));
}
function addCrumb(link, content, className) {
var newListItem=$('<li>');
if (className != undefined)
newListItem.addClass(className);
var newLink=$('<a>');
newLink.attr('href', link);
content.appendTo(newLink);
newLink.appendTo(newListItem);
newListItem.appendTo(breadcrumbList);
crumbItems.push(newListItem);
}
function addTextCrumb(linkHref, linkText) {
var newLink=$('<a>').text(linkText);
addCrumb(linkHref, newLink, 'breadcrumb_text');
return newLink
}
/***
* Set the "variable" crumb - i.e. the page content crumb
*/
function setVariableCrumb(linkHref, linkText) {
if (variableCrumb == undefined) {
variableCrumb=addTextCrumb(linkHref, linkText);
} else {
variableCrumb.text(linkText);
variableCrumb.attr('href', linkHref);
}
}
You have to understand how JavaScript scope works. There is global and local scope. Meteor wraps all *.js files by function (closure):
(function() {
//local function
function yourFunction() {
//code
}
})()
So your functions becomes local ones.
If you want to define global function in closure you need to define it as global variable:
(function() {
//global function
yourFunction = function() {
//code
}
})()
You have to change the function definition code.
initBreadcrumb = function(){...};
addCrumb = function(link, content, className){...};
addTextCrumb = function(linkHref, linkText){...};
...
Then your function can be run in your template code.
This will make your function to global.
Related
How to solve this error?
===plugin.js
(function($) {
$.fn.myjqueryplugin = function(options) {};
$.fn.func1 = function() {
return this;
};
}(jQuery));
===base.js
$(document).ready(function() {
my_plugin = $("#myid").myjqueryplugin();
});
===hello.js
$(document).ready(function() {
my_plugin.func1();
});
===BUILD file
Properly define 3 js files with correct order.
Closure compiler error on hello.js
[JSC_POSSIBLE_INEXISTENT_PROPERTY] Property func1 never defined on *
How Closure compiler is able to find 'func1'?
I have an EventListener that listens to the entire document and records keystrokes, but I want to remove this Listener when certain conditions are met.
The following is a snippet of my code:
document.addEventListener('keyup', function(e) {
var letter_entered = String.fromCharCode(e.keyCode).toLowerCase();
player.makeGuess(letter_entered);
if(player.win_status === true || player.lose_status === true) {
document.removeEventListener('keyup', arguments.callee, false);
}
});
This works, however according to the Mozilla Developer Docs this method has been deprecated.
I'm aware that I can simply name the function, but is there an alternative that would allow me to continue using the unnamed function?
Use the following process:
Create a variable
Assign an anonymous function to the variable
Invoke it with the variable reference
The anonymous function references itself using the variable name
Use it as such:
var foo = function(e)
{
"use strict";
console.log(e);
document.removeEventListener('keyup', foo, false);
}
document.addEventListener('keyup', foo);
You can solve this problem easily using the y combinator:
function y(f) {
return function () {
return f.bind(null, y(f)).apply(this, arguments);
};
}
Now you can rewrite your code as follows:
document.addEventListener("keyup", y(function (callee, e) {
player.makeGuess(String.fromCharCode(e.keyCode).toLowerCase());
if (player.win_status || player.lose_status) document
.removeEventListener("keyup", callee);
}));
That's all folks.
Use another anonymous function as a wrapper to store a named function (callee shim) to your original function:
document.addEventListener('keyup', (function(e)
{
var aFunction = function()
{
var letter_entered = String.fromCharCode(e.keyCode).toLowerCase();
player.makeGuess(letter_entered);
};
if(player.win_status === true || player.lose_status === true)
{
document.removeEventListener('keyup', window, false);
}
else
{
aFunction();
}
}
), false);
References
Strict Mode is Coming to Town
ES3.1:arguments.callee
Recursion with anonymous (inline) functions in XPath 3.0
Recursion with anonymous (inline) functions in XPath 3.0, Part II
I'm looking at how to turn off reactivity within a template helper function. I only want the data rendered when the template is initially populated and not when the underlying data changes.
My current helper has two reactive variables: one is a Session.get() and the other is a Collection.findOne(). I've tried to wrap the Session.get() in a Tracker.nonreactive() call and set the reactive option to false on the Collection.findOne() but I'm still seeing the reactive behavior.
Template.characterSkills.helpers({
data : function () {
var characterID = Tracker.nonreactive(function () {
return Session.get("currentCharacterID");
});
if(characterID) {
var record = Characters.findOne(
{ _id : characterID },
{
reactive : false,
fields : { skills : 1 }
}
);
if(record && record.skills)
return record.skills;
}
}
});
I've been trying to work this issue for about half a day now. Any help would be greatly appreciated.
You're using the name data for your helper which is reserved. So if you have another helper or an iron router route with data in it, or this template is a subtemplate of another template its likely to redraw too.
Try renaming it to something else.
You can use a closure to find and set the record when the template is rendered and then return a static object in the template helper:
Template.characterSkills.helpers({
dataItem: function(){
return record.get();
}
});
Template.characterSkills.onCreated(function(){
record.set(Session.get("currentCharacterID"));
});
var record = function(){
var local = {};
return {
set: function(characterId){
if ( characterID) local = Characters.findOne({_id: characterID},{fields : { skills : 1 }});
else local = null;
},
get: function(){
return local;
}
}();
Although even this feels like too much work. Surely there's an easier way.
I've been having a problem using an RSS parser in meteor. It's an async call, so it needs ot be wrapped, however it still doesn't seem to work. I presume this is because the anonymous on('readable' function is outside the fiber, but I can't see how to resolve it.
var FeedParser = Meteor.require('feedparser');
var request = Meteor.require('request');
function getBlog(url, parameter, id){
request(parameter)
.on('error', function (error) {
console.error(error);
})
.pipe(new FeedParser())
.on('error', function (error) {
console.error(error);
})
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
});
}
var wrappedGetBlog = Meteor._wrapAsync(getBlog);
Meteor.methods({
blog: function (url, parameter, id) {
console.log('parsing blog');
var items = wrappedGetBlog(url, parameter, id);
}
});
Meteor._wrapAsync() expects the wrapped function to return error and result to a callback. Your function, getBlog(), does not do that so _wrapAsync is not the right approach.
I have wrapped that function before but used a Future.
That approach allowed me to call feedparser from a Meteor.method(), which doesn't allow async functions, but you are also trying to do an insert inside the readable event. I think that insert will complain if it is not in a fiber. Maybe like this would also be necessary:
var r = request( parameter );
r.on( 'response' , function(){
var fp = r.pipe( new FeedParser() ); //need feedparser object as variable to pass to bindEnvironment
fp.on('readable', Meteor.bindEnvironment(
function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
}
, function( error ){ console.log( error );}
, fp // variable applied as `this` inside call of first function
));
});
Fibers is another option...
var Fiber = Npm.require( "fibers" );
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Fiber( function(){
Items.insert(new_item);
Fiber.yield();
}).run();
}
});
UPDATED
NOW I try to do this in my app (thx to Akshat)
//common
LANG = 'ru';
Dictionary = new Meteor.Collection("dictionary");
//if server
Meteor.startup(function () {
if (Dictionary.find().count() === 0) {
// code to fill the Dictionary
}
});
Meteor.publish('dictionary', function () {
return Dictionary.find();
});
//endif
//client
t = function(text) {
if (typeof Dictionary === 'undefined') return text;
var res = Dictionary.find({o:text}).fetch()[0];
return res && res.t;
}
Meteor.subscribe('dictionary', function(){
document.title = t('Let the game starts!');
});
Template.help.text = t('How to play');
//html
<body>
{{> help}}
</body>
<template name="help">
{{text}}
</template>
Still doesn't work as we wanted: when templates are rendered Dictionary was undefined. Butt('How to play') in console works perfectly )
Javascript variables aren't shared between the client and server reactively. You have to use a Meteor Collection to store your data e.g
if (Meteor.isServer) {
var Dictionary = new Meteor.Collection("dictionary");
if(Dictionary.find().count() == 0) {
//If the 'dictionary collection is empty (count ==0) then add stuff in
_.each(Assets.getText(LANG+".txt").split(/\r?\n/), function (line) {
// Skip comment lines
if (line.indexOf("//") !== 0) {
var split = line.split(/ = /);
DICTIONARY.insert({o: split[0], t:split[1]});
}
});
}
}
if (Meteor.isClient) {
var Dictionary = new Meteor.Collection("dictionary");
Template.help.text = function() {
return Dictionary.find({o:'Let the game starts!'});
}
}
In the above i'm assuming you have the autopublish package in (its in by default when you create a package so this shouldn't really bother you, but just in case you removed)
With your document title you would have to use a slightly different implementation because remember the data wouldn't be downloaded at the time Meteor.startup is run, since the html and javascript are downloaded first & the data is empty, then the data comes in slowly (and then reactively fills the data up)