meteor test equality of a input value to a document field - meteor

Hello: I’m using the Tutorials todo.
I’ve been trying to compare an Input value
<input type="text" name="text" placeholder="Type Or Scan to add new Name" />
To the field named ‘text’ within a Document named ‘Tasks’.
Have tried EasySearch and {{#if $eq a b}} ... {{ /if }}.
May because I’m new to Meteor not setting up <templates> correctly.
Was hoping there is a short template or helper to compare or check the values to.

You can do this using a reactive variable.
Here's some example code:
JS:
Template.yourTemplateName.onCreated( function() {
this.input = new ReactiveVar(""); // Declare the reactive variable.
});
Template.yourTemplateName.helpers({
tasks() {
return Tasks.find(); // Find all tasks as an example.
},
isInputEqualToTaskText( taskId ) {
var task = Tasks.findOne({ _id: taskId });
if( task && task.text ) {
return task.text == Template.instance().input.get();
}
}
});
Template.yourTemplateName.events({
'change input': function( event, template ) {
template.input.set(event.target.value);
}
});
HTML:
<template name="yourTemplateName">
{{#each task in tasks}}
{{#if isInputEqualToTaskText task._id}}
<p>I was equal: {{task.text}}</p>
{{/if}}
{{/each}}
<input type="text">
</template>

Related

Meteor - checkbox is maintaining old value

I am new to Meteor :)
I have a template helper:
userRoleMap: function() {
var u = getUser();
if (u) {
var lstRoles = Meteor.roles.find({}).fetch();
var userRoles = u.roles ? u.roles : [];
_.map(lstRoles, function(r) {
_.extend(r, {
userMapped: _.contains(userRoles, r.name)
});
return r;
});
return lstRoles;
}
return [];
}
I am using this helper in my template as:
{{#each userRoleMap}}
<div class="checkbox">
<label>
<input type="checkbox" class="chkUserRole" value="{{name}}" checked="{{userMapped}}"> {{name}}
</label>
</div>
{{/each}}
I am showing the above html/template in a bootstrap-modal. I am showing this modal on click of a button (and setting user-id in a Session which I am using when calling getUser() function).
The issue is the checkbox check state is not changing based on value of "userMapped". It is getting set correctly first time, but not afterwards. The helper userRoleMap is getting called every-time I open modal, but the checkboxes are having same checked state which they had when it was opened previously (manual checks/unchecks are getting maintained).
The return value of helper method is working as expected (verified by logging it on console).
Anything I am missing here ? Something to do with _.extend() ?
The Meteor way to do this is to either return checked as an element attribute or not:
Template.registerHelper( 'checked', ( a, b ) => {
return a === b ? 'checked' : '';
});
{{#each userRoleMap}}
<div class="checkbox">
<label>
<input type="checkbox" class="chkUserRole" value="{{name}}" {{checked userMapped true}}> {{name}}
</label>
</div>
{{/each}}

Reuse Meteor template as inclusion with different helpers

I'd like to re-use a Meteor template as an inclusion (i.e. using {{> }}) in two different contexts. I know I can pass in different data with the inclusion by using {{> templateName data1=foo data2=bar}}, but I'm struggling to figure out how I can provide different helpers based on the context. Here's the template in question:
<template name="choiceQuestion">
<div class="choice-grid" data-picks="{{numberOfPicks}}" data-toggle="buttons">
{{! Provide for the user to make multiple selections from the multiple choice list }}
{{#if hasMultiplePicks}}
{{#unless canPickAll}}<span class="help-block text-center">Pick up to {{numberOfPicks}}</span>{{/unless}}
{{#each choices}}
<label class="btn btn-default"><input type="checkbox" class="choice" name="{{this}}" autocomplete="off" value="{{this}}" checked="{{isChecked}}"> {{this}}</label>
{{/each}}
{{/if}}{{! hasMultiplePicks}}
{{#if hasSinglePick}}
{{#each choices}}
<label class="btn btn-default"><input type="radio" class="choice" name="{{this}}" id="{{this}}" autocomplete="off" value="{{this}}" checked="{{isChecked}}"> {{this}}</label>
{{/each}}
{{/if}}{{! hasSinglePick}}
</div>
</template>
and here's how I've reused it:
{{> choiceQuestion choices=allInterests picks=4}}
The key component of the template is a checkbox. In one context, it will never be checked. In another, it may be checked based on the contents of a field in the user document. I've added checked={{isChecked}} to the template. I've read this boolean attribute will be omitted if a falsey value is returned from the helper which should work well for my purposes.
The template's JS intentionally does not have an isChecked helper. I had hoped I could provide one on the parent where the template is included in the other context in order to conditionally check the box by returning true if the checked conditions are met, but the template doesn't acknowledge this helper.
Here's the template's JS:
Template.choiceQuestion.helpers({
hasSinglePick: function() {
return this.picks === 1;
},
hasMultiplePicks: function() {
return this.picks > 1 || !this.picks;
},
numberOfPicks: function() {
return this.picks || this.choices.length;
},
canPickAll: function() {
return !this.picks;
},
});
and the parent's JS:
Template.dashboard.helpers({
postsCount: function() {
var count = (Meteor.user().profile.posts||{}).length;
if (count > 0) {
return count;
} else {
return 0;
}
},
isChecked: function() {
return (((Meteor.user() || {}).profile || {}).contentWellTags || []).indexOf(this) > -1 ? 'checked' : null;
}
});
Template.dashboard.events({
'click .js-your-profile-tab': function(){
facebookUtils.getPagesAssumeLinked();
}
});
I've tried a few other approaches as well. I tried passing the helper to the template along with the other context (i.e. {{> templateName data1=foo data2=bar isChecked=isChecked}}. This kinda works, but it calls the helper immediately. This breaks these since I need to use a value from the context to determine what to return from my helper. Since this value doesn't exist when the function returns, the function always returns undefined.
If I return a function from this helper rather than the value and then pass the helper into the template inclusion along with the data context, I get better results. In fact, my console logs show the desired output, but I still don't end up with the checked box I expect.
Here's what that looks like. Returning a function:
isChecked: function() {
var self = this;
return function() {
return (((Meteor.user() || {}).profile || {}).contentWellTags || []).indexOf(this) > -1 ? 'checked' : null;
};
}
and passing that to the template:
{{> choiceQuestion choices=allInterests picks=4 isChecked=isChecked}}
Is there an established pattern for overriding template helpers from the parent or for including helpers on the parent that are missing from the child template? How can I achieve this?

UI updates with Meteor.js?

I'm having issues finding a way to update the UI AFTER adding to a collection. So in the example below after you click the button and add to the collection an additional input is added to the DOM. All good, but i'd like to find a way to target the new input element and preferably give it focus in addition to CSS. Unfortunately I can't find any info that helps solve this AFTER the DOM's been updated. Any ideas? Thanks
<body>
{{> myTemplate}}
</body>
<template name="myTemplate">
{{#each myCollection}}
<input type="text" value="{{name}}"><br>
{{/each}}
<br>
<button>Click</button><input type="text" value="test" name="testBox">
</template>
test = new Meteor.Collection("test");
if (Meteor.isClient) {
Template.myTemplate.rendered = function()
{
console.log("rendered");
this.$('input').focus()
}
Template.myTemplate.helpers({
'myCollection' : function(){
var testCollection = test.find({});
console.log("helpers");
return testCollection;
}
});
Template.myTemplate.events({
'click button': function(event){
event.preventDefault();
var val = $('[name="testBox"]').val();
console.log("events");
return test.insert({name: val});
}
});
}
Turn what you're adding into a template and call that template's rendered to set the needed css or do whatever transforms are needed.
HTML:
<body>
{{> myTemplate}}
</body>
<template name="item">
<input type="text" value="{{name}}"><br>
</template>
<template name="myTemplate">
{{#each myCollection}}
{{> item this}}
{{/each}}
<br>
<button>Click</button><input type="text" value="test" name="testBox">
</template>
JS:
test = new Meteor.Collection("test");
if (Meteor.isClient) {
Template.myTemplate.onRendered(function() {
console.log("rendered");
this.$('input').focus()
});
Template.myTemplate.helpers({
'myCollection' : function(){
var testCollection = test.find({});
console.log("helpers");
return testCollection;
}
});
Template.myTemplate.events({
'click button': function(event){
event.preventDefault();
var val = $('[name="testBox"]').val();
console.log("events");
test.insert({name: val});
}
});
Template.item.onRendered(function() {
this.$('input').focus();
}
}
On a side note, you should use onRendered instead of rendered as the latter has been deprecated for the former.
Do it inside of your myCollection helper function. Use jquery to target the last input in your template and focus it, add css. Meteor's template helpers are reactive computations based on the DOMs usage of their reactive variables, so it will run each time the DOM updates based on your collection.

Why does my template not reactive?

If I first open http://localhost:3000/, then click the test link, the roles labels will be displayed.
But if I directly open http://localhost:3000/test(Input the url in Chrome's address bar and hit enter), the roles labels will not be displayed.
Here is my code:
In client startup I subscribe to something:
Meteor.publish("Roles", function(){
return Roles.find();
});
Meteor.startup(function() {
if(Meteor.isClient) {
Meteor.subscribe('Roles');
}
});
And roles template:
Template.roles.helper( {
allRoles: function() {
return Roles.find();
}
})
html
<template name="roles">
<div>
{{#each allRoles}}
<label>test label</label>
{{/each}}
</div>
</template>
The problem is sometime roles template is rendered before the Roles is ready.
So these is no role labels displayed.
But according to Meteor document, helpers is a reactive computation, and Database queries on Collections is reactive data source. So after Roles is ready, the {{#with allRoles}} is reactive and should be displayed.
Why does roles not be displayed?
And then I rewrite my code to:
Meteor.startup(function() {
if(Meteor.isClient) {
roles_sub = Meteor.subscribe('Roles');
}
});
Template.roles.helper( {
allRoles: function() {
console.log(2);
return Roles.find();
},
isReady: function() {
console.log(1);
console.log(roles_sub.ready());
return roles_sub.ready();
}
})
html
<template name="roles">
<div>
{{#if isReady}}
{{#each allRoles}}
<label>test label</label>
{{/each}}
{{/if}}
</div>
</template>
And still role labels cannot be displayed.
And console gives me:
1
false
1
false
1
true
2
Which means isReady() is reactive? but why my roles labels remains blank?
Can somebody explain this?
use Template.subscriptionsReady
server/publish.js
Meteor.publish("Roles", function(){
return Roles.find();
});
client/client.js
Meteor.startup(function() {
Meteor.subscribe('Roles');
});
Template.roles.helpers({ // --> .helper change to .helpers
allRoles: function() {
return Roles.find();
}
})
client/templates.html
<template name="roles">
<div>
{{# if Template.subscriptionsReady }}
{{#with allRoles}}
<label>{{> role }}</label>
{{/with}}
{{ else }}
loading....
{{/if}}
</div>
</template>
<template name="role">
<div>{{ _id }}</div>
</template>
A reactive function that returns true when all of the subscriptions
https://github.com/meteor/meteor/blob/9fe2f4b442ec84eac5352b476d014c977c5ae4a2/packages/blaze/template.js#L424

Template reuse in meteor

I'm trying to reuse some control elements in my Meteor app. I'd like the following two templates to toggle visibility and submission of different forms.
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
I'll call these templates in another:
<template name='insectForm'>
{{#if editing}}
<!-- form elements -->
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
editing is a Session boolean.
What's a good way to wire up the controls to show, hide and "submit" the form?
The main problem is finding the addInsect template (where the data is) from the control templates (where the "submit" event fires). Here's what I did:
First, the controls:
<template name='addControl'>
<section class='controls'>
<span class="add icon-plus"></span>
</section>
</template>
<template name='okCancelControl'>
<section class='controls'>
<span class="submit icon-publish"></span>
<span class="cancel icon-cancel"></span>
</section>
</template>
Now the javascripts. They simply invoke a callback when clicked.
Template.addControl.events({
'click .add': function(event, template) {
if (this.add != null) {
this.add(event, template);
}
}
});
Template.okCancelControl.events({
'click .cancel': function(event, template) {
if (this.cancel != null) {
this.cancel(event, template);
}
},
'click .submit': function(event, template) {
if (this.submit != null) {
this.submit(event, template);
}
}
});
I then connected the callbacks using handlebars' #with block helper.
<template name='addInsect'>
{{#with controlCallbacks}}
{{#if addingInsect}}
<section class='form'>
{{> insectErrors}}
<label for='scientificName'>Scientific Name <input type='text' id='scientificName' /></label>
<label for='commonName'>Common Name <input type='text' id='commonName' /></label>
{{> okCancelControl}}
</section>
{{else}}
{{> addControl}}
{{/if}}
{{/with}}
</template>
And the corresponding javascript that creates the callbacks relevant to this form.
Session.set('addingInsect', false);
Template.addInsect.controlCallbacks = {
add: function() {
Session.set('addingInsect', true);
Session.set('addInsectErrors', null);
},
cancel: function() {
Session.set('addingInsect', false);
Session.set('addInsectErrors', null);
},
submit: function() {
var attrs, errors;
attrs = {
commonName: DomUtils.find(document, 'input#commonName').value,
scientificName: DomUtils.find(document, 'input#scientificName').value
};
errors = Insects.validate(attrs);
Session.set('addInsectErrors', errors);
if (errors == null) {
Session.set('addingInsect', false);
Meteor.call('newInsect', attrs);
}
}
};
Template.addInsect.addingInsect = function() {
Session.get('addingInsect');
};
Template.addInsect.events = {
'keyup #scientificName, keyup #commonName': function(event, template) {
if (event.which === 13) {
this.submit();
}
}
};
In the submit callback I had to use DomUtils.find rather than template.find because template is an instance of okCancelControl, not addInsect.
You can use Session for this. You Just need a template helper that returns a boolean flag that indicates whether you are editing the form fields. And manipulate the DOM based on the Session value set by this template helper.
Assume you have one text input, now when you are entering text in it, set the Session flag as true. This will trigger the helper to return true flag, Based on that, one of your two templates will be rendered in the DOM.
The isEditing is the helper that triggers whenever you change the Session value.
This helper function is the main part here, it returns true/false based on the session value you have set.
Template.insectForm.isEditing = function(){
if(Session.get('isEditing')){
return true;
}
else{
return false;
}
}
Remember to set the Session to false at the start-up as:
$(document).ready(function(){
Session.set('isEditing', false);
})
This will render the default add template in the html, Now when you click on ADD, you need to display another template, for that, set Session to true as:
'click .add' : function(){
Session.set('isEditing', true);
}
Accordingly when you click on CANCEL, set the session to false, this will make the isEditing to return false and the default add template will be displayed.
So your complete html will look something like this:
<template name='insectForm'>
{{#if isEditing}}
<!-- form elements -->
<input type="text" id="text" value="">
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
[UPDATE]
To get the instance of the template, you'll need to pass the additional parameter in the event handler that represents the template.
So update your event handler as:
Template.insectForm.events = {
'click .submit' : function(event, template){
//your event handling code
}
}
The parameter template is the instance of the template from which the event originates.
Note that, although the event fires form the image that is inside the okCancelControl template, the parameter will still contain the instance of the insectForm template. This is because we are calling the event handler as Template.insectForm.events = {} .
Also see this answer for template instances.

Resources