I'm trying to implement a simple shopping cart with JS.
$('.addToCart').click(function(event) {
event.preventDefault();
var name = $(this).data('name');
var price = Number($(this).data('price'));
cart.addProduct(name, price, 1);
});
and here is my cart object
var cart = (function () {
cartStorage = [];
function Product(name, price, count) {
this.name = name;
this.price = price;
this.count = count;
}
var object = {};
object.addProduct = function (name, price, count) {
for (var i in cartStorage) {
if (cartStorage[i].name == object.name) {
cartStorage[i].count++;
return;
}
}
var newItem = Product(name, price, count);
console.log(newItem);
cartStorage.push(newItem);
}
It prints undefined when I'am trying to console log it. And if I click one more time on a button it says 'Cannot read property 'name'.
here is my typical item card block
<div class="col-8 col-md-4 col-lg-3 card">
<img src="https://picsum.photos/200?random=9">
<div class="priceWrapper">
<input type="button" value="BUY" class = "addToCart" data-name = "Product #8" data-price = "0.001">
<p class="newPrice">0.001$ </p>
<p class="oldPrice"><strike>300$</strike></p>
</div>
<p class="item-name">Product #8 </p>
</div>
Try this
var newItem = new Product(name, price, count);
Use keyword new for new object from object constructor function.
you're doing this: var object = {}, and a few lines later, this: object.name.
Of course you'll get an error... I do not know what you were trying to do, but you have to initialize that object first to access any properties on it...
And as a side not, naming a variable "object" is not a really good idea, try to give your variables more meaningful names. It would make your code more readable both to you and to other developers coming after you.
I hope below code can give you some hints...
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js">
</script>
</head>
<body>
<div class="col-8 col-md-4 col-lg-3 card">
<img src="https://picsum.photos/200?random=9">
<div class="priceWrapper">
<input type="button" value="BUY" class="addToCart" data-name="Product #8" data-price="0.001">
<p class="newPrice">0.001$ </p>
<p class="oldPrice">
<strike>3$</strike>
</p>
</div>
<p class="item-name">Product #8 </p>
</div>
<div class="col-8 col-md-4 col-lg-3 card">
<img src="https://picsum.photos/200?random=9">
<div class="priceWrapper">
<input type="button" value="BUY" class="addToCart" data-name="Product #9" data-price="0.002">
<p class="newPrice">0.002$ </p>
<p class="oldPrice">
<strike>2$</strike>
</p>
</div>
<p class="item-name">Product #8 </p>
</div>
<script type="text/javascript">
var cart = (function () {
const cartStorage = [];
function Product(name, price, count) {
this.name = name;
this.price = price;
this.count = count;
}
var object = {};
object.addProduct = function (name, price, count) {
var isProductAlredyInCart = false
for (var i in cartStorage) {
if (cartStorage[i].name == name) {
cartStorage[i].count++;
isProductAlredyInCart = true;
}
}
if (!isProductAlredyInCart) {
var newItem = new Product(name, price, count);
cartStorage.push(newItem);
}
console.log(cartStorage);
}
return object;
})();
$('.addToCart').click(function (event) {
event.preventDefault();
var name = $(this).data('name');
var price = Number($(this).data('price'));
cart.addProduct(name, price, 1);
});
</script>
</body>
</html>
You need to return object from the cart function, suggestion from #Huỳnh Tuân is perfect.
I would personally suggest first you sharp your JS basics, like how new works ?, closures etc.
Related
I'm having a bit of trouble with this problem. I'm working on the project of an e-commerce application that works on several html pages. I managed to push products through the cart html page, but I can't seem to find a way to update on this page only the quantity of a product and not push every elements of said product (images, id, etc). Onclick, if product exists, I only want quantity to be updated. Here's the code if any of you can help me out that'd be greatly appreciated.
setItems(kanap);
function setItems(kanap) {
let cart = JSON.parse(localStorage.getItem('cart'));
let imgKanap = kanap.imageUrl;
let idKanap = kanap._id;
let colorKanap = colors.value;
let quantityKanap = parseInt(quantity.value);
let key = idKanap + ' ' + colorKanap;
let cartItem = {
id: idKanap,
color: colorKanap,
quantity: quantityKanap,
kanap: kanap
};
if (cart === null) {
cart = [];
}
cart.push(cartItem);
localStorage.setItem('cart', JSON.stringify(cart));
function addProduct(cartItem) {
var found = false;
for (key in cartItem) {
if (cartItem[key].idKanap == idKanap) {
cartItem[key].quantityKanap += quantityKanap;
found = true;
break;
}
}
if (!found) {
cart.push(cartItem);
}
}
addProduct();
}
<div class="item__content__addButton">
<button id="addToCart" type="submit">Ajouter au panier</button>
</div>
<section class="cart">
<!-- <section id="cart__items">
<article class="cart__item" data-id="{product-ID}">
<div class="cart__item__img">
<img id ="image" alt="Photographie dun canapé">
</div>
<div class="cart__item__content">
<div class="cart__item__content__titlePrice">
<h2 class=title></h2>
<p class =price></p>
</div>
<div class="cart__item__content__settings">
<div class="cart__item__content__settings__quantity">
<p class= quantity>Qté : </p>
<input type="number" class="itemQuantity" name="itemQuantity" min="1" max="100" value="">
</div>
<div class="cart__item__content__settings__delete">
<p class="deleteItem">Supprimer</p>
</div>
</div>
</div>
</article> -->
</section>
There's a few approaches you can take, but I am using .find to look through your cart.
If the .find() function finds an item with the same id as you're about to add, it will up the quantity of the existing item instead of appending another object with the same ID.
I used a mock local storage since local storage doesn't work in these snippets so just ignore that and use what you've been doing for local storage access.
let mockLS = null;
// guessed at the structure here, you may have something slightly different
const exampleItem = {
_id: "abc",
imageUrl: "imageurlexample",
colors: {
value: "red"
},
quantity: {
value: 1
}
}
const exampleItem2 = {
_id: "abc2",
imageUrl: "imageurlexample2",
colors: {
value: "blue"
},
quantity: {
value: 1
}
}
function setItems(kanap) {
//let cart = JSON.parse(localStorage.getItem('cart'));
// using a mock localstorage here since it doesn't work within this snippet, use what you currently have instead
let cart = mockLS;
let imgKanap = kanap.imageUrl;
let idKanap = kanap._id;
let colorKanap = kanap.colors.value;
let quantityKanap = parseInt(kanap.quantity.value);
let key = idKanap + ' ' + colorKanap;
let cartItem = {
id: idKanap,
color: colorKanap,
quantity: quantityKanap
//kanap: kanap not sure why you want the whole obj here so I left this one out
};
if (cart === null) {
cart = [];
}
// here is the case where cart exists and there may be the same item in it
const itemExists = cart.find(item => {
if(item.id === idKanap) {
item.quantity += quantityKanap;
return true;
}
return false;
})
if (!itemExists) {
cart.push(cartItem);
}
//localStorage.setItem('cart', JSON.stringify(cart));
mockLS = cart;
}
setItems(exampleItem);
setItems(exampleItem2);
setItems(exampleItem);
console.log(mockLS)
I am trying to store this information in Local storage but i don't want it to overwrite the data instead i want it append it in the object i created in local storage
Js code
function addToCartClicked(event)
{
var button = event.target
var shopItem = button.parentElement.parentElement
var title = shopItem.getElementsByClassName('shop-item-title')[0].innerText
var price = shopItem.getElementsByClassName('shop-item-price')[0].innerText
var imgSrc = shopItem.getElementsByClassName('shop-item-image')[0].src
console.log(title)
addItemToCart(title,price,imgSrc)
}
function addItemToCart(title,price,imgSrc)
{
var product = {
title : title,
price : price,
imgSrc : imgSrc
};
console.log(product)
localStorage.setItem("productsInCart" ,JSON.stringify(product))
}
python Code
<section class="container content-section">
<h2 class="section-header">On Sale</h2>
<div class="shop-items">
{% for data in product_data %}
<div class="shop-item" >
<span class="shop-item-title" id="title-item">{{ data.title }}</span>
<input type="image" class="shop-item-image" id="image-item" src={{ data["img_file"] }} onclick="takethatpage();">
<div class="shop-item-details">
<span class="shop-item-price" id="price-item">{{ data["price"]}}</span>
<button class="btn btn-primary shop-item-button" type="button">ADD TO CART</button>
</div>
</div>
{% endfor %}
</div>
</section>
You could use array to store multiple object on local storage.
So you can modify your code like :
function addItemToCart(title,price,imgSrc)
{
var products = JSON.parse(localStorage.getItem("productsInCart")||"[]"); // get current objects
var product = {
title : title,
price : price,
imgSrc : imgSrc
};
products.push(product); //push new one
console.log(product)
localStorage.setItem("productsInCart" ,JSON.stringify(products))
}
Test app on Plunker
you can very well use an array of objects to store
read more here https://www.kirupa.com/html5/storing_and_retrieving_an_array_from_local_storage.htm
I have a simple site that is getting a list of books from the Google Books API.
I have a separate file called scripts.js that is getting all the book information (title, author, ISBN, link to the image).
I want to create a div for each book in a gallery style page, where there is a picture of the book and on top of the book is the Title, Author, and ISBN.
I've tried creating the DIV's in Javascript but I want there to be an h3, p, and img inside of each DIV and I can't seem to wrap my head around how I could do that in Javascript.
My HTML code for the gallery:
<div id="content">
<h2>My Bookshelf</h2>
<div class="book">
<!-- The book image is the background of the div -->
<h3 class="book-title">Title</h3>
<p class="book-isbn">ISBN: 000000</p>
<p class="book-author">Authors: ABC</p>
</div>
</div>
My Javascript code that cycles through the JSON file and returns the needed information.
// Returns an array with the book title, ISBN, author, bookmark icon, description, image
apiRequest.onreadystatechange = () => {
if (apiRequest.readyState === 4) {
const response = JSON.parse(apiRequest.response);
var bookList = response.items;
// Removes old search results before display new ones
bookSection.innerHTML = "";
for (let i = 0; i < bookList.length; i++) {
console.log(i);
var title = (bookList[i]["volumeInfo"]["title"]);
try {
var isbn = (bookList[i]["volumeInfo"]["industryIdentifiers"][0]["identifier"]);
} catch (TypeError) {
var isbn = "ISBN Not Available";
}
var author = (bookList[i]["volumeInfo"]["authors"]);
var description = (bookList[i]["description"]);
try {
var image = (bookList[i]["volumeInfo"]["imageLinks"]["thumbnail"]);
} catch (TypeError) {
var image = "img/unavailable.png";
}
}
}
}
You can use template literals to make your job easier.
You can do it like this:
var bookSection = `<div id="content">
<h2>My Bookshelf</h2>
<div class="book">
<!-- The book image is the background of the div -->
<h3 class="book-title">${titleVar}</h3>
<p class="book-isbn">ISBN: ${ISBNVar}</p>
<p class="book-author">Authors: ${AuthorsVar}</p>
</div>
</div>`;
Learn more about template literals from here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Your code should look something like this
apiRequest.onreadystatechange = () => {
if (apiRequest.readyState === 4) {
const response = JSON.parse(apiRequest.response);
var bookList = response.items;
// Removes old search results before display new ones
bookSection.innerHTML = "";
let bookListHtmlMarkup = '';
for (let i = 0; i < bookList.length; i++) {
console.log(i);
// Declaring book object
const book = {};
const bookListHtmlMarkup = '';
book['title'] = (bookList[i]["volumeInfo"]["title"]);
try {
book['isbn'] = (bookList[i]["volumeInfo"]["industryIdentifiers"][0]["identifier"]);
} catch (TypeError) {
book['isbn'] = "ISBN Not Available";
}
book['author'] = (bookList[i]["volumeInfo"]["authors"]);
book['description'] = (bookList[i]["description"]);
try {
book['image'] = (bookList[i]["volumeInfo"]["imageLinks"]["thumbnail"]);
} catch (TypeError) {
book['image'] = "img/unavailable.png";
}
bookListHtmlMarkup += `
<div class="book">
<div class="book-image">
<img src="${book.image}" alt="Image unavailable" />
</div>
<div class="book-info">
<h3 class="book-title">${book.title}</h3>
<p class="book-isbn">ISBN: ${book.isbn}</p>
<p class="book-author">Author: ${book.author}</p>
<p class="book-description">Author: ${book.description}</p>
</div>
</div>
`;
}
// Assigning generated markup to innerHTML of bookSection
bookSection.innerHTML = bookListHtmlMarkup;
}
}
I am creating a dynamic, single-paged forum site using AngularJS as the front-end and Firebase as the back-end. The page consists of a list of threads on the left-hand side and the thread content on the right-hand side. The thread content displayed is based on the thread selected from the list.
I can successfully select a thread from the list and display its contents. However, when a thread is selected from the list, all of the other threads in the list become replicas of the selected thread. By this, I mean that the attribute values for the title, comments and votes of the selected thread are assigned to the same attributes in all of the other threads simultaneously, making them all identical. The ID of each thread does not change.
Can anybody give me some insight as to what is causing this issue? I can't identify anything in my code that would cause the attribute values of each Firebase object to be reassigned.
Here is the main.html page that contains the list and thread content sections
<div ng-controller="mainPageController">
<div>
<h3>
Welcome {{user.name}}! <button class="btn-danger img-rounded" ng-click="logout()" id="LogoutBtn">Logout</button>
</h3>
</div>
<div class="col-md-6">
<h2>All Threads</h2>
<div id="searchThreads" class="input-group col-md-5 img-rounded">
<input type="text" class="col-xs-5 form-control" ng-model="searchThread" placeholder="Search threads...">
</div>
<div id="addThread" class="input-group">
<input type="text" class="col-xs-5 form-control" ng-model="newThreadTitle" placeholder="New thread title..."/>
<button ng-click="addThread()">Add thread</button>
</div>
<!-- Thread List -->
<div>
<div ng-repeat="thread in threads | filter:searchThread | orderObjectBy:'votes'">
<button class="glyphicon glyphicon-chevron-up" ng-click="upvote(thread.$id, thread.votes)"></button> |
<button class="glyphicon glyphicon-chevron-down" ng-click="downvote(thread.$id, thread.votes)"></button>
<a href ng-click="showThread(thread)">{{thread.votes}}<span style="margin-left:1em"> {{thread.title}} by {{thread.username}}</span></a>
</div>
</div>
</div>
</div>
<!-- Thread content viiew -->
<div class="col-md-6">
<div ng-controller="threadPageController">
<h1>{{currentThread.title}} by {{currentThread.username}}</h1>
<div>
<input type="text" ng-model="newComment" placeholder="Write a comment..."/>
<button ng-click="addComment()">Add Comment</button>
</div>
<div>
<div ng-repeat="comment in currentThread.comments">{{comment.username}}: {{comment.text}}
</div>
<div ng-if="!currentThread.comments.length">There are no comments on this thread</div>
</div>
</div>
</div>
The mainPageController
angular.module('richWebApp')
.controller('mainPageController', function($scope, $location, userService, threadService, fb, $firebaseAuth, $filter){
$scope.user = userService.getLoggedInUser();
$scope.newThreadTitle = '';
$scope.currentThreadId = '';
$scope.threads = threadService.getAllThreads();
$scope.threads.$loaded().then(function(){
console.log($scope.threads)
});
$scope.users = userService.getLoggedInUsers();
$scope.addThread = function(){
if(!$scope.newThreadTitle){
return false; //Don't do anything if the text box is empty
}
var newThread = {
title: $scope.newThreadTitle,
username: $scope.user.name,
comments: [],
votes: 0
};
$scope.threads.$add(newThread);
$scope.newThread = '';
$scope.newThreadTitle = ''; //Clear the text in the input box
}
$scope.showThread = function(thread) {
$scope.$emit('handleEmit', {id: thread.$id});
};
$scope.upvote = function(threadId, threadVotes) {
var newVotes = threadVotes + 1;
var ref = new Firebase(fb.url);
var threadRef = ref.child("threads");
threadRef.child(threadId).update({
votes: newVotes
});
}
$scope.downvote = function(threadId, threadVotes) {
var newVotes = threadVotes - 1;
var ref = new Firebase(fb.url);
var threadRef = ref.child("threads");
threadRef.child(threadId).update({
votes: newVotes
});
}
$scope.logout = function(){
userService.logout();
}
});
The threadPageController
angular.module('richWebApp')
.controller('threadPageController', function($scope, $location, $routeParams, threadService, fb, userService){
$scope.$on('handleBroadcast', function (event, args) {
var threadId = args.id;
var currentThread = threadService.getThread(threadId);
currentThread.$bindTo($scope, 'currentThread') //creates $scope.thread with 3 way binding
});
$scope.newComment = '';
$scope.addComment= function(){
if(!$scope.newComment){
return false; //Don't do anything if the text box is empty
}
var currentUser = userService.getLoggedInUser();
var newComment = {
text: $scope.newComment,
username: currentUser.name
};
$scope.currentThread.comments = $scope.currentThread.comments || [];
$scope.currentThread.comments.push(newComment);
$scope.newComment = ''; //Clear the input box
}
});
threadService
angular.module("richWebApp").service("threadService", function($firebaseArray, $firebaseObject, fb){
this.getAllThreads = function(){
var ref = new Firebase(fb.url + '/threads');
return $firebaseArray(ref);
};
this.getThread = function(threadId){
var ref = new Firebase(fb.url + '/threads/' + threadId);
return $firebaseObject(ref);
};
});
I recently followed this tutorial to create a message board:
Real-time message board
The board works perfectly, but I am having trouble figuring out how to delete posts and comments from the view and SignalR.
I am basically trying to add a link to every post and comment to allow the post/comment to be deleted. I am new to SignalR and knockout, so help would be greatly appreciated.
Here is the js:
var post = function (id, message, username, date) {
this.id = id;
this.message = message;
this.username = username;
this.date = date;
this.comments = ko.observableArray([]);
this.addComment = function (context) {
var comment = $('input[name="comment"]', context).val();
if (comment.length > 0) {
$.connection.boardHub.server.addComment(this.id, comment, vm.username())
.done(function () {
$('input[name="comment"]', context).val('');
});
}
};
}
var comment = function (id, message, username, date) {
this.id = id;
this.message = message;
this.username = username;
this.date = date;
}
var vm = {
posts: ko.observableArray([]),
notifications: ko.observableArray([]),
username: ko.observable(),
signedIn: ko.observable(false),
signIn: function () {
vm.username($('#username').val());
vm.signedIn(true);
},
writePost: function () {
$.connection.boardHub.server.writePost(vm.username(), $('#message').val()).done(function () {
$('#message').val('');
});
},
}
ko.applyBindings(vm);
function loadPosts() {
$.get('/api/posts', function (data) {
var postsArray = [];
$.each(data, function (i, p) {
var newPost = new post(p.Id, p.Message, p.Username, p.DatePosted);
$.each(p.Comments, function (j, c) {
var newComment = new comment(c.Id, c.Message, c.Username, c.DatePosted);
newPost.comments.push(newComment);
});
vm.posts.push(newPost);
});
});
}
$(function () {
var hub = $.connection.boardHub;
$.connection.hub.start().done(function () {
loadPosts(); // Load posts when connected to hub
});
// Hub calls this after a new post has been added
hub.client.receivedNewPost = function (id, username, message, date) {
var newPost = new post(id, message, username, date);
vm.posts.unshift(newPost);
// If another user added a new post, add it to the activity summary
if (username !== vm.username()) {
vm.notifications.unshift(username + ' has added a new post.');
}
};
// Hub calls this after a new comment has been added
hub.client.receivedNewComment = function (parentPostId, commentId, message, username, date) {
// Find the post object in the observable array of posts
var postFilter = $.grep(vm.posts(), function (p) {
return p.id === parentPostId;
});
var thisPost = postFilter[0]; //$.grep returns an array, we just want the first object
var thisComment = new comment(commentId, message, username, date);
thisPost.comments.push(thisComment);
if (thisPost.username === vm.username() && thisComment.username !== vm.username()) {
vm.notifications.unshift(username + ' has commented on your post.');
}
};
});
I'm not sure if I need to add a delete function to the view model or the post/comment objects.
Here is the view:
<ul class="posts list-unstyled" data-bind="foreach: posts">
<li>
<p>
<span data-bind="text: username" class="username"></span><br />
</p>
<p>
<span data-bind="text: message"></span>
</p>
<p class="no-pad-bottom date-posted">Posted <span data-bind="text: moment(date).calendar()" /></p>
#*<form class="add-comment" data-bind="visible: $parent.signedIn(), submit: removePost">
<div class="row">
<div class="col-md-3">
<button class="btn btn-default" type="submit" name="delete">Delete Post</button>
</div>
</div>
</form>*#
<a href='#' data-bind='click: removePost'>Delete</a>
<div class="comments" data-bind="visible: $parent.signedIn() || comments().length > 0">
<ul class="list-unstyled" data-bind="foreach: comments, visible: comments().length > 0">
<li>
<p>
<span class="commentor" data-bind="text: username"></span>
<span data-bind="text: message"></span>
</p>
<p class=" no-pad-bottom date-posted">Posted <span data-bind="text: moment(date).calendar()" /></p>
</li>
</ul>
<form class="add-comment" data-bind="visible: $parent.signedIn, submit: addComment">
<div class="row">
<div class="col-md-9">
<input type="text" class="form-control" name="comment" placeholder="Add a comment" />
</div>
<div class="col-md-3">
<button class="btn btn-default" type="submit">Add Comment</button>
</div>
</div>
</form>
</div>
</li>
</ul>
I also need to communicate this to SignalR:
public void WritePost(string username, string message)
{
var ctx = new MessageBoardContext();
var post = new Post { Message = message, Username = username, DatePosted = DateTime.Now };
ctx.Posts.Add(post);
ctx.SaveChanges();
Clients.All.receivedNewPost(post.Id, post.Username, post.Message, post.DatePosted);
}
public void AddComment(int postId, string comment, string username)
{
var ctx = new MessageBoardContext();
var post = ctx.Posts.FirstOrDefault(p => p.Id == postId);
if (post != null)
{
var newComment = new Comment { ParentPost = post, Message = comment, Username = username, DatePosted = DateTime.Now };
ctx.Comments.Add(newComment);
ctx.SaveChanges();
Clients.All.receivedNewComment(newComment.ParentPost.Id, newComment.Id, newComment.Message, newComment.Username, newComment.DatePosted);
}
}
Thanks!
I'd add a deleteComment method on your vm which uses ajax to make a delete call to the server for the comment. If the delete is successful you can then broadcast a delete with the relevant information and update all clients.