I am making little game in angular and i have problem how to export array with images to storage so when i reload the page it doesn't disappear. Array is made of random items from other array and adding next item is by pressing button.
That's my code in typescript
containers = [];
images = [
{ id: 0, name: "sword", url: "../../../../assets/img/sword.png" },
{ id: 1, name: "sword2", url: "../../../../assets/img/sword2.png" },
];
add() {
let index = Math.round(Math.random());
this.containers.push(this.images[index]);
}
and this is in html:
<button (click)="add()">Add</button>
<div id="content">
<div id="contentInside" *ngFor="let image of containers">
<img class="item" src="{{image.url}}" />
</div>
</div>
I want to keep it looking like this
You can store a copy of your image array in localstorage as
localStorage.setItem('images', JSON.stringify(this.images))
And retrive those images on browser reload by simply doing
private images = JSON.parse(localStorage.getItem('images')) || [];
Related
i'm trying create a counter function on object compilated with handlebars.js. I'm trying to make a project site for a moving company, and I'm trying to create a cost estimation system in it, depending on what things and how many they have to move in their new house
handlebars compilated divs
problem
So I created objects in js which contain things to move such as fridge, bed, table etc... their are compilated right into HTML with handlebars template.
I want to be able to increase and decrease the numbers of things separately, so I was able to create one function that does this, but the problem is that it works only on the first compilated object and all other objects are not affected with this code, i don't even know if it is possible to do such thing with handlebars template and vanilla js, I know I can do this for each objects individually but there will be way too much duplicated codes in HTML and JS files..
here is the handlebars template on HTML file:
<script src="https://cdn.jsdelivr.net/npm/handlebars#latest/dist/handlebars.js"></script>
<script id="templateHB" type="text/x-handlebars-template">
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button id="increase-{{this.index}}">+</button>
<p>{{this.quantity}}</p>
<button id="decrease-{{this.index}}">-</button>
</div>
</div>
{{/each}}
here is the js file code:
const source = document.getElementById('templateHB').innerHTML;
const template = Handlebars.compile(source);
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
let compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
// here start the function
let quantity = contextSalon.lesMeublesSalon[0].quantity;
let addOneBtn = document.getElementById("increase-0");
let removeOneBtn = document.getElementById("decrease-0");
function updateQuantity(quantity) {
contextSalon.lesMeublesSalon[0].quantity = quantity;
compiledHtmlSalon = template(contextSalon);
injectionObjetSalon.innerHTML = compiledHtmlSalon;
addOneBtn = document.getElementById("increase-0");
removeOneBtn = document.getElementById("decrease-0");
addOneBtn.addEventListener("click", function() {
updateQuantity(quantity + 1);
});
removeOneBtn.addEventListener("click", function() {
updateQuantity(quantity - 1);
});
}
updateQuantity(0);
if the thing i'm trying to do is impossible with js and handlebars.js, what other tech can you suggest me? any js framework such as node.js and express.js?
I just created function to increase and decrease number based on ID of the template inside html file with handlebars, I was expecting it to work with all others compilated objects to work the same way.
It is possible that a rendering framework like React or Vue could be used for this, but they come with learning curves. The conditions as you have outlined them are fairly simple and so I think you can get by with Handlebars.
But we will need to make some modifications to your code.
The first issue I see is that you have index: 0 set on all objects in your lesMeublesSalon array. You could correct those to be sequential (0, 1...), but I think a better option would be to use Handlebars' built-in #index variable to set the index value for each item.
The next problem I would address is that you are trying to re-render the Handlebars template with each click of the increment/decrement button. The problem I see with this is that it is that you will have to re-attach your event listeners each time you re-render the HTML because the DOM will have new button elements that need to be listened to.
I think a better approach would be to render the initial HTML with Handlebars, but then to use JavaScript to directly update the DOM when your counts change.
Here is how I would implement this:
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button data-increase="{{#index}}">+</button>
<p data-quantity="{{#index}}">{{this.quantity}}</p>
<button data-decrease="{{#index}}">-</button>
</div>
</div>
{{/each}}
const source = document.getElementById('templateHB').innerHTML;
const template = Handlebars.compile(source);
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
// Initially render our HTML with Handlebars.
const compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
// Note the use of data-* attributes in the template.
// This allows us to query for ALL increment/decrement buttons
// and to attach a listener to each.
// Getting them by ID allowed us to get only one increment
// and one decrement button.
const addOneBtns = document.querySelectorAll("[data-increase]");
const removeOneBtns = document.querySelectorAll("[data-decrease]");
const quantityDisplays = document.querySelectorAll("[data-quantity]");
// This is the function that will directly manipulate the displayed
// quantity value for each item.
// It relies on the indexes of the elements to match the indexes in
// our lesMeublesSalon so that the correct quantity will be set.
function renderQuantities () {
quantityDisplays.forEach((quantityDisplay, index) => {
const quantity = contextSalon.lesMeublesSalon[index].quantity;
quantityDisplay.textContent = String(quantity);
})
};
// We loop through EACH increment button and attach a click listener.
addOneBtns.forEach(addOneBtn => {
addOneBtn.addEventListener('click', function (event) {
// We get the index from the `[data-increase]` attribute.
const index = Number(event.target.dataset.increase);
// We use that index to increment the quantity on the
// corresponding item in our array.
contextSalon.lesMeublesSalon[index].quantity += 1;
// We re-render the quantities because we know there is a change.
renderQuantities();
});
});
// This is basically the same as above, except for decrementing.
removeOneBtns.forEach(removeOneBtn => {
removeOneBtn.addEventListener('click', function (event) {
const index = Number(event.target.dataset.decrease);
// We use Math.max() so the quantity can't become less than zero.
contextSalon.lesMeublesSalon[index].quantity = Math.max(contextSalon.lesMeublesSalon[index].quantity - 1, 0);
renderQuantities();
});
});
I have created a Codepen for reference.
I've tesed your code and it works well , thanks you! however I forgot to mention that I have 4 categories of objects , as you can see on the picture , I got (Salon, Chambre, Cuisine, Bain) , how can we make so that it works on all of these categories ?
So it works on the "Salon" categories but not in the others,
here you have all others object :
const contextSalon = {
lesMeublesSalon: [
{
image: 'images/canape.png',
element: 'Canapé',
quantity: 0,
index: 0
},
{
image: 'images/canape.png',
element: 'Lit',
quantity: 0,
index: 0
}
]
};
// Initially render our HTML with Handlebars.
const compiledHtmlSalon = template(contextSalon);
const injectionObjetSalon = document.getElementById('meuble-salon');
injectionObjetSalon.innerHTML = compiledHtmlSalon;
const contextChambre = {
lesMeublesChambre: [
{
image: 'images/bed.svg.png',
element: 'Lit double',
quantity: 0,
index: 0
}
]
}
const compiledHtmlChambre = template(contextChambre);
const injectionObjetChambre = document.getElementById('meuble-chambre');
injectionObjetChambre.innerHTML = compiledHtmlChambre;
const contextCuisine = {
lesMeublesCuisine: [
{
image: 'images/frigo.svg',
element: 'Frigo',
quantity: 0,
index: 0
}
]
}
const compiledHtmlCuisine = template(contextCuisine);
const injectionObjetCuisine = document.getElementById('meuble-cuisine');
injectionObjetCuisine.innerHTML = compiledHtmlCuisine;
const contextBain = {
lesMeublesBain: [
{
image: 'images/machine-a-laver.svg',
element: 'Machine à laver',
quantity: 0,
index: 0
}
]
}
const compiledHtmlBain = template(contextBain);
const injectionObjetBain = document.getElementById('meuble-bain');
injectionObjetBain.innerHTML = compiledHtmlBain;
{{#each lesMeublesSalon}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button data-increase="{{#index}}">+</button>
<p data-quantity="{{#index}}">{{this.quantity}}</p>
<button data-decrease="{{#index}}">-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesChambre}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesCuisine}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
{{#each lesMeublesBain}}
<div class="meuble"><img src="{{this.image}}">
<p>{{this.element}}</p>
<div class="plus-moin">
<button>+</button>
<p>{{this.quantity}}</p>
<button>-</button>
</div>
</div>
{{/each}}
Im new to javascript, I wanted to add a remove button on my shopping cart, how do I make one that removes 1 row from the local storage. My table for my shopping cart is in my javascript and I cant figure out how to add another function for the Remove butoon
function displayCart(){
let cartItems = localStorage.getItem("productsInCart");
cartItems = JSON.parse(cartItems);
let productContainer = document.querySelector(".products");
let cartCost = localStorage.getItem('totalCost');
console.log(cartItems);
if( cartItems && productContainer){
productContainer.innerHTML = '';
Object.values(cartItems).map(item => {
productContainer.innerHTML += `
<div class="product">
<button class="btn btn-danger" onclick="removeItems()" >Remove</button>
<img src="./img/${item.tag}.jpg">
<span>${item.name}</span>
</div>
<div class="price">
₱${item.price}.00
</div>
<div class="quantity"> <span>${item.inCart}</span> </div>
<div class="total">
₱${item.inCart * item.price}.00
</div>
`
});
productContainer.innerHTML += `
<div class="basketTotalContainer">
<h4 class="basketTotalTitle">
Cart Total
</h4>
<h4 class="basketTotal">
₱${cartCost}.00
</h4>
`;
}
}
I tried putting another function, localstorage.removeItem(); above that function but it doesnt work either.
remove item from object array
An example of an approach that could be used to manage your shopping cart is described here.
The cart is stored in an array of objects which is ideally suited to storing in local storage (and network transfer) as a json string, and makes for easy rendering of the data to the html page. The example data structure I have used for the example is:
const cart = [
{name: "shoes", price: 85, tag:"img_001", inCart: 1},
{name: "hat", price: 42, tag:"img_002", inCart: 1},
{name: "belt", price: 24, tag:"img_003", inCart: 2}
] // end cart array
To store the data in localstorage, the array would be converted to a string using JSON.stringify(cart), and the retrieved string would be converted to a javascript array using JSON.parse(stringName) (where stringName is whatever variable the read local storage was stored in).
the remove function has to know which item had its remove button clicked
the easiest way to achieve this is to include the index of each object in the cart array to it's relevant button when the cart is rendered to the html document.
So, a renderCart() function (to read the cart data and render it in the html document) would include a modified version of the button markup:
<button class="btn btn-danger" onclick="removeItems(${index})" >Remove</button>
Notice the onclick attribute now includes an index argument for your removeItems() function. The value of index has come from an optional argument in the array.map method (which can supply both the element, in this case an object, and its index position in the array):
array.map((element,index)=> {// both the element and its numerical index are available here;}
The removeItems function now receives the index of the cart array holding the data relevant to the button that was clicked. The index can be used to remove that element from the array, before calling the renderCart() function to display the modified car in the html document.:
function removeItems(elementIndex) {
console.log(elementIndex);
cart.splice(elementIndex, 1);
renderCart();
// code to replace local storage copy of cart goes here;
} // end function removeItems
The function should also then replace the localStorage version of the data, for which you will need another function that converts it to a json string and sets the data to localstorage.
demonstration snippet
The following snippet demonstrates these principles and should be adaptable to your particular case:
const cart = [
{name: "shoes", price: 85, tag:"img_001", inCart: 1},
{name: "hat", price: 42, tag:"img_002", inCart: 1},
{name: "belt", price: 24, tag:"img_003", inCart: 2}
] // end cart array
productContainer = document.getElementById('container');
renderCart();
function renderCart() {
productContainer.innerHTML = "";
cart.map((item, index) => {
productContainer.innerHTML += `
<div class="product">
<button class="btn btn-danger" onclick="removeItems(${index})" >Remove</button>
<img src="./img/${item.tag}.jpg">
<span>${item.name}</span>
</div>
<div class="price">
price (each): $${item.price}.00
</div>
<div class="quantity"> quantity: <span>${item.inCart}</span> </div>
<div class="total">
total: $${item.inCart * item.price}.00
</div> `
});
} // end function renderCart
function removeItems(elementIndex) {
console.log(elementIndex);
cart.splice(elementIndex, 1);
renderCart();
// code to replace local storage copy of cart goes here;
} // end function removeItems
<div id="container"></div>
I am uploading multiple images using VueJs. Saving their respective
base64 values in Array (to store them in db).
I have added remove feature there, while clicking on remove button I find the index of that element and splice it from the array.
The clicked image value being removed from array thats fine but it does not change the position of the image.
Visually I can see the last image is deleted. How can I remove value of image from Array and remove same image from UI ?
Here is the code: https://codepen.io/echobinod/pen/GVMOqJ
<div id="app">
<input type="file" multiple #change="onFileChange" /><br><br>
<div class="row">
<div v-for="(image, key) in images">
<div class="col-md-4" :id="key">
<button type="button" #click="removeImage(key)">
×
</button>
<img class="preview img-thumbnail" v-bind:ref="'image' +parseInt( key )" />
{{ image.name }}
</div>
</div>
</div>
</div>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
images: [],
}
},
methods: {
onFileChange(e) {
var selectedFiles = e.target.files;
for (let i=0; i < selectedFiles.length; i++)
{
this.images.push(selectedFiles[i]);
}
for (let i=0; i<this.images.length; i++)
{
let reader = new FileReader(); //instantiate a new file reader
reader.addEventListener('load', function(){
this.$refs['image' + parseInt( i )][0].src = reader.result;
}.bind(this), false);
reader.readAsDataURL(this.images[i]);
}
},
removeImage (i) {
var arrayImages = this.images;
var index = arrayImages.indexOf(arrayImages[i]);
arrayImages.splice(index, i);
}
}
})
</script>
You need to force the component to reload, updating the data does not mean that the UI will change. to do this the best way imo is to put a key on whatever you need to reload, I think that would be here:
<div v-for="(image, key) in images" :key="reloadKey">
...
data(){
return {
reloadKey: 0}}
...
removeImage (i) {
var arrayImages = this.images;
var index = arrayImages.indexOf(arrayImages[i]);
arrayImages.splice(index, i);
this.reloadKey++
}
}
See here for further reading:
https://michaelnthiessen.com/force-re-render
One of the good things about Vuejs is that it provides two way data binding between the dom and your data. That means that when you change the images array defined in data the UI will automagically reflect the changes.
As a quick fix, after filling the arrayImages array, assign this.images = arrayImages and the UI should update correspondingly.
How about you use,
arrayImages.splice(index, 1);
Instead of using i in the splice
arrayImages.splice(index, i);
I have built a form that populates the fields with data from an array.
I have 2 buttons, one which will 'post' with Ajax - Submit, and one which I am trying to skip to the next object in the array - Skip.
I can't figure out how to make the skip button work. I have found code that lets you skip to the next record, when its only one record displaying, however I have many records I am adding to div's, so need something else.
How do I skip to the next object in the array?
Here is a JSfiddle of my code: http://jsfiddle.net/8vf4rs1w/
Here is the code a simplified code. I have about 20 input boxes I am adding data too.
<form>
<h1>Record: #<div id="record_id"></div></h1><h5>Date Added: <div id="date_added"></div></h5><br/>
<div class="form-group">
<label for="est_name">Name:</label>
<input type="text" class="form-control" id="est_name" placeholder="Est Name" disabled>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-primary">Verify</button>
<button type="button" id="skip" class="btn btn-secondary">Skip</button>
</div>
</form>
Get Json
$(document).ready(function(){
$.getJSON('getJSON.php', { get_param: 'name' }, function (data) {
console.log(data)
var index = 0;
var record_id = data[index].id;
var est_name = data[index].name;
var date_added = data[0].reg_date;
$('#record_id').text(record_id);
$('#date_added').text(date_added);
$('#est_name').val($('#est_name').val() + est_name);
});
});
JSON
[ 0: {id: "1", name: "name1", locale: "locale1" }
1: {id: "2", name: "name2", locale: "locale2" }
2: {id: "3", name: "name3", locale: "locale3" } ]
To scroll through your records array you have to store the array and index in a higher scope and increment the index when the skip button is clicked like so:
$(document).ready(function(){
// The variables that need to be remembered throughout the app
var index = 0;
var records = [];
// Function to set form values
function displayRecord(){
var record = records[index];
$('#record_id').text(record.id);
$('#date_added').text(record.reg_date);
$('#est_name').val(record.name);
}
// Increment index when the skip button is clicked and display data
document.getElementById('skip').addEventListener('click', function(){
index++;
displayRecord();
});
$.getJSON(/**/
records = data;
displayRecord();
/**/);
});
I have a winJS list which is full of images. When a user clicks on an image, they are being sent to a second page. Is there any way of being able to determine which image the user clicked on page 1 (each image, when clicked, goes to the same page 2 but with a custom div). Here is my list declared and then I am push items to it:
var names_Array = [];
var names_List = new WinJS.Binding.List(names_Array);
var idPL;
names_List.push({ name: "man 1", image: "image/man1.png", ClientID: "1111" });
names_List.push({ name: "man 2 ", image: "image/man2.png", ClientID: "2222" });
idPL = document.getElementById("names_List");
idPL.addEventListener("iteminvoked", onclickItem);
function onclickItem(e) {
console.log("Item clicked");
}
A the minute I am populating the div based on which item was last pushed to the list, but I need this to be more flexible and be able to select the first item (even after a second one has been added)
EDIT: I AM NOW GETTING AN ERROR 'Unable to get property 'addEventListener' of undefined or null reference'. I THOUGHT I had defined it in my code above
To get the detail of which item is clicked in winjs listview we can use itemInvoke event of the Winjs listview.
<div id="itemsList" data-win-control="WinJS.Binding.Template" style="display:none;">
<div data-win-bind="className: type">
<img src="#" style="width: 100px; height: 100px;"
data-win-bind="alt: title; src: picture" />
<div>
<h4 data-win-bind="innerText: title" style="width:100px; height:20px;"></h4>
</div>
<div>
<img id="like"src="images/like.png" class="win-interactive" data-win-bind="alt:title; onclick: LikeClick;" />
</div>
</div>
</div>
<div id="UserListView" data-win-control="WinJS.UI.ListView" style="border-top: 5px solid #000; min-width:500px;"
data-win-options="{
selectionMode:'multi',
itemTemplate:select('#itemsList'),
layout:{
type:WinJS.UI.GridLayout}}"
>
</div>
then in the js
UserListView.addEventListener("iteminvoked", ItemInvoke);
function ItemInvoke(evt) {
var name=evt.detail.itemPromise._value.data.title;
var picturePath=evt.detail.itemPromise._value.data.picture;
}
Here in this listview we are only displaying the image and title of the user.
You can reformat your image urls to include:
A hash: image.html#man1.png
A query string: image.html?image=man1.png
And then use those to extract information from the URL using JavaScript for when the 2nd page is loaded.
Either assign an eventListener globally and check which was clicked and do something, or assign an eventListener per item and do whatever you require.
UPDATE: I am unable to demonstrate using WinJS, but this example should suffice.
HTML
<ul id="names_List"></ul>
Javascript
var names_Array = [],
idPL = document.getElementById("names_List");
names_Array.push({
name: "man 1",
image: "image/man1.png",
ClientID: "1111"
});
names_Array.push({
name: "man 2 ",
image: "image/man2.png",
ClientID: "2222"
});
names_Array.forEach(function (item) {
var li = document.createElement("li"),
img = document.createElement("img");
img.name = item.name;
img.src = item.img;
li.appendChild(img);
idPL.appendChild(li);
});
function onclickItem(e) {
if (e.target.tagName === "IMG") {
console.log("Item clicked", e.target.name);
}
}
idPL.addEventListener("click", onclickItem, false);
On jsfiddle
The best and easiest way you can do this is by declaring a namespace with the value of the image inside an eventlistener for selectionchanged, like this:
listview.addEventListener("selectionchanged", function (item) {
listview.selection.getItems().done(function (items) {
WinJS.Namespace.define("imageInformation", {
seletedImage: items[0].data.image,
});
}, false);
After the namespace is declared, you can access it on the other page:
var path = imageInformation.selectedImage;