Meteor callback on some template render - javascript

In my meteor project, I have a code like this:
baz = function() {
// some jQuery add/remove class here...
};
Template.foo.onRendered(function() {
baz();
});
Template.bar.onRendered(function() {
baz();
});
Template.qux.onRendered(function() {
// no baz() call
});
Is there a better way to accomplish this task without repeat baz(); on some template render?

Meteor 1.2.1 allows you to run a global onRendered() function via the following code:
Template.onRendered(function() {
var that = this; //pass that into baz() if you need it
Deps.afterFlush(function() {
console.log('baz');
baz();
});
});
If that doesn't fit your needs, and you want it on every page, just use onRendered() within some common template like the menu or page header, however this will not guarantee that the HTML you are attempting to alter with JQuery will be rendered.

Similar to #Brett answer, you could also use the Template.body.onRendered(function(){..
Each onRendered function is executed only once per template (when it loads).
The body (implicit) template, being the main container(parent) for the other templates(childs) will be executed on each page request.

Related

what do you call this design pattern Polymer and Backbone use for parameters

In Polymer and Backbone.js I was digging through the source to find out how they organize their API's for the user.
Currently I'm using a revealing pattern and runs NAMESPACE.init(args) but I liked the way those two libs are making you set everything in the ready or attached methods as callback functions but I don't know the name to do some more research on it with.
Example: I call Polymer and assign something into its ready method as a callback function, I assume it's just overwriting an internal ready method and it stores it for later.
Backbone looks mostly the same
Polymer({
ready: function() {
console.log("im ready like!")
}
});
Perfect I guesss..
using underscore.js extend function rather then jquerys in this example but I guess jquery would work all the same.
Tacos = function (args) {
this.commands = {
popcorn : function(){
console.log("pop'n fun!");
},
render : function(){
vars.mine = args.cat;
}
}
// using underscore.js _.extend function
// this merges the commands and replaces the popcorn function with
// the users function! Fun!
_.extend(this.commands, args);
}
var u = new Tacos({
popcorn : function(){
console.log("popcorn denied a tron! ");
}
});
u.commands.popcorn()// should return "popcorn denied a tron!"

Running JavaScript inside a Handlebars Template

