Mongo pull in Meteor not pulling - javascript

I've been trying to pull an array from Mongo inside of Meteor, but I can't seem to get it to work. I'm able to push to it fine. Any help would be greatly appreciated. Here's my html:
<ul class="schedule">
{{#each employee.schedule}}
<li class="schedule_item"><a class="btn listExRemove" title="Remove this name">x</a><p class="schedule_item_info">{{ . }}</p></li>
{{/each}}
</ul>
<div class="form-group form-md-line-input">
<input type="text" name="schedule" value="" class="form-control" id="schedule" placeholder="Enter the schedule" >
<label for="item">Schedule</label>
</div>
Add
Here is the javascript for pushing:
'click #add_event': function(event, template) {
var id = this._id; // Set equal to the current id
var push_it = $('#schedule').val();
Employees.update({_id:id}, {
$push: {
schedule: push_it
}
});
console.log("It's updated");
And here is me pulling:
'click .listExRemove': function(event, template) {
var id = this._id; // Set equal to the current id
var the_text = $(event.target).parent().find('.schedule_item_info').text();
Employees.update({_id: id}, {
$pull: {
schedule: the_text,
}
});
console.log(the_text);
Edit:
New values are being pushed into the collection by clicking the "#add_event" button. It then pushes the value of the "#schedule" input into the "schedule" array.
Values are trying to be removed by clicking the ".listExRemove" button. It then looks in the parent container for the ".schedule_item_info" text, and saves it in the "the_text" variable. Next it should pull "the_text" from the "schedule" array, and display "the_text" in the console.
It displays "the_text" in the console, but doesn't pull it from the collection. I'm not receiving any errors, warnings, etc. I've tried pushing into different array variations and pulling without success. I've also tried replacing "the_text" with a string I know is in the array to no avail.

It turns out since the "listExRemove" button was in an {{#each}} loop, "this._id" wasn't getting the id of the template. In order to get the id I used "template.data._id" instead.

Related

Generating unique variables for #each in an array in order to write an upvote/downvote app

I am brand new to coding so forgive my very obvious ignorance. My question is this: How can I create a unique variable for each item in a global array in MongoDB so that I can tally upvotes and downvotes and sort accordingly. I'm doing all this in the Meteor framework.
Here's my code:
<template name="website_item">
<li>
{{title}}
<p>
{{description}}
</p>
<a href="#" class="btn btn-default js-upvote">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"> </span>
</a>
<a href="#" class="btn btn-default js-downvote">
<span class="glyphicon glyphicon-arrow-down" aria-hidden="true"> </span>
</a>
<p>
Votes: {{totalVotes}}
</p>
</li>
</template>
Here's my client.js:
var totalVotes = 0;
Template.website_item.events({
"click .js-upvote":function(event){
// example of how you can access the id for the website in the database
// (this is the data context for the template)
var website_id = this._id;
console.log("Up voting website with id "+website_id);
// put the code in here to add a vote to a website!
totalVotes++;
console.log(totalVotes);
Websites.update({_id:website_id}, {$set: {totalVotes:totalVotes}});
return false;// prevent the button from reloading the page
},
"click .js-downvote":function(event){
// example of how you can access the id for the website in the database
// (this is the data context for the template)
var website_id = this._id;
console.log("Down voting website with id "+website_id);
// put the code in here to remove a vote from a website!
totalVotes--;
console.log(totalVotes);
Websites.update({_id:website_id}, {$set: {totalVotes:totalVotes}});
return false;// prevent the button from reloading the page
}
})
In collection.js I have:
Websites = new Mongo.Collection("websites");
and in server.js I have:
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
if (!Websites.findOne()){
console.log("No websites yet. Creating starter data.");
Websites.insert({
title:"Test site",
url:"http://www.test.com",
description:"This is a test.",
createdOn:new Date(),
totalVotes: 0
});
Websites.insert({
title:"Google",
url:"http://www.google.com",
description:"Popular search engine.",
createdOn:new Date(),
totalVotes: 0
});
}
});
I hope i've been comprehensive and clear with my question. I just want to be able to save up and downvote tallies for each item in my global array but right now there's just a single variable which does nothing for me.
Thanks so much!
you're missing publish and subscribe. the idea is your template will subscribe to the website data, and the server will then publish what you're asking for. you would write a helper that performs a find() on the published data, and an #each loop that would iterate over that data.
once you're done that, the tricky part, which is what you're asking about, is to tag each loop item so that, when clicked, you can uniquely identify it in the event handlers.
let's set up a new template (i'm writing all this code just in this text box, w/o trying it, so please forgive typos):
<template name="Websites">
{{#each website in websites}}
{{website.title}}
Votes: {{website.totalVotes}}
<button class="js-upvote" data-id={{website._id}}>Vote Up</button>
{{/each}}
</template>
then, you need to subscribe, like this:
Template.Websites.onCreated(function() {
this.subscribe('websites');
});
i'll assume you already have a publish written, or auto publish is on...
then write the helper:
Template.Websites.helpers({
websites() {
return Websites.find({});
}
});
and finally the event listener that can identify which item was clicked:
Template.Websites.events({
'click .js-upvote': function(event, template) {
event.preventDefault();
if (event && event.currentTarget && event.currentTarget.dataset) {
let websiteId = event.currentTarget.dataset.id;
// now you can save the incremented votes for this website
}
}
});
Regarding your totalVotes variable, i think i understand what you're trying to do, and now you can get rid of that. with the code i've written, it will save to the db the increments and decrements of each website, and because you're subscribed, you'll get that updated vote total back and reactively display it in the template.
update:
alternatively, accessing _id without writing it to the DOM:
<template name="Websites">
{{#each website in websites}}
{{website.title}}
Votes: {{website.totalVotes}}
<button class="js-upvote">Vote Up</button>
{{/each}}
</template>
Template.Websites.events({
'click .js-upvote': function(event, template) {
event.preventDefault();
let websiteId = this._id;
// now you can save the incremented votes for this website
}
}
});

Remove mongodb documents from a collection by an array of _ids

Using Meteor, I have a collection for Contacts which I list with a checkbox beside each contact.
I want to be able to select multiple checkboxes and then click a button to delete the selected contacts from the Contacts collection.
With the code I have below, the selected _ids show up in an array in the console, but nothing is being deleted and no error is being produced.
contacts.html
<template name="contacts">
<h1>Contacts</h1>
<ul>
{{#each contacts}}
{{> contact}}
{{/each}}
</ul>
<br/>
Create Contact
<a class="delete-selected" href="">Delete Selected Contacts</a>
</template>
contact.html
<template name="contact">
<li style="list-style:none;">
<input id="{{_id}}" type="checkbox" value="{{_id}}" name="contact"/>
<span class="contact">{{firstName}} {{lastName}} <strong>{{company}}</strong></span> {{#if contactType}}({{contactType}}){{/if}}
</li>
</template>
Client JS
Template.contacts.events = {
'click .delete-selected': function(e) {
e.preventDefault();
var selectedContacts = [];
$('input[name=contact]:checked').each(function() {
selectedContacts.push($(this).val());
});
Meteor.call('removeSelectedContacts', {selectedContacts: selectedContacts});
}
Server JS
Meteor.methods({
removeSelectedContacts: function(selectedContacts) {
Contacts.remove({_id:{$in:[selectedContacts]}})
//Contacts.remove({selectedContacts});
console.log(selectedContacts);
}
});
Thanks
#corvid: I think you can only remove one at a time on the client (by _id)
#serks: you have an extra array level in there, in your method just do:
Contacts.remove({ _id: { $in: selectedContacts }});
There's also an error in how you're calling the Method, instead of:
Meteor.call('removeSelectedContacts', {selectedContacts: selectedContacts});
Simply pass the parameter directly:
Meteor.call('removeSelectedContacts',selectedContacts);
The method is expecting an array, not an object.
Use the $in operator.
Template.contacts.events({
'submit #delete-contacts-form': function (e, template) {
// assumes permissions are set on server appropriately.
Contacts.remove({
_id: {
$in: $(e.target).find('input:checked').map(el => el.val()) // assumes the value of each checkbox is the _id
}
});
}
});

Group results in autocompleted dropdown [Meteor]

I try to do a dropdown list in my app. First of all I use a Meteor, so that's specific kind of app ofc :)
Second thing is that I use sebdah/meteor-autocompletion package, because I want my results to be sorted in specific way and limited.
The last thing I need is to group my results.
For example: If I have 2 products named "blah" I want to get only 1 "blag" in my dropdown "autocompletion" list.
Some code:
HTML:
<template name="InvoicesEditInsertInsertForm">
<input id="descriptionautocomplete" type="text" name="description" value="" class="form-control" autofocus="autofocus" placeholder="New Item...">
</template>
JS:
Template.InvoicesEditInsertInsertForm.rendered = function() {
AutoCompletion.init("input#descriptionautocomplete");
};
Template.InvoicesEditInsertInsertForm.events({
'keyup input#descriptionautocomplete': function () {
AutoCompletion.autocomplete({
element: 'input#descriptionautocomplete', // DOM identifier for the element
collection: InvoicesItem, // MeteorJS collection object
field: 'description', // Document field name to search for
limit: 5, // Max number of elements to show
sort: { modifiedAt: -1 },
}); // Sort object to filter results with
},
});
I need to use function that could group my "description" here.
I tried to do it in helper and I get it on my screen, but to be honest I don't know how to put that into my dropdown :(
try: function() {
var item= InvoicesItem.find({},{sort:{modifiedAt:-1}}).fetch();
var descriptions={};
_.each(item,function(row){
var description = row.description;
if(descriptions[description]==null)
descriptions[description]={description:description};
});
return _.values(descriptions);
},
I don't think you can do what you want with that package. If you have a look at the current limitations of the package documentation, you can see other potential solutions to your problem.
You can do addtional filtering as follows:
filter: { 'gender': 'female' }});
but I don't think this will allow you to demand only unique options.
The code you wrote above for try won't do anything. Autocomplete doesn't take a field called try.

Meteor - return all documents from collection 1 and then query collection 2 as looping through collection 1

I am very new to Meteor (and web programming) and setup two collections, one called Posts and the other Authors (I do realize I could put all this information in one collection but I want to try it this way). I am trying to display all posts so I am doing eachPost in the HTML code which will loop through all my Posts. As I am looping through my Posts, I am trying to save the post id in a Hidden input so I can use that id for each post to query the Authors collection and display the Author name. I added a console.log and it writes undefined every time for my postId - any idea why this is doing this or a better way to solve this problem (without embedding author information in Posts)?
html:
<template name="dashboard">
{{#each eachPost}}
<input type="hidden" id="postId" value="{{_id}}">
<p>{{authorName}}</p>
{{/each}}
</template>
js:
Template.dashboard.helpers({
eachPost: function()
{
return Posts.find({});
},
authorName: function()
{
var postId = $('#postId').val();
console.log(postId);
return Authors.findOne({_id: postId});
}
});
Thanks in advance, I really appreciate any and all help!
I think there is no need to store it in a hidden element
you can access it by using this._id in authorname helper
You can do like below
<template name="dashboard">
{{#each eachPost}}
<p>{{authorName}}</p>
{{/each}}
</template>
Template.dashboard.helpers({
eachPost: function()
{
return Posts.find({});
},
authorName: function()
{
console.log(this._id);
return Authors.findOne({_id: this._id});
}
});
Try this,it may work for you

Update list of divs after ajax post

I have this code that loads some div
<input class="input-large" placeholder="Notat" id="txtNewNote" type="text">
<button class="btn" id="btnAddNote" type="button">Lagre</button>
#foreach (var item in Model.UserNotes)
{
<div class="alert" id="divNote-#item.UserNoteID">
<button type="button" class="close" onclick="NoteDeleteClicked(#item.UserNoteID)" id="btnCloseNote">×</button>
<strong>#item.User.Name - #item.Added</strong><br />
#item.Text
</div>
}
Then I run this javascript when the user enter more text:
var Text = $("#txtNewNote").val();
var Added = Date.now();
vvar UserID = $("#hiddenUserID").text();
$.post("/api/apiUserNotes",{ Text : Text, Added: Added, UserID: UserID }, function() { //need functianality for updating the list of divs above });
Anyone can point me in the right direction here. I cannot just create the div after the post are done because I need to fetch data from the database so that the information in the div are correct.
There are mainly two approaches:
apiUserNotes returns HTML - This approach is a bit easier to maintain since you have only one template. But it is also more restricting in the sense that HTML is good for showing, but not so much for manipulating it.
apiUserNotes returns JSON - This is more flexible since a JSON API can be consumed by pretty much anything, from HTML to native iOS or Android apps, as it's much easier to manipulate. It's also more work though, as you then have templates both on the server-side (your Razor view) as well as on the client-side (your HTML/JavaScript).
This is more or less what #1 would look like:
You first make a partial view to display user notes:
_UserNotes.cshtml:
<div id="user-notes">
#foreach (var item in Model.UserNotes)
{
<div class="alert" id="divNote-#item.UserNoteID">
<button type="button" class="close" onclick="NoteDeleteClicked(#item.UserNoteID)" id="btnCloseNote">×</button>
<strong>#item.User.Name - #item.Added</strong><br />
#item.Text
</div>
}
</div>
Once you have this partial view, you can consume it from your view:
<input class="input-large" placeholder="Notat" id="txtNewNote" type="text">
<button class="btn" id="btnAddNote" type="button">Lagre</button>
#Html.Partial("_UserNotes")
And from your apiUserNotes action:
public PartialViewResult apiUserNotes(string text, DateTime added, int userID)
{
// TODO: save new note
var notes = yourRepo.GetUserNotes(userID);
return PartialView("_UserNotes", notes);
}
Finally, your client-side scripts simply overrites the div containing all user notes:
var data = {
text = $("#txtNewNote").val(),
added = Date.now(),
userID = $("#hiddenUserID").text()
};
$.post("/apiUserNotes", data, function (result) {
$('#user-notes').html(result);
});
There are of course much more efficient ways of doing this. For example, you don't need to reload all user notes; only the one your just added. But hopefully this gets you started.
You can access above Divs in following way, and update as per your requirement.
$.post("/api/apiUserNotes",{ Text : Text, Added: Added, UserID: UserID },
function()
{
//need functianality for updating the list of divs above
$("div.alert").each(....)
});

Categories