This.data from #each-iteration - meteor

I'm trying to access a value inside an {{#each in}}-iteration:
{{#each room in channels}}
<form class="enterRoom">
<button type="submit" class="roomJoin">
<b>{{room.name}}</b>
<img src="{{room.roomBanner}}" alt=".">
<input type="hidden" value="{{room.name}}" name="name">
</button>
<div class="inRoom">
{{#each name in room.inRoom}}
{{name}}
{{/each}}
</div>
</form>
{{/each}}
Normally I would use this.name, for example, to get the name of it inside an event to use it further, like so
'submit .enterRoom'(event) {
event.preventDefault();
const isClosed = this.name; // room.name example here
}
But this doesn't work in this scenario. What I tried before was:
room.name
this.room.name
But those give the same error
chat.js:86 Uncaught ReferenceError: room is not defined
at Object.submit .enterRoom (chat.js:86)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3818
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3769)
at Blaze.View.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3817)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2617
at Object.Blaze._withCurrentView (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2271)
at Blaze._DOMRange.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2616)
at HTMLFormElement.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:863)
at HTMLDivElement.dispatch (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9685)
at HTMLDivElement.elemData.handle (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9492)
Could someone explain to me how I could do it in this {{each in}}-setting properly?

The error has nothing to do with the each iterations of your template. What you try is to get the form data within the submit event handle. However, there is no context bound to this or room.
In order to get the room value, you need to access the input value.
Blaze offers a fast way of doing so, by using the Template's builtin jQuery (using templateInstance.$), which automatically scopes to the Template root instead of the whole document:
'submit .enterRoom'(event, templateInstance) {
event.preventDefault();
const roomName = templateInstance.$(event.currentTarget).find('input[name="name"]').val();
// ...
}

Related

meteor template argument value differs between helper and event

I want to include a Blaze template with an argument and then use the argument value in an event. The problem is that when I include the template a second time with a different argument I get the argument value from the first instance of the template in events.
Template:
<template name="UploadFormLayoutImage">
<form class="uploadPanel">
<input type="file" name="fileupload" id="input-field">
<label for="input-field">Upload file</label>
</form>
</template>
Include:
{> UploadFormLayoutImage layoutArea="area1"}}
{> UploadFormLayoutImage layoutArea="area2"}}
js:
Template.UploadFormLayoutImage.onCreated(function(){
this.currentArea = new ReactiveVar;
this.currentArea.set(this.data.layoutArea);
});
Template.UploadFormLayoutImage.helpers({
layoutArea: function() {
return Template.instance().currentArea.get(); //Returns the correct argument value for each instance of the template.
}
});
Template.UploadFormLayoutImage.events({
'change input[type="file"]': function(e, instance) {
e.preventDefault();
console.log(instance.data.layoutArea); //Allways returns 'area1'
}
});
What am I missing here? (This is my first Stackoverflow question. Please be gentle :))
What if you change the instance.data.layoutArea in your events method to this.layoutArea?
In my effort to make the code example easy to read i stripped away the part that caused the problem. I'm using a label for the input field and therefore the input field has an id and thats of course not ok when repeating the template.
I now use the layoutArea-helper as an id value and every thing works just fine.
<template name="UploadFormLayoutImage">
<form class="uploadPanel">
<input type="file" name="fileupload" id="{{layoutArea}}">
<label for="{{layoutArea}}">Upload file</label>
</form>
</template>

Meteor helper not called when Session changes

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.

Pass context to yield when using Meteor Iron Router