I want to execute a bit of Javascript code inside my handlebars template. Typically in the application I do this
<script type="text/javascript">
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if(Model.OnChangeJavascript.IsNotNullOrEmpty()) {
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
But since I am already inside of a handlebars template with I tried to just insert the $(function(){}) that just gets spit out as text which makes sense. So how then can I create a bit of dynamic Javascript inside of handlebars???
<script type="text/x-handlebars-template" id="tagsTemplate">
<div>Tags</div>
var #Model.JavascriptVariableName;
$(function () {
#Model.JavascriptVariableName = new TagInput()
.withAvailableTags(#Html.Raw(Model.AvailableTagsJson))
.withAppliedTags(#Html.Raw(Model.AppliedTagsJson))
.withMinCharsAutocomplete(#Model.MinCharsAutocomplete)
.allowBackspaceDelete(#Model.DeleteWithBackspace.ToString().ToLowerInvariant())
.allowNewTags(#Model.AllowNewTags.ToString().ToLowerInvariant())
.initialize($('##Model.ElementId'), $('##(Model.ElementId)_hidden'));
#if (Model.OnChangeJavascript.IsNotNullOrEmpty())
{
#:#(Model.JavascriptVariableName).onChange = function () { #Html.Raw(Model.OnChangeJavascript) }
}
});
</script>
The above code wont work.
Unless you are using handlebar to compile your webpage server side I don't see why you need to wait every time for the window Ready event: $(function(){}) in fact wrap a function to make it sure it will fire only when the DOM is ready (has been loaded).
You can simply skip that part in your code if you want.
I would strongly discourage to put JS code in a template: why don't you generalize that code and compile with Handlebars some DOM stuff with ids or classes that you can use in your "generic" function instead?

Meteor: Template is displayed although collection is not loaded yet

Hi fellow Meteor friends!
Please note: I am using Tom's router!
So I'm trying to only display my template when the mongo collection is ready but for some reason it does not work! :(
I first followed this post: LINK
So I have my publish functions in the server.js and I subscribe to these functions inside my router, so no Deps.autorun() involved here (btw: is this the right approach? Deps.autorun() did not work for me properly):
So I have something like:
'/myroute': function(bar) {
Meteor.subscribe("myCollection", bar, function() {
Session.set('stuffLoaded', true);
});
return 'stuffPage';
}
In the template, where the data loaded from "myCollection" is displayed, I will have something like this:
<template name="stuffPage">
{{#if stuffLoaded}}
<!-- Show the stuff from the collection -->
{{else}}
<p>loading!</p>
{{/if}}
</template>
For some reason "loading!" is never displayed.
Also, for a couple of milliseconds, the "old data" from the last time the same template was displayed (but with another "bar" value provided to the publish function --> different data) is displayed.
This of course is not good at all because for a couple of ms the user can see the old data and suddenly the new data appears.
To avoid this "flash" I want to display "loading!" until the new data is loaded but again: this does not work for me! :-(
What am I doing wrong?
Thx in advance for your help!
EDIT:
Ok so the problem with the answer in the first post provided by #user728291 is the following:
For some reason the router stuff get's called AFTER the Deps.autorun() ... what is wrong here? :( (please note: eventsLoaded == stuffLoaded.)
Where do you guys put your Deps.autorun() for the subscriptions or in other words: What's your code mockup for this?
I actually really think that my code mockup is just plain wrong. So how do you make different subscriptions based on the route (or in other words: based on the template which is currently shown)?
AND: Where do you put the Deps.autorun()? Inside the router.add() function? Or just inside of (Meteor.isClient)?
I think #user728291's answer is pretty spot on, I'd just add that Meteor.subscribe returns a handle that you can use to check readiness:
Keep a reference to the handle
Deps.autorun(function() {
stuffHandle = Meteor.subscribe(Session.get('bar'));
});
Then check it in your template:
{{#if stuffHandle.ready}}
...
{{/if}}
Template.barTemplate.helpers({stuffHandle: stuffHandle});
And control it via the session:
'/myroute': function(bar) {
Session.set('bar', bar);
return 'barTemplate';
}
Better to put the subscription in a Deps.autorun and use Session variable to pass arguments from the router. Also, make sure you are setting stuffLoaded to false before the subscribe runs. Otherwise it just keeps its old value.
'/myroute': function(bar) {
if ( ! Session.equals( "bar", bar ) ) {
Session.set( "stuffLoaded", false); //subscription needs to be run
Session.set( "bar", bar ); // this change will trigger Dep.autorun
}
return 'stuffPage';
}
Deps.autorun ( function (){
Meteor.subscribe("myCollection", Session.get( "bar" ), function() {
Session.set("stuffLoaded", true);
});
});
You might need some initial default values for the Session variables if you are not getting what you want on the first time the page loads.
First off, you may be missing the actual function name for the callback as demonstrated in this post.
Meteor.subscribe("myCollection", bar, function onComplete() {
Session.set('stuffLoaded', true);
});
Which seems to be great practice. I don't usually miss a beat using this method.
Secondly, I'm not sure subscriptions inside routes work well? I'd rather do the following:
'/myroute': function(bar) {
Session.set("myCollectionParam", bar)
return 'stuffPage';
}
So then the subsciption finally looks like this:
Meteor.subscribe("myCollection", Session.get("myCollectionParam"), function onComplete() {
Session.set('stuffLoaded', true);
});
OR (not sure which works correctly for you, depending on your publish function):
Meteor.subscribe("myCollection", {bar: Session.get("myCollectionParam")}, function onComplete() {
Session.set('stuffLoaded', true);
});
Good luck!
EDIT
Just mentioning something about the publish function:
While Session.get("myCollectionParam") could return null, you can ensure the behaviour a bit more by using the following publish method:
Meteor.publish("myCollection", function(myCollectionParam) {
check(myCollectionParam, String);
return MyCollection.find({_id: myCollectionParam});
});

Scope issue inside a custom object

I think I am having a scope visibility issue I can't figure out exactly: when I log the variable displayatonce I get back the right result, but as I try to use the buttons I get nothing in return. I have also tried to log this.navbuttons but all I get is an empty set... I really don't get what's wrong with this code.
<!-- html code -->
<div id="nav">
Previous
Next
</div>
/* Js Script with jQuery */
(function() {
var NewsNavigator = {
init: function(config) {
this.navbuttons = config.navbuttons;
this.displayatonce = config.displayatonce;
this.counter = 0;
this.showNews();
this.enableNav();
},
showNews: function() {
console.log(this.displayatonce);
},
enableNav: function() {
console.log(this.navbuttons);
this.navbuttons.on('click', function() {
console.log("clicked");
});
}
};
NewsNavigator.init({
displayatonce: 3,
navbuttons: $('div#nav').find('a')
});
})();
That is happening because as you are using (function())(); which executes the function immediately, maybe it's running the code before the dom is ready
everything is working fine in the below demo
DEMO
Put all your code inside document ready or at least call the initialize method inside doc ready block like
$(function(){
NewsNavigator.init({
displayatonce: 3,
navbuttons: $('div#nav').find('a')
});
});
Read more about Javascript self executing Anonymous function here
Javascript self executing function "is not a function"
or
http://markdalgleish.com/2011/03/self-executing-anonymous-functions/
You're using jQuery too soon, specifically before the DOM is ready to be searched.
Here is fiddle demonstrating this: http://jsfiddle.net/w7KaY/ (JavaScript is placed in <head>, so init() is invoked pretty early) while here (http://jsfiddle.net/w7KaY/1/), the call to init() is encapsulated in an event handler for jQuery's DOM-ready event.
Make sure the html elements are there in the DOM. I don't see any issue with the script other than the fact you have to use the bind method for binding to events.
this.navbuttons.bind('click', function() {
console.log("clicked");
});

Javascript scope issue, inside an anonymous function

Sorry I couldn't be anymore specific with the title.
I'm building a web-site (personal), which displays different content to the user depending on the query string that is used in the url.
e.g. page=home.html would display home.html
The websites Javascript is wrapped inside an object, with each value containing different data, some pseudo code:
(function(){
var wrapper = {
init: function(){
//Runs on document ready
this.foo();
this.nav.render();
},
foo: function(){
//Some functionality goes here for the website, e.g. Display something from an API
},
nav: {
//Functionality to handle the navigation, has different properties
config: {
//Contains the config for nav, e.g. page names + locations
dir: '/directory/to/content/',
pages: {
page_name: wrapper.nav.config.dir + 'page_value'
}
},
render: function(){
//some code
},
routes: function(){
//some code}
}
}
};
$(function(){
wrapper.init();
});
})();
My problem is that I'm trying to prepend the dir value to each of the page values (inside the object where the pages are defined), expecting to get the output of (in this pseudo code case) of directory/to/content/page_value, but instead dir is undefined when I'm trying to access it, I've tried the following to achieve what I want:
wrapper.nav.config.dir + 'page_value'
I've been playing around with the last 30 minutes trying to find out what I'm doing wrong, and even thought about hard-coding the URL in for each page.
The reasoning for wanting to do this is that my local development server and web host have different directory structures, so I don't want to re-write the URL's each time I want to develop + publish. As for why everything is wrapped inside an object, I thought it would be easier to maintain this way.
Hopefully the answer is simple and it's just an amateur mistake / lack of understanding.
The issue is that you can't refer to a variable that is being defined in that very definition.
So, inside the definition of wrapper, you can't refer to wrapper. And, inside the definition of config, you can't refer to config either and so on.
The usual design pattern for solving this is to initialize as much as you can in the declaration of your data structure and then do the rest in .init() when you can freely access all of it.
Change the first two lines to:
var wrapper = null;
(function(){
wrapper = {
Otherwise, the wrapper is a local variable to your anonymous function.
The problem is that you're still busy defining the wrapper when you ask for its value, which is why it's still undefined.
The code below fails too:
var x = {
y:"1",
z:x.y
}
Why not:
//...
init: function(){
//Runs on document ready
this.foo();
var config = this.nav.config;
for (var page in config.pages) {
config.pages[page] = config.dir + config.pages[page];
}
},
//...

Categories