Meteor - Passing data between helpers, events and templates - javascript

I'm not sure how to approach my issue. Whether I should set my variables in my event to my spacebars syntax fields from my template or pass my returned event data into my helpers somehow. Everything is published and subscribed properly too.
Problem: I am trying to make it so users can click an "Add" button next to any of the people in a directory list (DirectoryList collection) and have that users information be added to a Contacts List collection. Its simply a messaging app where a person can scroll a directory of everyone and add users to their contacts list. Here are my files:
templates > directoryList.html
<template name="directoryList">
{{> searchDirectory}}
<ul>
{{#each directoryList}}
<li>
{{firstname}} {{lastname}} <button name="addFriend" id="addFriend">Add</button>
</li>
{{/each}}
</ul>
</template>
helpers>directoryList.js
Template.directoryList.helpers({
'directoryList': function(){
return DirectoryList.find({}, {sort: {createdAt: -1}});
}
});
events>directoryList.js
Template.directoryList.events({
'click .addFriend': function(event){
event.preventDefault();
var currentUserId = Meteor.userId();
var currentContact = DirectoryList.findOne(this._id);
var currentContactFirstname = currentContact.firstname;
var currentContactLastname = currentContact.lastname;
var currentContactEmail = currentContact.email;
console.log("test");
ContactsList.insert({
firstname: currentContactFirstname,
lastname: currentContactLastname,
email: currentContactEmail,
createdBy: currentUserId
});
}
});
its obviously throwing me an error for the {{}} syntax in my event but i dont know what else to do or how to get this to work. Thought it might be able to inherit from the template those fields but i guess not?

You event handler is for addButton class, where you button doesn't have addButton class. You need to change id to class in your template.
<template name="directoryList">
{{> searchDirectory}}
<ul>
{{#each directoryList}}
<li>
{{firstname}} {{lastname}} <button name="addFriend" class="addFriend">Add</button> <!-- CHANGED ID TO CLASS FOR THIS BUTTON -->
</li>
{{/each}}
</ul>
</template>
And you can follow other answer to avoid unnecessary query to improve performance.
Hope it helps.

In your event handler this already contains the current contact, you don't need to look it up again. You can simplify your event handler down to:
Template.directoryList.events({
'click .addFriend': function(event){
event.preventDefault();
console.log(this);
ContactsList.insert({
firstname: this.firstname,
lastname: this.lastname,
email: this.mail,
createdBy: Meteor.userId()
});
}
});

Related

How to pass a variable to a helper and display its in the template?

I'd like to collect a variable(user _id)collected from a template, and pass it to another template using session. Then I want to display this variable.
Actually it seems to work in the collection of the variable and the pass to the other template, but I'm not able to display the user's info in the second template...
This is my code:
HTML
<template name="main"><!--Template 1-->
<table>
<tr>
<th>Patient</th>
</tr>
{{#each allUsers}}
<tr>
<th><label><input type="radio" class="selected" name="patient" value="{{this._id}}"><i class="fa fa-user"></i> {{this.profile.lastName}} {{this.profile.firstName}}</label></th>
</tr>
{{/each}}
</table>
{{>test}}
</template>
<template name="test"> <!--Template 2-->
<p>Name is <button class="test" name="patient" value="{{this._id}}">Test</button></p>
<div name="show">Name: {{this.profile.firstName}}</div>
</template>
JS
Template.main.events({
'click .selected': function (){
var selPat = $('input[name="patient"]:checked').val();
Session.set("selPat",selPat);
console.log("collect", selPat);
}
});
Template.test.events({
'click .test': function(){
var PAT= Meteor.users.findOne({ _id: Session.get("selPat")});
console.log("pass", PAT);
return PAT;
}
});
Template.patients.helpers({
allUsers: function() {
return Meteor.users.find({});
}
});
I want to display in the template 2 the first name of the user selected in the template 1 with {{this.profile.firstName}}
I believe this is what you are doing:
You are choosing patient's id from a list of patients via the radio buttons in the main template. [this implementation is correct]
You are setting the patient id in a session in the main template's events. [this implementation is correct]
When you click the "test" button in the test template, it should reveal the user's first name in the div below the button. [...not quite]
You are unable to display anything in <div name="show">Name: {{this.profile.firstName}}</div> because you don't have a relevant helper supplying the template with that information.
Although clicking a button to reveal the patient's firstName in the test template sounds a bit redundant, I'm sure you have some reason to do it in that manner.
I propose that you wrap the div inside an if block. The if condition renders true, when the button is clicked, and hence the div element is shown.
<template name="test"> <!--Template 2-->
<p>Name is <button class="test" name="patient" value="{{this._id}}">Test</button></p>
{{#if isButtonClicked}}
<div name="show">Name: {{data.profile.firstName}}</div>
{{/if}}
</template>
Your helpers and events will be like so:
Template.test.events({
'click .test': function(){
// set this session to true when the button has been clicked.
Session.set("testClicked", true);
}
});
Template.test.helpers({
isButtonClicked: function(){
// return the if block's validation.
return Session.get("testClicked");
},
data: function(){
// return the actual user data retrieved from the collection.
var PAT= Meteor.users.findOne({ _id: Session.get("selPat")});
console.log("pass", PAT);
return PAT;
});
Note:
You might want to make sure that the div does not stay open when you select a different patient from the list of radio buttons. Not doing so will make the div be visible when you first click the button, and remain open until you refresh the page, even when you select a different patient.
You could set testClicked to false or undefined in Template.main.events --> 'click .selected'

Meteor: Render a template to the DOM on click

I am having a seemingly simple problem that I cannot really find a solution to. I have 2 columns: One with an overview of different tasks, and one with an area where detailed information about a task should be displayed when the "More information" button attached to each task is clicked. My logic is:
Have 2 templates: task_short and task_long
When the button in task_short is clicked use Blaze.render to render task_long to a div in the second column.
when "More information" is clicked on another task_short, use Blaze.remove to remove the view.
My main problem is: How do I tell Meteor which task should be render in task_long? task_short gets its {{content}},{{name}} etc parameters through the each tasks loop. But how do I do it with a single task? Also, I don't really understand Blaze.remove. Where do I get the ViewId from that needs to be passed in?
I am insanely grateful for any help!
This can be solved with a session variable and some conditionals in your template. You shouldn't need to use Blaze.render/Blaze.remove unless you are doing something really fancy. I don't know exactly how your template is structured, but this example should give you and idea of what to do:
app.html
<body>
{{#each tasks}}
{{> task}}
{{/each}}
</body>
<template name="task">
<div class='short'>
<p>Here are the short instructions</p>
<button>More information</button>
</div>
{{#if isShowingLong}}
<div class='long'>
<p>Here are the long instructions</p>
</div>
{{/if}}
<hr>
</template>
app.js
if (Meteor.isClient) {
Template.body.helpers({
tasks: function () {
// return some fake data
return [{_id: '1'}, {_id: '2'}, {_id: '3'}];
}
});
Template.task.helpers({
isShowingLong: function () {
return (this._id === Session.get('currentTask'));
}
});
Template.task.events({
'click button': function () {
Session.set('currentTask', this._id);
}
});
}

Computed.alias not updating bind-attr

I recently started using Ember.js. In my small application I currently have problems regarding Ember.computed.alias, because an {{#if}}-section is updated properly, but the bind-attr helper in the same template is not updated accordingly.
The application controller and the action influencing the value look as follows:
App.ApplicationController = Ember.ObjectController.extend({
isEditing: false,
actions: {
toggleEdit: function() {
var a = this.get('isEditing');
this.set('isEditing', !a);
}
}
});
The controller taking care of the template causing problems:
App.CategoriesController = Ember.ArrayController.extend({
needs: ['application'],
isEditing: Ember.computed.alias('controllers.application.isEditing'),
general: function() { // example depending on the alias
var result = this.filterBy('type', 1);
if (!this.get('isEditing')) {
result = result.filterBy('isHidden', false);
}
return result;
}.property('#each.type', '#each.isHidden', 'isEditing'),
// ......
The related template:
<ul id="categories">
{{#if isEditing}}YES!{{else}}NO!{{/if}}
{{#each general}}
<li {{bind-attr class=":general isEditing:editing"}}>
{{name}}
</li>
{{/each}}
</ul>
When the action toggleEdit is triggered, the {{#if}} section is updated and swaps between YES! and NO!, but the editing class is not applied to the list element. I tried encapsulated the alias into another property of the controller depending on the alias, but without success.
I assume it's a beginners mistake, but I can't figure out what I am overlooking.
Thanking you in anticipation.
isEditing is no longer in scope, use controller.isEditing, sorry phone response
Here's an example that would keep it in scope, but I'm fully qualifying it just to show you.
{{#each item in general}}
<li {{bind-attr class=":general controller.isEditing:editing"}}>
{{item.name}}
</li>
{{/each}}

How to pass parameters with the action Helper of Ember.js inside an input field from an Handlebars template?

In my handlebars template I have this loop:
{{#each itemController="fund"}}
<li>
<label>{{title}}</label>
<span>{{amount}}</span>
{{input type="text" placeholder="new user"
value=newFullName action="createUser"}}
{{partial 'user-list'}}
</li>
{{/each}}
and need to pass the current object as parameter to the 'createUser' action.
Something like this:
action="createUser(this)"
or:
action 'createUser' this
But it seems that ember can't handle parameters for actions inside an input field...
Am i missing something?
You can now pass a function, along with values -
submit=(action 'setName' 'Sal')
http://emberjs.com/blog/2015/06/12/ember-1-13-0-released.html#toc_closure-actions
I think that isn't possible to do this using the action property from input view helper.
A workaround could be wrap your input in a form that use the action view helper using the submit event, like the following:
Template
{{#each}}
<li>
<form {{action "createUser" this on="submit"}}>
{{name}}
{{input type="text" value=name}}
</form>
</li>
{{/each}}
Route
...
actions: {
createUser: function(user) {
alert(user.get('name'));
}
}
...
So when the user hit enter, will have the event triggered.
The main difference between the action property and the action view helper is that the action view helper is more flexible and you can supply the context and put it inside of any tag:
<div {{action "someAction" someObject}} on="click">Click me</div>
In the route:
actions: {
someAction: function(someObject) {
// do something with the someObject
}
}
See the docs for further information
Please give a look in the jsfiddle to see that sample in action http://jsfiddle.net/marciojunior/UAgjX/
I hope it helps
Finally i ended up with this solution:
Template
{{input class="newUser" type="text" value=newFullName placeholder="add user"}}
<button {{action 'createUser' this newFullName}}>Send</button>
Controller
createUser: function (fund, newFullName) {
var fullName = newFullName;
var user = this.store.createRecord('appUser', {
fullName: fullName,
fund: fund,
payments:[]
});
user.save().then(function(){
fund.get('users').pushObject(user);
fund.save().then(function(){
fund.reload();
});
});
}
You can pass a parameter to an action helper :{{action "doSomething" xxx }}
Where doSomething is your controller method ,and xxx is anything in the current context of the template.

this in event handling functions of Meteor: How does this get bound to model object?

The following code is taken from a tutorial in tutsplus.
if (Meteor.isClient) {
var Products = new Array(
{ Name: "Screw Driver",
Price: "1.50",
InStock: true},
{ Name: "Hammer",
Price: "2.50",
InStock: false}
);
Template.Products.ProductArr = function () {
return Products;
};
Template.Products.events = {
"click .Product": function () {
if (this.InStock)
confirm("Would you like to buy a " + this.Name + " for " + this.Price + "$");
else
alert("That item is not in stock");
}
};
}
Here is the template:
<template name="Products">
{{#each ProductArr}}
<div class="Product">
<h2>{{Name}}</h2>
<p>Price: ${{Price}}</p>
{{#if this.InStock}}
<p>This is in stock</p>
{{else}}
<p>This is sold out</p>
{{/if}}
</div>
{{/each}}
</template>
I wonder how this get bound to the model object product? This looks like magic to me.
The expression "click .Product" specifies that the click event on HTML elements having class Product should trigger the specified function. I understand it. But I don't understand why this is bound to an element of the Products array.
This is how Handlebars (which Meteor builds on) works. What you're seeing in the template isn't pure JS, but syntax specific to Handlebars.
Inside the each block helper, the context is to set each element of the array you're iterating over. So if you use InStock, it will look for it on the element of the current iteration.
The this keyword is used for disambiguation. This comes in handy if you have a general helper registered with the name InStock, like this, for example:
Template.Products.InStock = function (){
//...
};
But you want to make sure you're referring to the property of the element from the array, so you can use this to access its context explicitly.

Categories

Resources