The below code is expected to insert a document of 3 pairs in the FooterButtons collection, then those values are shown as labels on 3 buttons in the footer template.
But when the "click .menuItem" is called, it only insert "YES" in the collection. Any idea why it is broker and best way to fix it? Thanks
Server and client code
FooterButtons = new Mongo.Collection('footerButtons');
Server code
Meteor.publish('footerButtons', function(){
return FooterButtons.find();
});
Client code
Meteor.subscribe('footerButtons');
//---main_menu.js--------------------
Template.mainMenu.events({
'click .menuItem': function (event) {
FooterButtons.insert({button:"NO", button:"EXTRA", button:"YES"});
}
});
//---footer.html---------------
<template name="footer">
{{#each footerButtons}}
<h1>
<button class="col-xs-4" type="button">{{button}}</button>
</h1>
{{/each}}
</template>
//---footer.js---------------
Template.footer.helpers({
footerButtons: function(){
return FooterButtons.find();
}
});
This command is totally wrong for the mongo insertion
FooterButtons.insert({button:"NO", button:"EXTRA", button:"YES"});
If you create a javascript object,
var obj = {button:"NO", button:"EXTRA", button:"YES"};
since all the keys is duplicated, your object will only has one key which has the value of the last one: Yes
You need to insert one by one
FooterButtons.insert({button:"NO"});
FooterButtons.insert({button:"EXTRA"});
FooterButtons.insert({button:"YES"});
Related
Sorry kind of new to the Meteor framework!
I Subscribed to two Publish functions. Even if both publish functions target the same Collection, they both have different functions, that I would like to display in one template. How do I achieve this. I have done allot of research but there doesn't seem to be sufficient information on how to achieve.
Following are the two publish functions in code that I subscribe to:
.server/main.js:
Meteor.publish('MerchantTrending', function (categoryMan){
var currentUser = this.userId;
return buyList.find({ who:"Merchant", ownerId:currentUser, itemCategory: { $in: categoryMan } }, {skip: 0, limit: 3});
});
.server/main.js:
Meteor.publish('myTopViews', function (){
var currentUser = this.userId;
return buyList.find({ newArrivalsExpiryDate : {'$lte': new Date()}}, {ownerId:currentUser }, {skip: 0, limit: 3});
});
Following is the subscription function in code
.client/main.js:
Router.route('/MerchantLandingPage', {
subscriptions: function(){
var categoryMan = Session.get('category');
return Meteor.subscribe('MerchantTrending', categoryMan, 'merchantTopViews')
}
});
Now the helper function in code:
Template.MerchantLandingPage.helpers({
'top3Trending' : function () {
return buyList.find({}).fetch();
},
'myTopViews' : function () {
return buyList.find({}).fetch();
}
});
And now the template in code:
<template name="MerchantLandingPage">
##### *** Top three trending items *** ########
{{#each top3Trending}}
ItemName:: <b>{{itemName}}</b> <br>
Item Category:: <b>{{itemCategory}}</b> <br>
Discription:: <b>{{descriptions}}</b> <br>
Image:: {{this.photo._id}} <br>
Date Created:: {{createdDate}} <br>
{{/each}}
<br><br>
############ *** My top Views *** #############
{{#each myTopViews}}
ItemName:: <b>{{itemName}}</b> <br>
Item Category:: <b>{{itemCategory}}</b> <br>
Discription:: <b>{{descriptions}}</b> <br>
Image:: {{this.photo._id}} <br>
Date Created:: {{createdDate}} <br>
{{/each}}
</template>
Both {{#each myTopViews}} and {{#each top3Trending}} successfully display but not correctly. When the variable categoryMan in
Meteor.subscribe('MerchantTrending', categoryMan, 'merchantTopViews')
changes value, it affects both both the outcome of both {{#each myTopViews}} and {{#each top3Trending}}, when its only supposed to affect {{#each top3Trending}}.
How can I get the subscriptions to NOT have an affect on both {{#each myTopViews}} and {{#each top3Trending}}, but only {{#each myTopViews}} in my template?
Thanks for the help!
Welcome to Meteor!
The solution is straight forward once you understand that:
Subscription is just a stream of your DB documents from server into your client's MiniMongoDB. So your 2 subscriptions (it is perfectly fine having several subs on the same Collection) just fill in your client's buyList local collection.
Use of Collections client side is generally independent from how you subscribe the data. So you should simply use a similar selector and possibly options in your top3Trending and myTopViews helpers as you have done for your publication server side (not the same between the 2 helpers, obviously).
As a side note, you do not even need to fetch() the Collection cursor returned by find(), Blaze knows how to handle it directly.
I see a few problems with your code, first of all - your second subscription isn't going to work because your query is wrong:
Meteor.publish('myTopViews', function (){
var currentUser = this.userId;
return buyList.find(
{ ownerId:currentUser, newArrivalsExpiryDate : {'$lte': new Date()}},
{skip: 0, limit: 3}
);
});
You had ownerId: currentUser wrapped in curly braces, it is fixed above.
The way publications/subscriptions work is, if you have two publications sending different data, the template doesn't 'know' the data is coming from two different subscriptions. It will just have access to all of the data being sent by all subscriptions.
For that reason, your two helpers top3trending and myTopViews are returning exactly the same thing. You can delete one of them!
You should move your subscriptions out of the router and in to the Template itself. Here's an article that will help you with that!
There is a package percolate:find-from-publication that permits to filter the data from publications.
I have a simple <firebase-query> tag, and I'd like to manipulate some of the data before having it displayed through a <dom-repeat>. For example, I need to turn some fields into links, and also parse some dates.
So, I need to get the data once it's ready, loop through each item, and change some of the values.
To do that, I have an observer on the data to detect when it's ready. However, I can't figure out how to loop through the data from that JavaScript function. For some reason, for(var i in items) doesn't work, although the items do exist.
Here is the component:
<dom-module id="cool-stuff">
<template>
<firebase-query id="query" path="/items" data="{{items}}"></firebase-query>
<template is="dom-repeat" items="{{items}}" as="item">
[[item.name]]<br />
[[item.date]]<br />
</template>
</template>
<script>
Polymer({
is: 'ix-table',
properties: {
items: {type: Object, observer: "_itemsChanged"},
}
itemsChanged: function(data) {
// how do I loop through the data received from firebase-query?
console.log(data);
}
});
</script>
</dom-module>
Ideally, all I'd want to do in the observer function is something like:
for(var i in data) {
obj = data[i];
obj.name = '<a href="/item/"+obj.key>'+ojb.name+'</a>';
}
But I can't seem to be able to loop through the data.
Inside the observer function, console.log(data) returns some weird stuff like this:
Array[o]
0: Object (which contains a proper item)
1: Object (same)
2: Object (same)
Update:
Here is a screenshot of what console.log(data) returns (from inside the observer):
The array seems to be populated with all the objects, but it shows as Array[0]. So it won't let me loop through them.
Update 2:
Thanks to arfost here is the solution:
<script>
Polymer({
is: 'ix-table',
properties: {
items: {type: Object},
}
observers: [
'_itemsChanged(items.splices)'
],
_itemsChanged: function(changeRecord) {
if (changeRecord) {
changeRecord.indexSplices.forEach(function(s) {
for (var i=0; i<s.addedCount; i++) {
var index = s.index + i;
var item = s.object[index];
console.log('Item ' + item.name + ' added at index ' + index);
// do whatever needed with the item here:
this.items[index].name = "New name";
}
}, this);
}
},
});
</script>
<firebase-query> results
Note that <firebase-query> results in an array of objects. Let's say your database contained the following items under /notes/<USER_ID>/:
Your <firebase-query> would look similar to this:
<firebase-query
id="query"
app-name="notes"
path="/notes/[[user.uid]]"
data="{{notes}}">
</firebase-query>
(where user is bound to <firebase-auth>.user).
Assuming the user is logged in, <firebase-query> would then populate its data property (i.e., bound to notes) with the following array:
Note how each object contains a $key property, which corresponds to the item's key seen in the Firebase console's Database view.
You could then iterate notes directly with <dom-repeat>:
<template is="dom-repeat" items="[[notes]]">
<li>
<div>key: [[item.$key]]</div>
<div>body: [[item.body]]</div>
<div>title: [[item.title]]</div>
</li>
</template>
Binding to HTML strings
You should be aware that the string data bindingsĀ are rendered literally in this case, so attempting to set name to obj.name = '<a href="...">' would render the literal string instead of an anchor. Instead, you should declare the tags in your template, and bind the key and name properties inside those tags. So, your observer could be replaced with this:
<template is="dom-repeat" items="{{items}}" as="item">
<a href$="/item/[[item.key]]">[[item.name]]</a><br />
[[item.date]]<br />
</template>
Iterating an array
The following note is only relevant if you prefer to mutate the data before displaying it...
When iterating an array, you should avoid for..in because it doesn't guarantee order of iteration, and because it may iterate over enumerable properties you might not necessarily care about. Instead, you could use for..of (assuming ES6 is available to your app):
for (let note of notes) {
note.title += ' ...';
}
or Array.prototype.forEach():
notes.forEach(function(note) {
note.title += ' ...';
});
I thinks I have run into the same issue as you.
It come from the way firebase query is getting the array, the way polymer obersvers works, and is hidden by the fact that the javascript console is reference based when it show the objects.
In fact what really happen here, is that firebase query is creating an empty array, which trigger your polymer observer.
So your function is called as soon as the array is created, but still empty and you can't iterate through, since it's empty. You then log it, where the primitives sub-properties are correctly displayed (array[0])
Then firebase begin to populate the array with the datas. The arrays reference stay the same, so polymer won't fire the observer again, and in the console, when it try to display the array it display the array referenced in the log, which now contains the datas.
I recommend that you use a array mutation observer in place of your simple one as follow
`properties: {
items: {type: Object},
},
,
observers: [
'_itemsChanged(items.splices)'
],`
It will fire every time an object is added to your array, and you would be able to do the work you need :)
I had the link for the documentation on array mutation observer :)
polymer array mutation observer
I hope this will solve your issue,
have a good day.
i don't think i can think of a scenario where you'd need to mutate the data by looping through the array rather than just using computed bindings. like this:
<template is="dom-repeat" items="{{items}}" as="item">
<child-el date="{{_computeDate(item.date)}}"></child-el><br />
<child-el attr1="{{_someOtherConversion(item.prop1)}}"></child-el><br />
<child-el attr2="{{_iPromiseAnyConversionCanBeDoneLikeThis(item.prop2)}}"></child-el><br />
</template>
<script>
_computeDate: function(item) {
//do date converstion
}
I'm trying to prevent a template helper to be rerun when it is unnecessary. I made a simple application to illustrate this behavior:
Let's say I want to display some items that contain only a title and a description.
<template name="Tests">
{{#each items}}
{{> TestsItems}}
{{/each}}
</template>
<template name="TestsItems">
<div class="title">{{title}}</div>
<div class="description">{{description}}</div>
</template>
I have autopublish enabled.
Template.Tests.helpers({
items: function () {
return Items.find();
}
});
Template.TestsItems.helpers({
description: function () {
// I'm using this helper to do some updates
// on a jQuery plugin when the description field change.
// see example 1: https://github.com/avital/meteor-ui-new-rendered-callback/
console.log("The description is run");
return this.description;
}
});
When a new update is made on the title field only, you can see that the description helper is rerun. What I'm trying to achieve is to only rerun this helper when there is a new value for the description field and not every time a field has changed in the document.
As {{#constant}} and {{#isolate}} are deprecated, how can I get this behavior in the latest Meteor versions?
Note 1: Create a new subtemplate including the description does not fix the problem.
I would avoid side effects in template helpers. Instead I would use an autorun:
Template.TestItems.rendered = function () {
var _id = this.data._id;
this.autorun(function () {
// Select only the description field, so that we only
// trigger a re-run if the description field changes
var description = Items.findOne(_id, {fields: {description: 1}}).description;
// update the JQuery plugin
});
}
I have the following templates (.html) with their respected managers (.js files):
adminManageCategories
adminAddCategory
adminUpdateCategory
Consider the following:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
{{> adminUpdateCategory}}
</template>
Notice the {{> adminUpdateCategory}} is outside of the iteration. This is also a form, and I want to keep it on the same page.
And admin_manage_categories.js
Template.adminManageCategories.events({
"click .clickme": function(event) {
event.preventDefault();
console.log(this._id);
}
});
Notice the console.log() function, which works, as the template manager is smart enough to know the ID of the item that was clicked.
What I want to do is load this items values into the form when clicked. My example above is slim, but in my real data I have a title, sort order, among other things.
So my question is, what is the proper way to pass the _id from the adminManageCategories template to the adminUpdateCategory template, which is the form?
I can hack at this with JavaScript and make things happen, but I think I'm missing a "Meteor way" of doing things.
You need to use a ReactiveVar to store the currently clicked item.
First you need to run meteor add reactive-var, as it's not a package added by default in a standard meteor web app.
JS:
Template.adminManageCategories.created=function(){
// instantiate the reactive-var in the created callback
// we store it as a property of the template instance
this.currentItemId=new ReactiveVar(null);
};
Template.adminManageCategories.helpers({
// this helper reactively returns the currently clicked item
currentItem:function(){
// retrieve the reactive-var from the template instance...
var currentItemId=Template.instance().currentItemId.get();
// ...to fetch the correct collection document
return Items.findOne(currentItemId);
}
});
Template.adminManageCategories.events({
"click .clickme": function(event,template) {
event.preventDefault();
// assign the correct item id to the reactive-var attached to this template instance
template.currentItemId.set(this._id);
}
});
HTML:
<template name="adminManageCategories">
{{#each category}}
<div class="clickme">{{title}}</div>
{{/each}}
<p>Current item title is : {{currentItem.title}}</p>
{{! pass the currentItem as a parameter to your child template this will be
accessible as {{item}} in the HTML and "this.item" in JS helpers or
"this.data.item" in created/rendered/destroyed callbacks}}
{{> adminUpdateCategory item=currentItem}}
</template>
EDIT:
When I initialize the reactive-var in the created callback, I set it to null, this means that until one item is clicked, the helper will return null too and when you'll try to access this.item._id in the adminUpdateCategory this will fail.
The simplest way to solve this issue is maybe to not initialize the variable to null but to the first item in the collection.
Template.adminManageCategories.created=function(){
var firstItem=Items.findOne({},{
sort:{
sortedField:1
}
});
this.currentItemId=new ReactiveVar(firstItem && firstItem._id);
};
There may still be a case when you have 0 items in the collection, so you'll probably end up having to guard against the existence of the item in the JS.
Template.adminUpdateCategory.helpers({
itemProperty:function(){
return this.item && this.item.property;
}
});
Using Meteor, I am trying to loop through and display a list of notes from a database with an option to delete each note.
Here is the HTML (using Handlebars.js)
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
And here is the client Javascript
Template.Notes.events = {
"click .deleteNote" : function(){
noteID = $('.deleteNote').parent().attr("id");
Notes.remove({ID:noteID});
}
};
This grabs the first instance of .deleteNote, so unless I'm trying to delete the first one, that won't help. How can I grab the parent of the particular instance of .deleteNote that was clicked, not just the first one it finds?
The reason why the first element is deleted is.. in your .click event, you are accesssing the div directly as $('.deleteNote').parent() which grabs the first node in the html which has a class .deleteNode.
Now to remove the specific notes, from the collection: Every document in the collection has a unique _id attribute which is generated automatically. assign that unique _id of the document to the span element as <span id= "{{_id}}" class="deleteNote">Delete</span>.
So the cilck event will look like:
Template.Notes.events = {
"click .deleteNote" : function(e){
var noteID = e.currentTarget.id;
Notes.remove({_id:noteID});
}
};
And the template will look like:
<template name="Notes">
{{#each NoteArr}}
<div class="Note">
<h2>{{Title}}</h2>
<p>{{Body}}</p>
<span id= "{{_id}}" class="deleteNote">Delete</span>
</div>
{{/each}}
</template>
Untested code, but hope this will help solving your issue.
The _id of a note is stored in 'this' as well. In addition, the remove function accepts '_id' as a string. So this should work as well:
Template.Notes.events = {
'click .deleteNote': function(){ return Notes.remove(this._id)}
}
A few benefits here. Less querying the DOM for information. Less jQuery. Fewer lines of code to think about. Cleaner templates.