We're seeing very strange Meteor behavior. After a simple event hook is executed (which gathers info from a form, executes an insert and updates a Session variable), the client seems to startup again, redrawing the entire page. In effect, Meteor.startup is being executed more than once, even though the browser window is not being refreshed (or anything like that). Even stranger is the fact that we've made extremely similar apps, but they don't display this behavior at all. We cannot detect any significant differences between the different projects.
We're using Meteor version 0.6.4.1 (in all cases), both autopublish and insecure have been removed.
Playlist.html:
<body>
{{> addSong}}
{{> playlist}}
</body>
<template name="addSong">
<form>
<fieldset>
<legend>Add a song to the playlist!</legend>
<div><input type="text" id="artist" /></div>
<div><input type="text" id="title" /></div>
<div><button type="submit" id="insertButton">Insert</button></div>
</fieldset>
</form>
</template>
<template name="playlist">
<div>Votes left: {{votes}}</div>
<ul>
{{#each songs}}
<li>
{{artist}} - {{title}} - {{score}}
<button class="voteUp" mongo_id="{{_id}}">Vote up!</button>
<button class="remove" mongo_id="{{_id}}">X</button>
</li>
{{/each}}
</ul>
</template>
lib/common.coffee
#Songs = new Meteor.Collection "songs"
Songs.allow
insert: (userID) ->
true
update: (userID) ->
true
remove: (userID) ->
true
client/client.coffee
Meteor.subscribe "songs"
Template.playlist.songs = ->
Songs.find {},{sort:{"score":-1}}
Template.playlist.votes = -> Session.get("votes")
Template.addSong.events
'click #insertButton': (event,template) ->
artist = template.find("#artist").value
title = template.find("#title").value
Songs.insert({"artist":artist,"title":title,"score":1})
votes = Session.get("votes")
Session.set "votes", votes+3
return
Template.playlist.events
'click .voteUp': (event,template) ->
id = event.target.attributes.mongo_id.value
Songs.update({_id:id},{$inc:{"score":1}})
'click .remove': (event,template) ->
id = event.target.attributes.mongo_id.value
Songs.remove({_id:id})
Meteor.startup ->
alert "Starting"
Session.setDefault "votes", 0
server/server.coffee
Meteor.publish "songs", -> Songs.find({})
To replicate the weird behavior, just submit items on the form, this triggers a startup every time (verified in Chrome as well as Safari).
If you have the code on github, I can help to look at it, but with just your description, it's hard to say what the problem would be...
OK. We've found out what the problem is. The <form> tag, in combination with a submit button caused the client to execute a HTTP POST, which cause the entire page to re-render.
It's a very subtle problem and quite hard to detect, but the lesson here is that you shouldn't use <form> tags unless you are absolutely certain. Some kind of mechanism/warning/documentation might be a good idea to prevent this from happening to other people.
Related
projects.html
{{#if projects}}
{{#each projects}}
<div class="project-item">
<div class="project-name">
{{name}}
</div>
<div class="project-settings">
<span class="rename">Rename</span>
<span class="edit">Edit</span>
<span class="delete">
<!-- Here -->
</span>
</div>
</div>
{{/each}}
{{/if}}
projects.js
Template.Projects.events({
"click .project-item .delete": function (e, template) {
e.preventDefault();
debugger
// "this" refers to the specific project
}
});
In an event handler, I noticed "this" conveniently refers to a specific object inside the template where the event is related to. For example, in this case, the delete button is inside each projects block, and the handler for the delete button has this = some project. This is convenient, but I'd like to know the scopes and rules more completely. Can someone explain in briefly and point me to the right document?
This is a data context sensitive feature. Basically, there is a lexical scope in spacebars helpers. Have a look at this: http://devblog.me/no-data-context.html
The original pull request is here: https://github.com/meteor/meteor/pull/3560
The following calls messages when the page loads but not when a Session value is changed. Here is the template code;
<head>
<title>Your welcome to chat</title>
</head>
<body>
{{> chat}}
</body>
<template name="chat">
<div class="ui list" style='height: 100px; overflow:scroll;' id='chatHistory'>
{{> currentChatList}}
</div>
<div class="ui large icon input">
<input type="text" class="field" id="message" placeholder="Type your message here" />
<i class="comment icon"></i>
</div>
</template>
<template name="currentChatList">
{{#each messages}}
{{/each}}
</template>
Here is the code;
if (Meteor.isClient) {
Template.chat.events ({
'keydown input#message' : function (event) {
if (event.which == 13) { // 13 is the enter key event
Session.set('time', new Date());
}
}
});
Template.currentChatList.helpers({
messages : function () {
debugger;
return [];
}
});
}
debugger statement is hit when the page loads, but not when the enter key is pressed on the textbox and Session.set('time'..) is executed. I thought a Session value change would cause the template to render.
saimeunt's answer will solve you problem. Your helper at the moment doesn't access any reactive variable (Session or Collection), which means it's never executed again.
Recently more and more people prefer to use reactive-var:
The idea is that you declare a reactive variable within your JS file and then USE that reactive variable in your helper. Every time you change the variable, all helpers using this variable are re-executed.
You do not depend on the Session variable you're assigning anywhere on your code. A reactive computation such as template helpers only reruns if one of its dependent reactive data sources is modified, which is clearly not the case in your example.
Try to depend on the Session variable inside your helper code to make it re-execute whenever you press enter.
Template.currentChatList.helpers({
messages: function () {
debugger;
var time = Session.get("time");
console.log("You pressed enter at",time);
return [];
}
});
FINALLY THE ANSWER: OMG.
https://blog.meteor.com/the-meteor-chef-reactive-dict-reactive-vars-and-session-variables-971584515a27
I have tried window.someVariableThatChanges = NOT WORKING
I have tried a Session.someVariableThatChanges = NOT WORKING
REACTIVE VARIABLE -- TOTALLY WORKING.
The difference is that you need to create a Template ON CREATED event, add your variables there. Simply use this variable in your even that changes the value, and your helper reference -- it works like magic and feels soooooo goooooood.
Going through Your First Meteor Application and I'm stuck. I'm in Chapter 9 "Forms" at the section titled "The Event Object, Part 1."
I can't get anything to log to the console.
Here's where I'm at via MeteorPad:
Source code (I've made a comment saying "THIS IS WHERE I'M STUCK" on line 46 of common.js)
You are missing the <form> tag.
This is how the insert event should look.
Template.addPlayerForm.events({
'submit form': function(event,template){
event.preventDefault();
PlayersList.insert({
name:template.$('#newPlayer').val(),
score:8,
})
return false;
}
});
And the HTML.
<template name="addPlayerForm">
<form>
<input type="text" name="playerName" id="newPlayer">
<input type="submit" value="Add Player">
</form>
</template>
Here is the same meteorpad but with the above changes.
I was just playing around a bit with Meteor.js when I ran into this strange issue, I have a form with two textfields, but somehow my event is not listening to the submit.
When I remove one textfield, everything works fine ...
Below is my template for the form:
<template name="new_timer">
<div class="timer timer--empty">
<form id="new_timer">
<input type="text" name="timer__name" class="timer__name" placeholder="Timer name">
<input type="text" name="timer__description" class="timer__description" placeholder="Timer description">
</form>
</div>
</template>
And on the client side:
Template.new_timer.events({
'submit form': function(e) {
console.log('new timer');
e.preventDefault();
}
})
This doens't seem to work, however when I change my template to the following, it works
<template name="new_timer">
<div class="timer timer--empty">
<form id="new_timer">
<input type="text" name="timer__name" class="timer__name" placeholder="Timer name">
</form>
</div>
</template>
Am I just overlooking something very basic here?
You might add an event like
'keyup form': function(e) {
if (e.keyCode === 13) {
// do something
}
}
Basically using a submit in a single page application is not adapted. In this kind of application everything is event based, you never reload a page so you never really 'submit' a form.
The 'form' tag becomes useless, most of developers (including me) are keeping it by habit but it is not required.
It is a bit late for an answer, I hope it can help somebody else!
I had similar problem, submit event does not work with more inputs without this:
<input type="submit" hidden="hidden">
I'm trying to complete the parties demo
with an "edit party" feature
I understood the create Dialog opens upon setting Session showCreateDialog
{{#if showCreateDialog}}
{{> createDialog}}
{{/if}}
this shows the popin
but I want to set to fields post opening
and I don't see how to act after the opening action ?
You can set manipulate the DOM inside the Template's rendered event. But if you find yourself writing lots of glue code here ($("#someInput").val("someVal")) then watch out because you're likely on the wrong track!
Template.createDialog.rendered = function() {
// you can manipulate the DOM here
}
Remember, you can bind field values to instances, so something like the below will auto-bind your object
<template name="editDialog">
{{#with party}}
<input type="text" id="myPartyName" value="{{name}}" />
...
{{/with}}
</template>
Template.editDialog.party = function() {
return Parties.findOne(Session.get("selectedParty"));
};