Remove mongodb documents from a collection by an array of _ids - javascript

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
}
});
}
});

Related

Meteor: show each item from an array from mongo in separate list tag

Note: Whole code can be found here:
https://github.com/Julian-Th/crowducate-platform/tree/feature/courseEditRights
Currently, all items from an array are displayed in one single list instead of a separate list tag:
My JS (I commented out some prior approaches):
Template.modalAddCollaborators.events({
'click #js-addCollaborator' : function (event) {
var collaboratorName = $('#collaboratorName').val(); //
Courses.update(
{ _id: this._id },
{ $addToSet: {canEditCourse: collaboratorName } }
)
$('#collaboratorName').val("");
}
});
Template.modalAddCollaborators.helpers({
'addedCollaborators': function () {
return Courses.find();
//return Courses.find({_id: this._id}, {$in: "canEditCourse"});
//return Courses.distinct("canEditCourse");
}
});
My HTML:
<template name="modalAddCollaborators">
<div id="modalAddCollaborators" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Manage Your Collaborators</h4>
</div>
<div class="modal-body">
<form class="form" role="form">
<ul class="list-group">
{{#each addedCollaborators}}<li class="list-group-item">{{canEditCourse}}</li>{{/each}}
</ul>
<div class="form-group">
<input type="text" id="collaboratorName" placeholder="add a collaborator ...">
<button type="button" id="js-addCollaborator" class="btn btn-success">Add</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</template>
MY JSON:
{
"_id" : "rmZEFmfoBwf4NwqX4",
"title" : "Love",
"coverImageId" : "P7PyR6x64uCSX7X9m",
"author" : "test",
"keywords" : [
"test"
],
"published" : "true",
"about" : "test",
"canEditCourse" : [
"wicazRk3EsThE5E8W",
"Jolle",
"jolle",
"vW59A6szZijMDLDNh"
],
"createdById" : "wicazRk3EsThE5E8W",
"dateCreated" : ISODate("2015-12-27T15:06:28.272Z")
}
Any help appreciated, thank you.
Courses.find(); returns a cursor and not an array. Use fetch() method instead:
Template.modalAddCollaborators.helpers({
'addedCollaborators': function () {
return Courses.find().fetch();
}
});
In your template, create nested {{#each}} blocks with the first one iterating over the courses array and the next each block getting the canEditCourse array as the parameter. Inside the block, you can use this to reference the element being iterated over, something like the following for example:
<template name="modalAddCollaborators">
{{#each addedCollaborators}}
<h1>{{title}}</h1>
<ul class="list-group">
{{#each canEditCourse}}
<li class="list-group-item">{{this}}</li>
{{/each}}
</ul>
{{/each}}
</template>
It looks like you are storing two types of values in the canEditCourse:
String - Meteor.userId
String - username
It may be good to store either the userId or the username, but perhaps not both.
UserID solution
In this approach, you store the User IDs in the canEditCourse array, and then use a collection helper to retrieve the username for display:
Courses.helpers({
"getCollaboratorUsernames": function () {
// Get collaborator IDs array
var userIds = this.canEditCourse;
// Get the users, using MongoDB '$in' operator
// https://docs.mongodb.org/v3.0/reference/operator/query/in/
var users = Meteor.users.find({_id: {$in: userIds}).fetch();
// placeholder array for usernames
var collaboratorUsernames = []
// Get username for each user, add it to usernames array
users.forEach(function (user) {
// Add current username to usernames array
collaboratorUsernames.push(user.profile.username);
});
return collaboratorUsernames;
}
});
Also, it may be cleaner if the template helper were only to return the array of userIds, as opposed to a course object (Courses.find().fetch()).
Inputting UserIDs
You may choose a typeahead approach for inputting user IDs, similar to how courses are categorized in Crowducate.
Note: you will need a publication and subscription to make usernames/IDs available for the Selectize input.
Displaying Usernames
The other key component will be how to display the usernames as separate Boodstrap tag elements. You can iterate over the returned collaboratorUsernames array like so:
{{# each getCollaboratorUsernames }}
<span class="label label-info">{{ this }}</span>
{{/ each }}
Note: make sure the course collaborator users are available via a publication/subscription:
In server code:
Meteor.publish('courseCollaborators', function (courseId) {
// Get the course object
var course = Courses.findOne(courseId);
// Get course collaborator IDs
var collaboratorIds = course.canEditCourse;
// Consider renaming the 'canEditCourse' field to 'collaboratorIds'
// Then, it would look like
// var courseCollaboratorIds = course.collaboratorIds;
// Or, you could even skip that, and the code would still be literate
// Get course collaborators
var collaborators = Meteor.users.find({_id: {$in: collaboratorIds}).fetch();
return collaborators;
});
Then, in your template.created callback:
Template.modalAddCollaborators.created = function () {
// Get reference to template instance
var instance = this;
// Get reference to router
var route = Router.current();
// Get course ID from route
var courseId = route.params._id;
// Subscribe to Course Collaborators, template level
instance.subscribe("courseCollaborators", courseId);
};
Be sure to wrap all of your code for creating the Selectize widget in an if (instance.subscriptionsReady()) {} block:
Template.modalAddCollaborators.rendered = function () {
// Get reference to template instance
var instance = this;
// Make sure subscriptions are ready before rendering Selectize
if (instance.subscriptionsReady()) {
// Get course collaborator usernames/IDs
// Render the Selectize widget
// User should see usernames
// UserID is saved to collection
}
};

Getting the value of element clicked on in Emberjs

I have a series of labels that I would like to retrieve the text from when the user clicks on them. I thought the jquery might be doable in Emberjs:
$(this).attr('text');
This however, doesn't work with Ember. My component view is this:
<ul class="list-inline">
{{#each model as |model|}}
<li {{action "sendToInput"}} class="label label-default">{{model.name}}</li>
{{/each}}
</ul>
I would like to retrieve that model.name value via the action "sendToInput".
In my component js file I have tried:
actions: {
sendToInput() {
$(this.target).attr('text');
}
}
I have also tried:
this.innerHTML;
$(this).text();
this.get('text');
$(this).val();
I have also opened up the console and dug through this and cannot seem to find where they store the element clicked on.
The documentation doesn't mention this and there's no issue I can find on the Github for ember-cli.
Thanks.
The common way is to pass a model name as an action parameter like this:
<ul class="list-inline">
{{#each model as |model|}}
<li {{action "sendToInput" model.name}} class="label label-default">{{model.name}}</li>
{{/each}}
</ul>
and then handle it in the action:
actions: {
sendToInput(name) {
// do something with name
}
}
But if you still need to access the exact element's html, there is another workaround:
export default Ember.Component.extend({
didInsertElement () {
this.$().on('click', '.label', function (event) {
$(this).text();
})
}
});

Mongo pull in Meteor not pulling

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.

How to bind a Kendo UI list to a ViewModel?

I have this fallowing list:
<ul data-template="view" data-bind="source: contacts"></ul>
...
<script type="text/x-kendo-template" id="view">
<li>
<div data-role="button" data-bind="click: viewContact">
<h4>#: first_name #</h4>
</div>
</li>
</script>
var someClass = kendo.observable({
title : 'Contact',
contacts : [],
getContacts: function () {
var data = new Contacts().get();
$.when(data()).then(function (data) {
someClass.contacts = data;
});
}
});
I want to assign data from getContacts to products and populate the template with results as they come in.
Any ideas?
In your question you are talking about "products" but in the code snippet i see "contacts".
In any case, you need to do the following:
someClass.set('contacts', data)
Note: assuming you have contacts as a property on the viewmodel.
Since its an observable object, if you don't use get() & set() methods, kendo wont know who all are observing that property. If you use set() method to set new value, Kendo will propagate the change and will notify the listeners of the property.

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

Categories