I am starting to use Iron Router in my Meteor app and its yields for templating.
I've recently run into a problem where I can't start a named yield with a context, as follows:
{{#with context}}
{{yield 'subtemplate'}}
{{/with}}
and get this error Sorry, couldn't find a yield named "subtemplate". Did you define it in one of the rendered templates like this: {{yield "subtemplate"}}?
If I remove the {{#with}} block expression, I am able to render the yield.
Does anyone know of a good way to pass the context to a named yield?
I have posted my problem as an issue on the iron-router github project, but haven't gotten any solution yet.
Would appreciate any help.
EDIT 1/1/2014:
So my code looks like this:
// main template for the route
<div class="form-container">
<div class="form">
{{yield 'section'}}
</div>
</div>
The logic to get the yield section to display
// router.js
ApplyController = RouteController.extend({
before: function() {
var section = this.params.section || 'personal-info';
Session.set('current', section);
},
action: function() {
var section = Session.get('current');
this.render();
this.render(section, {
to: 'section'
});
},
data: function() {
return {
app: Applications.findOne({user: Meteor.userId()})
}
}
});
Example of one of the section template:
<template name="education">
{{#with app}}
<form id="education" name="education" class="fragment" method="post" action="">
<h2>Education</h2>
<div class="form-group">
<label for="college" class="control-label">College/ University</label>
<select class="form-control" id="college" name="college" placeholder="Select a College/ University">
<option value="">Select a College/ University</option>
{{#each colleges}}
<option value="{{slug}}" {{selected slug ../college}}>{{name}}</option>
{{/each}}
</select>
</div>
<!-- other content here -->
</form>
{{/with}}
</template>
Using the {{#with app}} block is how I currently get around this issue, but because I have 10 different section templates, I have to put that in all of them.
You pass a data context in the router using ironrouter. You can't pass it this way because if you pass a route in the router it would override the route's data context.
It might however work with the shark branch of ironRouter which is based off Meteor UI since it uses {{>yield}} instead of {{yield}}.
You can use this though:
Route specific data context
Router.map(function() {
this.route('template', data: function() { return Something.find() });
});
You basically pass the context using the data param. It might be easier to do it this way than to use {{#with context}} because you can use more dynamic data which is different for each route.
You might have tried this, I'm a bit unsure on whether it would go to a named yield's template.
Using an ordinary Template helper for the template
Template.templateInYieldName.helper = function() {
return Something.find();
}
Then you can use something like {{helper.name}} in your named yield.
Global data context with handlebars helper
If you intend to use data for all the routes you can use a Handlebars global helper. i.e
Handlebars.registerHelper('todaysDate', function() {
return (new Date).toString();
});
then just use {{todaysDate}} in any of your templates. You can use your data instead of a date instead.

Content wrapped in currentUser re-rendering when user updated

I'm using Meteor and having an issue where my content is being re-rendered when I don't want it to.
I have my main content wrapped in a currentUser if statement which I feel is fairly standard.
{{#if currentUser}}
{{> content}}
{{/if}}
The problem with this is my content template is being re-rendered when I update my user object. Is there any way around this? I don't reference users anywhere inside the content template.
Thank you!
Here's a sample app to replicate my problem:
HTML
<head>
<title>Render Test</title>
</head>
<body>
{{loginButtons}}
{{> userUpdate}}
{{#if currentUser}}
{{> content}}
{{/if}}
</body>
<template name="userUpdate">
<p>
<input id="updateUser" type="button" value="Update User Value" />
User last update: <span id="lastUpdated">{{lastUpdated}}</span>
</p>
</template>
<template name="content">
<p>Render count: <span id="renderCount"></span></p>
</template>
JavaScript
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set("contentRenderedCount", 0);
});
Template.content.rendered = function() {
var renderCount = Session.get("contentRenderedCount") + 1;
Session.set("contentRenderedCount", renderCount);
document.getElementById("renderCount").innerText = renderCount;
};
Template.userUpdate.events = {
"click #updateUser": function() {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastActive: new Date()}});
}
};
Template.userUpdate.lastUpdated = function() {
return Meteor.user().lastActive;
};
}
if (Meteor.isServer) {
Meteor.users.allow({
'update': function () {
return true;
}
});
}
Update:
I should've explained this example a little. After creating a user, clicking the Update User Value button, causes the render count to increment. This is because it's wrapped in a {{#if currentUser}}. If this is if is removed, you'll notice the render count remains at 1.
Also, you'll need to add the accounts-ui and accounts-password packages to your project.
Meteor will re-render any template containing reactive variables that are altered. In your case the {{currentUser}} is Meteor.user() which is an object containing the user's data. When you update the users profile, the object changes and it tells meteor to re-calculate everything reactive involving the object.
We could alter the reactivity a bit so it only reacts to changes in whether the user logs in/out and not anything within the object itself:
Meteor.autorun(function() {
Session.set("meteor_loggedin",!!Meteor.user());
});
Handlebars.registerHelper('session',function(input){
return Session.get(input);
});
Your html
{{#if session "meteor_loggedin"}}
{{> content}}
{{/if}}

meteor : setting fields post opening dialog

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"));
};

Resources