I am following the meteor tutorial, and along with adding a form to add a task, I made a form to delete a task by its text value as well.
Template.body.events({
"submit .new-task": function (event) {
// Prevent default browser form submit
event.preventDefault();
// Get value from form element
var text = event.target.text.value;
// Insert a task into the collection
Tasks.insert({
text: text,
createdAt: new Date() // current time
});
// Clear form
event.target.text.value = "";
},
"submit .delete-task": function (event) {
// Prevent default browser form submit
event.preventDefault();
// Get value from form element
var text = event.target.text.value;
// Insert a task into the collection
Tasks.remove({
text: text,
});
// Clear form
event.target.text.value = "";
}
});
}
The new-task form works fine, but the delete-task form doesn't work. I tried a similar query using shell (meteor mongo) and it worked. What error do I have here?
EDIT:
Here's the html as well:
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
<form class="new-task">
<h2> Add a task </h2>
<input type="text" name="text" placeholder="Type to add new tasks" />
</form>
<form class="delete-task">
<h2> Delete a task </h2>
<input type="text" name="text" placeholder="Type to delete tasks" />
</form>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li class="{{#if checked}}checked{{/if}}">
<button class="delete">×</button>
<input type="checkbox" checked="{{checked}}" class="toggle-checked" />
<span class="text">{{text}}</span>
</li>
</template>
Uncaught Error: Not permitted. Untrusted code may only remove documents by ID. [403]
This shows up in the browser's javascript console when you try to run the deletion by text as you've described in your code / problem statement. This was a design decision by the Meteor team in 0.5.8 and is discussed in this previous question.
You can have this functionality if you create a server side method. Otherwise your client side code will have to delete by ID. Something like this:
Change your Tasks.remove({text: text}) call to Meteor.call('removeTaskByText', text) on the client code, and on the server side define:
Meteor.methods({
'removeTaskByText': function(text){
Tasks.remove({text: text});
}
});
Related
The finished product is just supposed to have a checkbox next to each entry, and the option to edit or delete each item. I'm nowhere near that as I can't even get an item to post.
Here's are the files that I have: HTML, CSS, JS.
Also, I'm sorry for the formatting.I didn't paste the CSS as that's not an issue as far as I'm concerned.
HTML:
var list = document.getElementById('list'); //The unordered list.
var entry = document.createElement("li"); //Whatever this is. I'm assuming a command saved into a variable (?).
//var todolist = list.getElementsById('li');
// var btn = document.getElementById('ToDoButton');
//
// btn.addEventListener("click", function(){
// todolist.appendChild('li');
// });
/* Upon submission of the item in the text field, the string is stored in inputValue
and theText becomes a text node of inputValue, which is appended to the end of the variable
entry. This means that there should be a new item added to the unordered list with the information
found in the text field, but it doesn't show.
Also, as far as I know, this is only if the Add button is clicked, and not upon
pressing Enter while in the text box. */
function newElement() {
var inputValue = document.getElementById("textBox").value;
var theText = document.createTextNode(inputValue);
entry.appendChild(theText);
if (inputValue !== '') {
document.getElementById(list).appendChild(entry);
}
}
<!DOCTYPE html>
<html>
<head>
<title>To-Do List</title>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<div class="toDoList">
<h4>To-Do List</h4>
<form target="_self">
<!-- This is so that the submitted information doesn't go anywhere but to the current page. -->
<input type="text" name="toDoList" placeholder="To-Do" id="textBox">
<input type="submit" value="Add" onclick="newElement()" id="ToDoButton">
<!-- newElement() is a JS function that will add the information in the search bar to the unordered list below. -->
</form>
</div>
<section id="main">
Tasks:
<ul id="list">
<!-- These are dummy values for the unordered list. The idea
is that the next item should be placed beneath them. -->
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
</section>
<script type="text/javascript" src="todo.js">
</script>
</body>
</html>
This is a short example of how to dynamically add elements on the page. User types in a to do item, then clicks the button. When they click the button, we get the value from the input box, create a new list item element, and then append it to the dom.
function addItem() {
var el = document.createElement('li')
var val = document.getElementById('item-val').value;
el.innerHTML = val;
document.getElementById('list').append(el);
}
<input id="item-val" type="text" />
<button onclick="addItem()">Add an item</button>
<ul id="list"></ul>
A few problems:
1) When you click the button, it submits the form. This causes your page to refresh, so any and all changes made by the JavaScript are lost, because you re-load the page from the server. Changing it to <button type="button" means it doesn't cause a postback any more. To be honest you probably don't actually need <form> here at all if you aren't going to send the data to the server.
2) Better to put your list and entry variables inside the function - globals are best avoided if you can, to reduce accidental scope issues. Also you need to create a new entry each time, not keep appending the same one.
3) document.getElementById(list).appendChild(entry) doesn't work because list is already an object representing an element - it's not a string containing an ID. so list.appendChild() is correct here - i.e. you can just call the appendChild() function on the existing object directly.
4) Optionally, you don't really need the separate textNode object - just set the innerText property of the list item instead.
5) Optionally again, but considered best practice: I declared an unobtrusive event handler (using addEventListener) rather than putting it inline inside the HTML. This is generally considered to make the code more maintainable and traceable, as all the script is held in one place, separate from the HTML.
Here's a fixed version:
document.querySelector("#ToDoButton").addEventListener("click", newElement);
/* Upon submission of the item in the text field, the string is stored in inputValue
and theText becomes a text node of inputValue, which is appended to the end of the variable
entry.*/
function newElement() {
var list = document.getElementById('list'); //The unordered list.
var entry = document.createElement("li"); //a new list item
var inputValue = document.getElementById("textBox").value;
if (inputValue !== '') {
entry.innerText = inputValue;
list.appendChild(entry);
}
}
<!DOCTYPE html>
<html>
<head>
<title>To-Do List</title>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<div class="toDoList">
<h4>To-Do List</h4>
<form target="_self">
<input type="text" name="toDoList" placeholder="To-Do" id="textBox">
<button type="button" id="ToDoButton">Add</button>
</form>
</div>
<section id="main">
Tasks:
<ul id="list">
<!-- These are dummy values for the unordered list. The idea
is that the next item should be placed beneath them. -->
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
</section>
<script type="text/javascript" src="todo.js">
</script>
</body>
</html>
Your main issue is that you were using and <input type="submit"> when you were expecting the behavior of <input type="button"> with a click event listener:
document.querySelector('#ToDoButton').addEventListener('click', newElement);
function newElement() {
var inputValue = document.getElementById("textBox").value;
var theText = document.createTextNode(inputValue);
var liEl = document.createElement('li');
liEl.appendChild(theText);
if (inputValue !== '') {
document.getElementById('list').appendChild(liEl);
}
}
<head>
<title>To-Do List</title>
<link rel="stylesheet" href="todo.css">
</head>
<body>
<div class="toDoList">
<h4>To-Do List</h4>
<input type="text" name="toDoList" placeholder="To-Do" id="textBox">
<!--The input type needs to be "button, not "submit"-->
<input type="button" value="Add" id="ToDoButton">
</div>
<section id="main">
Tasks:
<ul id="list">
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
</section>
<script type="text/javascript" src="todo.js">
</script>
</body>
I am trying to make a basic recipe form that has another form inside it. The inner form uses a collection. I am trying to input that collection into the larger form. I am not sure how to write the code on that particular part.
Ingredients = new Mongo.Collection('ingredients');
Recipe = new Mongo.Collection('recipe');
'submit .recipe_submit': function(event){
Recipe.insert({
recipeServing:event.target.mealserving.value,
recipeIngredients:event.target.,
recipeDirection:event.target.recipedirections.value
})
}
<template name="addingredients">
<h2>Enter Ingredients</h2>
<form class="form-group">
Food Item</span><input name="ingredientType" type="text">
Quantity</span><input name="ingredientQuantity" type="text">
Amount</span><input name="ingredientAmount" type="text" >
<button type="submit">Add</button>
</form>
<div class="col-md-12">
<ul>
{{#each ingredients}}
<li >
<div>
<div>{{this.foodItem}}</div>
<div>{{this.foodQuantity}}</div>
<div>{{this.foodAmount}}</div>
<div class="delete"></div>
</div>
</li>
{{/each}}
</ul>
</div>
</template>
Normally I would use he name="" from the input, but I don't see how this works in this case. I also don't need it to import the delete button either. Any help would be awesome.
Your event needs to extract each value out properly. Here is an example of how you could do it:
Template.addingingredients.events({
'submit .recipe_submit': function(event, template){
Recipe.insert({
recipeType: template.$("input[name=ingredientType]").val(),
recipeQuantity: template.$("input[name=ingredientQuantity]").val(),
recipeAmount: template.$("input[name=ingredientAmount]").val()
})
}
});
I had to rename the keys in your recipe to match the field names, you don't have a 'serving', 'ingredients' or 'direction' elements on your form. I don't think this is what you're looking for in your question, though.
The key point is ff you want to extract a value from an element you can use something like $("input[name=ingredientQuantity]").val() to get the value for an element like <input name="ingredientQuantity" type="text"/>
You can either reference or embed Ingredients inside Recipes. Since the ingredient information is frequently used with the recipe information, I would recommend embedding the Ingredients, but you will first need to grab the ingredients from the inner form with JQuery, which requires distinguishable selectors:
{{#each ingredients}}
<li class="ingredient">
<div class="item">{{this.foodItem}}</div>
<div class="quantity">{{this.foodQuantity}}</div>
<div class="amount">{{this.foodAmount}}</div>
<div class="delete"></div>
</li>
{{/each}}
When the form is submitted, we create an array of Ingredient objects and then embed them as a property of Recipe:
Template.addingingredients.events({
'submit .recipe_submit': function(event, template){
var ingredients = [];
$("li.ingredient").each(function() {
var selector = this;
ingredients.push({
foodItem: $(selector).find(".item").text(),
foodQuantity: $(selector).find(".quantity").text(),
foodAmount: $(selector).find(".amount").text()
});
});
Recipe.insert({
recipeServing: event.target.mealserving.value,
recipeIngredients: ingredients,
recipeDirection: event.target.recipedirections.value
});
}
});
I seem to be able to hide the resource container using
resource.parent().parent().hide();
but I don't understand why the input value is not clearing with
resource.parent().siblings('.resource-value').children('input').val('');
when I use
resource.parent().siblings('.resource-value') I get the parent of the input value but adding .children('input').val('') on top of that does nothing or if I add .children('input:text').val('')
I have very similar code for something else which works just fine, looked at other questions and not sure what I'm missing.
function removeResource(resource) {
'use strict';
//hide resource on screen
resource.parent().parent().hide();
//set resource text value to ''
resource.parent().siblings('.resource-value').children('input').val('');
}
(function($) {
'use strict';
$(function() {
$('#resources').on('click', '.remove-resource', function(evt) {
// Stop the anchor's default behavior
evt.preventDefault();
// Remove the image, toggle the anchors
removeResource($(this));
});
});
})(jQuery);
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="resources">
<div class="resource">
<div class="resource-value">
<input type="text" name="resources[]" value="1" />
</div>
<p class="hide-if-no-js"><a title="remove resource" href="javascript:;" class="remove-resource">remove resource</a > </p>
<!-- .hide-if-no-js -->
</div>
<div class="resource">
<div class="resource-value">
<input type="text" name="resources[]" value="2"/>
</div>
<p class="hide-if-no-js"><a title="remove resourcee" href="javascript:;" class="remove-resource">remove resource</a> </p>
<!-- .hide-if-no-js -->
</div>
</div>
</body>
<html/>
Tried your code and worked fine for me in terms of the actual value of the field clearing, though in inspector the HTML element still has the value attribute showing.
You can use
.attr('value','')
to clear that too http://jsfiddle.net/bvtg93dm
You just have to change the value witch jquery to set "" (so, empty).
input.attr('value','')
Try to log your sibling element with
Try to change your removeResource function to
function removeResource(resource) {
'use strict';
//hide resource on screen
var parent = resource.parent().parent();
parent.hide();
// log your element
console.log(parent.find('.resource-value input'));
// make sure you are getting an element you need
console.log(parent.siblings('.resource-value').childer('input').get(0);
//set resource text value to ''
parent.find('.resource-value input').val('');
}
I have this code which was working earlier. Then I started putting the code into small small functions and now it is not working. I can see that it is adding list item but automatically removing it also. Please guide -
<body>
<header>
<h1>Your Ration List</h1>
</header>
<div id="container">
<form class="shopList-form">
<input id="add" type="text" placeholder="Type new item here" />
</form>
<ul id="item_list">
<li id="base" class="hidden">
<form>
<input class="check" type="checkbox" /> <span class="item">Item</span>
</form>
<button class="delete_item hidden"></button>
</li>
</ul>
JQuery code -
$(document).ready(function () {
/* Get user input */
getItem();
function getItem() {
$('input#add').keydown(function (event) {
if (event.keyCode == 13) {
addItem();
}
});
}
function addItem() {
$('li#base').clone(true).appendTo('#item_list').removeAttr('id').removeClass('hidden');
$('ul#item_list>li:last>form>span').text($('input#add').val());
$('input#add').val("");
}
});
Full code can be found at this JSFiddle -
http://jsfiddle.net/varunksaini/Zjxq5/8/
Since it is a form, pressing enter not only triggers your function but also submits the form (since there is no action it submits to itself) so the page actually refreshes and that is why the new <li> is gone.
All you need to do is add return false to getItem.
see fiddle: http://jsfiddle.net/Zjxq5/9/
The problem you have with your script is that you are using a form and when you press enter it submits the form to the server and reloads the page. You can use the preventDefault() function to avoid that.
$('input#add').keydown(function (event) {
if (event.keyCode == 13) {
event.preventDefault();
addItem();
}
});
Example: http://jsfiddle.net/rdnKq/1/
I work on the simple ToDo list written on jQuery (and JS, of course).
I already created the static ToDo list with a possibility to add new items only by editing the code. It is logically that I am going to create a dynamic list now.
I've already tried some methods, like .load() code from external file, create .after(), but it all goes wrong for me.
What would you suggest me to do?
You may find all the source codes in strasbourgmeetings.org/ccc
I would be very grateful if you could help me solving this question.
Gloserio, yes, Add Item does not work now, because I of the problem I described.
ncubica, the problem is that at the moment I am not able to add new items to my list (only bu editing the code). Dynamic means that it would be possible to add/delete items. To do that I tried to use .after() method with the function inside it, that will copy the <li id="item1">List item here</li><li id="buttons1">Buttons here</li> (roughly speaking), but it puts all list items on the upper side and all buttons to the bottom.
This is a part of the JS code:
<script>
// Waiting for the document to load
$(document).ready(function() {
// Wanted to create a dynamic item list, that's why I used this variable. I thought it would
// generate unique IDs (there was 'id++' somewhere in the function I already deleted).
var id = 1;
// This is going to be used as an action for 'Add Item' button. (It is not a button, actually, it is just <span> with cursor: pointer. Using <a> causes page reload);
$('.add').click(function() {
$('#item'+id).after(function(i) { // '#item'+id should be something like this in the code: #item2, #item3, etc.
})
})
// 'Done' button
$('#done1').click(function() {
console.log('# "Done" button pressed');
$('#list1').css('background-color','#89f49a').css('border','1px solid #16bf31').css('text-decoration','line-through').css('color','#817f7f').css('font-weight','normal');
console.log('# Item doned successfully');
});
// 'Undone' button (is gonna be renamed to 'Reset');
$('#undone1').click(function() {
console.log('# "Undone" button pressed');
$('#list1').css('background-color','').css('border','').css('text-decoration','').css('color','').css('font-weight','normal');
});
// 'Working' button
$('#working1').click(function() {
$('#list1').css('background-color','#edc951').css('border','1px solid #dda119').css('font-weight','bold').css('color','#000').css('text-decoration','none');
});
// 'Cancel' button
$('#cancel1').click(function() {
$('#list1').css('background-color','#ff8c8c').css('border','1px solid #ea2c2c').css('font-weight','normal').css('text-decoration','line-through').css('color','#f00');
});
// 'Delete' button
$('#del1').click(function() {
$('div#dlist1').remove();
$('li#action1').remove();
});
});
</script>
And HTML part:
<div class="list">
<ul id="sortable">
<div class="list-item" id="item1"><div class="spacer1" id="spacer1"></div>
<div class="l-element" id="dlist1"><li id="list1" class="ui-widget ui-state-default">Create add feature</div>
<li id="action1" class="action"><input type="button" value="Done" class="done" id="done1"><input type="button" value="Undone" class="undone" id="undone1"><input type="button" value="Working" class="working" id="working1"><input type="button" value="Cancel" class="cancel" id="cancel1"><span id="del1" class="delete">Delete</span></li>
<div class="spacer"></div></div>
</ul>
<div>
As you can see, there is only 1 list item I wrote. IDs are static and can not be changed at the moment. All I need is to change IDs (ok, it will be var id = 1; id++) and to add the part of the code (inside <div class="list-item")
why don't you try jQuery's .clone() and attach it to the "Add Item" behaviour?
You can check it here.
I personally made one as well, and it works really simply, a checkbox, the text, followed by a delete button.
Works great, though I'm still working on a way to make it save it after you close the browser.
jQuery code:
function addListItem() {
var textToAdd = $('#new-text').val(); // The finish class is just for css styling
$('#list').append('<li class="item"><input type="checkbox" class="finish" />' + textToAdd + '<button class="delete">Delete</button></li>');
$('#new-text').val('');
}
function deleteItem() {
$(this).parent().remove();
}
$(document).ready(function() {
$('#add').on('click', addListItem);
$(document).on('click', '.delete', deleteItem);
});
HTML code:
<!DOCTYPE html>
<html>
<head>
<title>To Do List</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h2>To Do List</h2>
<p>Project started on <strong>4-1-2015</strong>.</p>
<input type="text" id="new-text" /><button id="add">Add</button>
<ul id="list">
</ul>
<script src="jquery.js" type="text/javascript"></script>
<script src="script.js" type="text/javascript"></script>
</body>
</html>
Hope this helped :)