I have the following event in my client file:
Template.categories.events({
...
'keyup #add-category': function (e,t){
if (e.which === 13)
{
var catVal = String(e.target.value || "");
if (catVal)
{
lists.insert({Category:catVal,owner:this.userId});
Session.set('adding_category', false);
}
}
},
...
});
And this is the relevant template part:
<template name="categories">
<div id="categories" class="btn-group">
{{#if new_cat}}
<div class="category">
<input type="text" id="add-category" value="" />
</div>
{{else}}
<div class="category btn btn-inverse" id="btnNewCat">+</div>
{{/if}}
{{#each lists}}
<div class="category btn {{list_status}}" id="{{_id}}">
{{Category}}
</div>
{{/each}}
</div>
</template>
So when a new Category is inserted, the owner should be set.. But it doesn't.
Here's the entry in MongoDB:
> db.lists.find()
{ "Category" : "test-admin", "_id" : "EsybjC3SLnNzCBx2t" }
Any idea what I'm doing wrong? (actually I'm following the "Getting Started with Meteor" book lending library example
EDIT it seems that:
console.log(this.userId);
undefined
Swap this line:
lists.insert({Category:catVal,owner:this.userId});
to this one:
lists.insert({Category:catVal,owner:Meteor.userId()});
this is probably not what you expect it to be inside that event. You should debug to confirm that that is the case. You are probably just getting undefined for this.userId. I would recommend assigning this to a variable (call it "self" or "that") outside of this event handler function but inside the scope where this will be what you actually want it to be. You can then reference that variable inside the event handler.
It should look like this:
function thatRegistersEvents() {
var self = this;
// ...
registerSomeEvent(function () {
return self.someThisProperty;
});
}
This is the correct behavior. If you read the official Meteor documentation for references to this.UserId, it is only available in Meteor.publish() and Meteor.methods(). this.UserId is not available in Meteor.template(), which you have done in the code sample above, so one must use Meteor.userId() in templates.
Related
I am new to meteor and js and trying to build some app using meteor.
I want to get the _id for further use. Before I just use this._id to get the object id but this time I cannot.
Here is the html:
{{#each player in getPlayers}}
<div class="card test">
<div class="content">
<a class="header">{{player.name}}</a>
</div>
</div>
{{/each}}
Here is the js:
Template.playerList.helpers({
'getPlayers'(){
var player = PlayerLists.find().fetch();
return player;
}
});
and the function I try to catch the _id:
'click .test': function() {
event.preventDefault();
var currentId = this._id;
currentPlayer = PlayerLists.findOne({_id:currentId});
console.log(currentId);
}
PlayerLists is a collection and play is document. I try to just the selected player's _id but currentId shows undefined whenever I click the player.
Could anyone help me with it? Thanks a lot!
I use a simple solution:
{{#each player in getPlayers}}
<div class="card test">
<div class="content">
<a class="header useThisEvent" data-id="{{_id}}>{{player.name}}</a>
</div>
</div>
{{/each}}
'click .useThisEvent': function() {
event.preventDefault();
//there you have id of selected item. Its imporatant to use event.currentTarget
var id = $(event.currentTarget).data('id')
}
//Try to simple you code:
Template.playerList.helpers({
'getPlayers'(){
return PlayerLists.find().fetch();
}
});
{{#each player in getPlayers}} i suggest to change for {{#each getPlayers}}
btw in 'this' probably you have window object, not _id from PlayerList collection. Try to use Template.instance() or Template.currentData() but this methods sometimes can return something else.
I'm having a bit of trouble seeing what's wrong with my code. More likely with the Knockout.js part... It's giving me the following error:
Message: Unable to process binding "attr: function (){return {href:website()} }"
HTML
<div class="demo-card-square mdl-card mdl-shadow--2dp" data-bind="foreach: favoriteSpot">
<div class="mdl-card__title mdl-card--expand">
<h2 class="mdl-card__title-text">Update</h2>
</div>
<div class="mdl-card__supporting-text" data-bind="text:name"></div>
<div class="mdl-card__supporting-text" data-bind="text:location"></div>
<a data-bind="attr: {href: website()}">Website</a>
</div>
JS
var favoriteSpotsList = [{
venueName: "name",
venueLocation: "address",
website: "url",
image: "<img src='img'",
}];
var favoriteSpot = function(data) {
this.name = ko.observable(data.venueName);
this.address = ko.observable(data.venueLocation);
this.website = ko.observable(data.website);
this.image = ko.observable(data.img);
};
var AppViewModel = function() {
var self = this;
/* Create array of hotspot locations. */
this.hotSpotList = ko.observableArray([]);
favoriteSpotsList.forEach(function(spot) {
self.hotSpotList.push(new favoriteSpot(spot));
});
};
ko.applyBindings(new AppViewModel());
As #saj and #haim770 mentioned in comment, there is no favoriteSpot property on the view-model. So, the data bind should loop the hotSpotList to get the website property in it. Like below.,
data-bind="foreach: hotSpotList"
There is an easy way to identify these kind of issues, specifically while performing bindings in view
You just need add a button with click binding, The Button should be placed before the exception line.
<button data-bind="click: function () { console.log($context); }"> Context Log </button>
The above code will log the entire context in the browser console(F12). As usual you will get the exception. And this code will not resolve the issue. But this will be very helpful to identify the issue.
The above code will log the entire context of the current operation. Which holds object, property with the value.
Below are common scenarios where as you can exactly find your binding object has exceptions.
1. Properties are present/missing due to the scope level problems?
2. Whether it has case sensitive problem?
3. Your object comes under where? Is it a parent, child / Alone?
4. Human error which makes exception while binding.
There are few other ways to find the object/data in view:
1. Logs the root:
<button data-bind="click: function () { console.log($root); }"> Root Log </button>
2. Logs the Current scope data:
<button data-bind="click: function () { console.log($data); }"> Current Data Log </button>
3. Logs the parent data: (specifically helpful when we do looping)
<button data-bind="click: function () { console.log($parent); }"> Parent Log </button>
4. Logs the list of parent data: (specifically helpful when we do looping with different types of parents)
<button data-bind="click: function () { console.log($parents); }"> Parents Log </button>
5. Logs the list of parent data: (specifically helpful when we do looping and access different types of parents)
<button data-bind="click: function () { console.log(objectName.propertyName); }">Property Log </button>
For Example in your case you can do like below:
<!-- Added this button before the exception -->
<button data-bind="click: function () { console.log(favoriteSpot); }">Log </button>
<div class="demo-card-square mdl-card mdl-shadow--2dp" data-bind="foreach: favoriteSpot">
<div class="mdl-card__title mdl-card--expand">
<h2 class="mdl-card__title-text">Update</h2>
</div>
<div class="mdl-card__supporting-text" data-bind="text:name">
</div>
<div class="mdl-card__supporting-text" data-bind="text:location">
</div>
<a data-bind="attr: {href: website()}">Website</a>
</div>
When you click the button, obviously the message will be logged as undefined in console.
Hope this helps.,
I'm trying to figure out where is the best place to run a jQuery plugin that replaces a textarea (with a reactive value). It needs to be called after the textarea has been assigned the value.
I've tried various places. The most correct place I've tried seems to be in the templates onAfterAction callback, in a Meteor.defer. This works about 95% of the time.
Something like this:
MyController = RouteController.extend({
waitOn: function () {
return Meteor.subscribe('post', this.params._id);
},
onAfterAction: function () {
Meteor.defer(function () {
$('.make-wysiwyg').wysiwyg();
});
}
});
However, occasionally it doesn't. If I start bouncing between posts really quick, occasionally one will apparently run before the textarea has data and fail to display property (it'll be empty, because it needs the value before wysiwyg() is called).
I've eliminated the wysiwyg() function itself as the culprit by replacing that line with:
$('.make-wysiwyg').each(function () {console.log($(this).val())});
And I can clearly see every so often it'll print empty value fields for no apparent reason.
I'm not sure if the template or publish() function could be a culprit, so I'll supply them as well.
Any ideas greatly appreciated.
Template:
<template name="adminPostsEdit">
<h1>Edit Post</h1>
<form id="edit-post" class="{{isNewClass}}" method="post">
<label for="post-title">Title</label>
<input id="post-title" value="{{post.title}}">
<label for="post-slug">Slug</label>
<input id="post-slug" value="{{post.slug}}">
<label for="post-content">Content</label>
<textarea id="post-content" class="make-wysiwyg">{{post.content}}</textarea>
<label for="post-excerpt">Excerpt</label>
<textarea id="post-excerpt" class="make-wysiwyg">{{post.excerpt}}</textarea>
{{#if post.published}}
<button value="save">Save</button>
<button value="unpublish">Unpublish</button>
{{else}}
<button value="save">Save Draft</button>
<button value="publish">Publish</button>
{{/if}}
<button value="cancel">Cancel</button>
</form>
</template>
publish():
Meteor.publish('post', function (id) {
return Posts.find({_id: id});
});
Helpers:
Template.adminPostsEdit.helpers({
post: function () {
return Posts.findOne();
},
isNewClass: function () {
return !this.id ? 'new' : '';
}
});
You should do that in the template's render function.
Template.adminPostsEdit.rendered = function() {
$('.make-wysiwyg').wysiwyg();
})
I'm completely new to Meteor, and I was doing the leaderboard example. I have a little problem with my code.
I was trying to add a toggle button to toggle sorting. The toggling and all is working fine, but the button's text doesn't update.
My javascript code:
if (Meteor.isClient) {
Meteor.startup(function () {
Session.set("sortMethod", "score");
});
...
Template.leaderboard.players = function () {
if (Session.equals("sortMethod", "name"))
return Players.find({}, {sort: {name: 1}});
else if(Session.equals("sortMethod", "score"))
return Players.find({}, {sort: {score: 1}});
};
...
Template.leaderboard.sortMethod = function () {
return Session.get("sortMethod");
}
...
Template.leaderboard.events({
'click input.sortToggle': function () {
Session.equals("sortMethod", "name") ? Session.set("sortMethod", "score") : Session.set("sortMethod", "name");
}
});
}
My handlebars template:
<template name="leaderboard">
<!-- this is where it goes wrong, the button text doesn't update at {{sortMethod}} -->
<input type="button" class="sortToggle" value="sort by {{sortMethod}}">
<!-- -->
<div class="leaderboard">
{{#each players}}
{{> player}}
{{/each}}
</div>
{{#if selected_name}}
<div class="details">
<div class="name">{{selected_name}}</div>
<input type="button" class="inc" value="Give some points" />
</div>
{{else}}
<div class="none">Click a player to select</div>
{{/if}}
</template>
Note: I removed some irrelevant code.
It works if you use a button instead:
<button class="sortToggle">sort by {{sortMethod}}</button>
With the corresponding change to events:
'click .sortToggle': function () { ...
Changing the value of an input was reported as an issue in the past, but it was closed. Perhaps it needs to be reopened.
I am not sure if this is a bug or a feature. I think the problem stems from trying to update an input element that has focus. So the fix is to blur() the element on the end of your event handler. Like this:
'click input.sortToggle': function ( event ) {
Session.equals("sortMethod", "name")
? Session.set("sortMethod", "score")
: Session.set("sortMethod", "name");
$( event.currentTarget ).blur();
}
I have template:
{{#each action_log}}
<div>
{{Name}}
{{#if editing_score}}
<input type="text" id="set_score" parm="{{_id}}" value="" />
{{else}}
<div class="score" id="{{_id}}">{{Score}} .</div>
{{/if}}
</div>
{{/each}}
And I have a javascript code:
Session.set('editing_score', false);
Template.today_list.editing_score = function() {
return Session.equals('editing_score', true);
};
Template.today_list.events({
'click .score': function(e, t) {
Session.set('editing_score', true);
Session.set('editing_score_id', e.target.id);
Meteor.flush();
}
});
So, user can see list of actions, want to click on one of them and edit some value. But now, if user click on the action, the code display textboxes for all actions.
How can I display a textbox only for one action ?
I have action id in Session:
Session.set('editing_score_id', e.target.id);
But I can not do something like this:
{{#if editing_score && editing_score_id == _id}}
or
{{#if editing_score(_id)}}
What is the best way to do it ?
Thanks in advance.
With your template:
{{#if editing_score id}}
With your helper:
Template.today_list.editing_score = function(id) {
return Session.equals('editing_score_id', id);
};
However, I would refactor a bit so that you only have one Session variable — no need for two.
Also, your 'click .score' passes context, so you can id from this — which I find is more reliable than mucking around with the event that you are passing in.