Meteor delete list item with selected class - meteor

I am trying to add a delete function that if i click on a list item(template) it adds a selected class to the li. I then want be able to click a button that finds the li with a class of selected and passes the data to a meteor method that removes the data from a collection. How do i access this data.
I have tried a few ways but this is what i have so far.
sidebar.js
Template.Sidebar.events({
'click .button-collapse': function() {
console.log("here")
$(".button-collapse").sideNav();
},
'click #delete i': function() {
Meteor.call('deleteListItem', $( "li.selected" )._id);
}
})
sidebar.html
<template name="Sidebar">
<ul id="slide-out" class="side-nav fixed grey darken-3">
<li class="action-bar">
<span id="add-new" data-target="modal-add" class="modal-trigger"><i class="small material-icons">add</i></span>
<span id="save"><i class="small material-icons">note_add</i></span>
<span id="rename"><i class="small material-icons">mode_edit</i></span>
<span id="delete"><i class="small material-icons">delete</i></span>
<span data-activates="slide-out" id="close" class="button-collapse close "><i class="small material-icons right">reorder</i></span>
</li>
<!-- Load save items-->
{{#if Template.subscriptionsReady}}
{{#each userSaves}}
{{>ListItem}}
{{/each}}
{{else}}
<p>Loading</p>
{{/if}}
</ul>
<i class="material-icons">menu</i>
<!-- Modal form to add new simulator file -->
<!-- Modal Structure -->
<div id="modal-add" class="modal">
<div class="modal-content">
<h4>New Simulator</h4>
{{> quickForm collection=saves id="newSimulator" type="insert" buttonClasses="modal-action modal-close btn waves-effect waves-light" buttonContent="Add"}}
</div>
</div>
</template>
list class
Meteor.methods({
deleteListItem: function(id) {
Saves.remove(id);
}
});

You can not retrieve the ID of an object like that. Either your button has to be inside the document you're gonna remove or you have to include the _id in your HTML attributes and get that value.
So simply add data-value="{{_id}}" to your <li> in your loop and get the selected like this:
//this will retrieve the id of one selected element only
//you need to use each/forEach if you want to remove multiple
$('li.selected').attr('data-value')
EDIT:
Another solution would be using Session. Since you click on an item to select it, you can set a Session in that event
//in the click event where you select a list item
Session.set('itemId', this._id);
//in Sidebar events
'click #delete i': function() {
const itemId = Session.get('itemId');
Meteor.call('deleteListItem', itemId);
}
EDIT 2:
If you use Session solution, don't forget to remove the Session when the template is destroyed. Otherwise (for your use case) when you go to another route and come back without deleting a selected item and click the delete button, it will delete the document even though it doesn't appear to be selected.
Template.templateName.onDestroyed(function(){
Session.set('sessionName', undefined);
});

Related

Meteor collection not displaying

I am trying to display my Orders collection. The Orders collection schema has a select field populated from the Items collection.
I cannot seem to get the Orders collection to display on my admin's template. I have verified that I am posting to the collection with Mongol and I'm not receiving any errors in console. I have also tried displaying it in a tabular table with no luck.
Any ideas? I'm still learning meteor and have been staring at this screen for hours.. maybe need some fresh air now and a fresh look later...
/collections/orders.js
Orders = new Mongo.Collection("orders");
Orders.attachSchema(new SimpleSchema({
station: {
type: String,
label: 'Station',
max: 2,
},
itemselect: {
type: [String],
label: 'Items',
optional: false,
autoform:{
type: "select",
options : function() {
return Items.find().map(function (c) {
return {label: c.name , value: c._id}
})
}
}
}
}));
/templates/admin.html
<template name="ordersTable">
<div class="admin">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#collapse2">
<button type="button" class="btn btn-default navbar-btn">Orders</button>
</a>
</h4>
</div>
<div id="collapse2" class="panel-collapse collapse">
<div class="panel-body">
<ul>
{{#each orders}}
<li>{{> station}}</li>
{{/each}}
</ul>
</div>
<div class="panel-footer">
{{> addOrderFormAdmin}}
</div>
</div>
</div>
</div>
</template>
/templates/admin.js < This ended up being my problem..
Template.dashboard.rendered = function() {
return Orders.find();
};
**should be a helper.. so this instead:
Template.ordersTable.helpers({
orders: function () {
return Orders.find();
}
});
Insert Order Form
<template name="addOrderFormAdmin">
{{> autoformModals}} <!-- this is required for this modal to open -->
{{#afModal class="btn btn-primary" collection="Orders" operation="insert"}}
Add New Order
{{/afModal}}
</template>
Your code inside your dashboard rendered callback does not make any sense. I think you want to create a helper function for your ordersTable template instead:
Template.ordersTable.helpers({
orders: function () {
return Orders.find();
}
});
Furthermore, please note that Template.myTemplate.rendered is deprecated in Meteor version 1.0.4.2 (and later), use Template.myTemplate.onRendered instead.
Check publish and subscribe if you have removed the autopublish package. First, see if you can reach the collection through the console(on the web page, not the command line). Second, see if the collection is updated after your posts (for this you could use the command line by typing "meteor mongo" while the server is running or just download Robomongo).

Data available ("this") from a template event handler

projects.html
{{#if projects}}
{{#each projects}}
<div class="project-item">
<div class="project-name">
{{name}}
</div>
<div class="project-settings">
<span class="rename">Rename</span>
<span class="edit">Edit</span>
<span class="delete">
<!-- Here -->
</span>
</div>
</div>
{{/each}}
{{/if}}
projects.js
Template.Projects.events({
"click .project-item .delete": function (e, template) {
e.preventDefault();
debugger
// "this" refers to the specific project
}
});
In an event handler, I noticed "this" conveniently refers to a specific object inside the template where the event is related to. For example, in this case, the delete button is inside each projects block, and the handler for the delete button has this = some project. This is convenient, but I'd like to know the scopes and rules more completely. Can someone explain in briefly and point me to the right document?
This is a data context sensitive feature. Basically, there is a lexical scope in spacebars helpers. Have a look at this: http://devblog.me/no-data-context.html
The original pull request is here: https://github.com/meteor/meteor/pull/3560

Wrong data context in modal

I can't figure out why this Bootstrap modal receives the wrong data context.
Let's begin with my templates (excluding the modal for now). The first iterates through a list of items fetched by itemsList:
<template name="CategoryItems">
<ul>
{{#each itemsList}}
{{> Item}}
{{/each}}
</ul>
</template>
itemsList looks like this, by the way:
itemsList: function() {
return Items.find()
}
The inside template, Item, details just how these items should appear:
<template name="Item">
<li>
<span class="item-name">{{name}} </span>
<a href="#" class="anchor-item-edit" data-toggle="modal" data-target="#edit-item-modal">
<span class="fa fa-2x fa-pencil-square-o"></span> //Font Awesome icon
</a>
{{> EditItemModal}}
</li>
</template>
So basically it displays the name of the item fetched from the database and then provides an edit button that opens the edit-item-modal. The modal itself is placed here (it's hidden by default) so that it gets the correct data context, however that doesn't seem to work.
When the edit link is clicked, the modal opens. Excluding a lot of markup, it looks like this:
<template name="EditItemModal">
<div class="modal fade" id="edit-item-modal" tabindex="-1" role="dialog" aria-hidden="true">
<h4>{{name}}</h4>
</div>
</template>
The name, however, always displays the name of my first item in the list, ignoring what I actually clicked on.
A very strange thing, though, is that if I include a helper check inside the modal like so,
<template name="EditItemModal">
{{checkDataContext}}
//the other stuff
</template>
and makes the helper look like this,
Template.EditItemModal.helpers({
checkDataContext: function() {
console.dir(this)
}
})
then all the correct items are spit out in the console as soon as I load the page.
What's going on here?
Your modal markup only defines one shared ID between all of your modals, which is not valid HTML and ends up being the root of your problem.
When you click on any button triggering the modal, it's going to show up the first modal it finds in your markup, which always happens to be the first one.
You need to decorate your modals IDs with your items IDs (since they come from a Mongo.Collection), your markup will no longer contain duplicated modal IDs and your code will run as expected.
<template name="EditItemModal">
<div class="modal fade" id="edit-item-modal-{{_id}}" tabindex="-1" role="dialog" aria-hidden="true">
<h4>{{name}}</h4>
</div>
</template>
<template name="Item">
<li>
<span class="item-name">{{name}} </span>
<a href="#" class="anchor-item-edit" data-toggle="modal" data-target="#edit-item-modal-{{_id}}">
<span class="fa fa-2x fa-pencil-square-o"></span> //Font Awesome icon
</a>
{{> EditItemModal}}
</li>
</template>

Why does my click event not fire when I add each blocks into my template to iterate through a collection (Meteor)

The event fires only when I remove the each blocks. What the event does is apply a vertical accordion slide down transition on an element. What I want to do is add the same slide down transition to all my documents when they are displayed in my views.
Right now, when I add an each block to iterate through my collection and display all the documents from my collection, the slide down event stops working.
Here's my template:
<template name="auctionsList">
<div class="container">
<div id='cssmenu'>
<ul>
{{#each auctions}}
{{>auction}}
{{/each}}
</ul>
</div> <!-- end cssmenu -->
</div><!-- end container -->
{{#if isReady}}
{{#if hasMoreauctions}}
<a class="load-more btn btn-default center-block text-uppercase" id="loadMore" href="#" style="margin-top:10px;">Load More</a>
{{/if}}
{{else}}
<div class="loading">{{>spinner}}</div>
{{/if}}
</template>
<template name="auction">
<li class='has-sub'>
<a href='#'>
<div class="auction-image">
<img src="brand_logos/DominosPizza.png" class="img-responsive" height="200" width="200">
</div>
{{> durationLeft}}
</a>
<ul>
<li><a href='#'>
<span>Sub Product</span></a></li>
</ul>
</li>
</template>
Here's my rendered/helper
Template.auctionsList.rendered = function () {
$('#cssmenu li.has-sub>a').on('click', function(){
$(this).removeAttr('href');
var element = $(this).parent('li');
if (element.hasClass('open')) {
element.removeClass('open');
element.find('li').removeClass('open');
element.find('ul').slideUp();
}
else {
element.addClass('open');
element.children('ul').slideDown();
element.siblings('li').children('ul').slideUp();
element.siblings('li').removeClass('open');
element.siblings('li').find('li').removeClass('open');
element.siblings('li').find('ul').slideUp();
}
});
}
Template.auctionsList.helpers({
auctions: function () {
return Template.instance().userauctions();
}
});
Template.auctionsList.events({
'click #cssmenu li.has-sub>a' : function(event, template) {
$(this).removeAttr('href');
var element = $(this).parent('li');
if (element.hasClass('open')) {
element.removeClass('open');
element.find('li').removeClass('open');
element.find('ul').slideUp();
}
else {
element.addClass('open');
element.children('ul').slideDown();
element.siblings('li').children('ul').slideUp();
element.siblings('li').removeClass('open');
element.siblings('li').find('li').removeClass('open');
element.siblings('li').find('ul').slideUp();
}
}
});
You should move this from rendered to events. $('#cssmenu li.has-sub>a').on('click', function(){ }
Put the event within the auction template where your <li> tag exists.
Updated
Couple of things based on the gists you published:
1) you should try not use script tags directly in the template - i havent seen this often if at all. Move all this code to
Template.accordion.rendered = function(){ //here };
or better make it work in auctionsList because then its not duplicated x times for every each iteration.
But what would be even better is not to have the 'on' event at all. You should use a meteor event... like you mentioned in your earlier post. Meteor events have access to 'this' which is the current context item.
I would try put it in a template event on the master auctionsList template and later on worry about how it will work with the sub templates.
2) I dont think you need to add everything into another template like you have, the accordion template doesnt really need to exist, you can probably get away with putting it in the auction template (consider using even auctions to do the click event because if you do it that way your js snippet wont be repeated x times per post listing.You only need this once..
3) When you create the li in the auction template give it an ID at that point
<li id={{new accordion code in auction template}}>
You can then reference this id on your template click event. If you are new to meteor do yourself a favor and do the following in a click event to better understand what is going on (you will most likely find the ID you need within one of the values - most likely this:
Template.YourTemplate.events({
'click any button in your each': function(event,bl,value)
{
console.log(event);
console.log(bl);
console.log(value);
//and most importantly
console.log(this);
}
});
I know its not "an answer" but i hope it leads you in the right direction.

Semantic-ui search - access object properties not used in 'searchFields'

I am using the Semantic UI search for the title property of my data object. data has other fields and I want to access them when an object is selected. For example, I want to put the value from the uuid property in a hidden input.
Is there a Semantic UI way of doing this? - I couldn't figure it out from the documentation (I know I can go and search through all data.title's for the selected one, but ... there probably is another way).
$('.ui.search').search({
source: data,
searchFields: [
'title'
]
,onSelect : function(event){
//...some other code
$("#tags").append('<input type="hidden" value="'+ value_from_my_uuid_field +'"');
}
});
<div class="ui search">
<div class="ui icon input">
<i class="search icon"></i>
<input class="prompt" type="text" placeholder="Search subjects...">
</div>
<div class="results"></div>
</div>
Thank you.
The search widget has an onSelect callback you can register (docs) When your user selects a suggestion from the search response, your callback will be called with the selection:
$searchWidget.search({
onSelect: function(result) {
// do something with result.id or whatever
}
});
I had a similar problem (but with ajax source data) and I finally ended up adding hidden content-tags to the results (on server side) like <div style='display:none;' class='id'>12345</div>.
And in the onSelect-callback I search the result (with jquery) for this content:
onSelect : function(event){
var $result = $(this);
var id = $result.find(".id").html();
...
// Return 'default' triggers the default select behaviour of the search module.
return "default";
}
HTH
Semantic UI actually provides a way of accessing any of the object's properties.
I used both dropdown and search classes, as shown in the docs with hidden input values for the properties.
<template name="search_drop">
<div class="ui floating dropdown labeled search icon button">
<i class="search icon"></i>
<span class="text">Search subjects...</span>
<div class="menu">
{{#each subjects}}
<div class="item" data-id="{{this.id}}" data-value="{{this.value}}" data-child="{{this.haschildren}}">{{this.name}}
</div>
{{/each}}
</div>
</div>
</template>
subjects contains my objects with id, name, value, haschildren properties.
Template.search_drop.rendered = function(){
$(".dropdown").dropdown({
onChange: function(value, text, $choice){
console.log(value); //will output the equivalent of {{this.name}}
console.log($choice[0].attributes); //list of attributes
console.log($choice[0].attributes["data-id"].value); //the equivalent of {{this.id}}
}
});
}

Resources