Reputation: 796
The goal is when I add a customer I want to be able to upload an image as well. On the main customer page, it will list all customers and a thumbnail image of the customer.
Currently, when I upload an image to indexedDB it is not taking in the correct URL. In my current learning project when I upload an image it uploads this URL address:
C:\fakepath\01-gel-nail-designs-thecuddl.jpg
I am not sure why it keeps giving the C:\fakepath
which is not the correct path so when it renders on the main customer page it is just a broken image filler.
Here is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Customer Manager</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<link href="../../assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Customer Manager v0.1</a>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Customers <span class="sr-only">(current)</span></a></li>
<li><a href="add.html">Add Customer</a></li>
<li><a onclick="clearCustomers()" class="danger" href="#">Clear Customers</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1 class="page-header">Customers</h1>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email Address</th>
<th></th>
</tr>
</thead>
<tbody id="customers">
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="js/bootstrap.min.js"></script>
<!-- Just to make our placeholder images work. Don't actually copy the next line! -->
<script src="../../assets/js/vendor/holder.min.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Here is my add.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Add Customer</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<link href="../../assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Customer Manager v0.1</a>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Customers <span class="sr-only">(current)</span></a></li>
<li><a href="add.html">Add Customer</a></li>
<li><a class="danger" href="#">Clear Customers</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1 class="page-header">Add Customer</h1>
<form role="form" onsubmit="addCustomer()">
<div class="form-group">
<label>Customer Name</label>
<input type="text" id="name" class="form-control" placeholder="Enter Name">
</div>
<div class="form-group">
<label>Customer Email</label>
<input type="email" id="email" class="form-control" placeholder="Enter Email">
</div>
<input type="file" id="imageSelector" multiple="multiple" />
<button type="submit" class="btn btn-default">Add Customer</button>
</form>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="js/bootstrap.min.js"></script>
<!-- Just to make our placeholder images work. Don't actually copy the next line! -->
<script src="../../assets/js/vendor/holder.min.js"></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>
<script src="js/main.js"></script>
</body>
</html>
Here is my main.js:
$(document).ready(function(){
//Open database
var request = indexedDB.open('customermanager',1);
request.onupgradeneeded = function(e){
var db = e.target.result;
if(!db.objectStoreNames.contains('customers')){
var os = db.createObjectStore('customers',{keyPath: "id", autoIncrement:true});
//Create Index for Name
os.createIndex('name','name',{unique:false});
}
}
//Success
request.onsuccess = function(e){
console.log('Success: Opened Database...');
db = e.target.result;
//Show Customers
showCustomers();
}
//Error
request.onerror = function(){
console.log('Error: Could Not Open Database...');
}
});
//Add Customer
function addCustomer(){
var name = $('#name').val();
var email = $('#email').val();
var image = $('#imageSelector').getAttribute('src');
var transaction = db.transaction(["customers"],"readwrite");
//Ask for ObjectStore
var store = transaction.objectStore("customers");
//Define Customer
var customer = {
name: name,
email: email,
image: image
}
//Perform the Add
var request = store.add(customer);
//Success
request.onsuccess = function(e){
window.location.href="index.html";
}
//Error
request.onerror = function(e){
alert("Customer was not added");
console.log('Error', e.target.error.name);
}
}
//Display Customers
function showCustomers(e){
var transaction = db.transaction(["customers"],"readonly");
//Ask for ObjectStore
var store = transaction.objectStore("customers");
var index = store.index('name');
var output = '';
index.openCursor().onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
output += "<tr id='customer_"+cursor.value.id+"'>";
output += "<td>"+cursor.value.id+"</td>";
output += "<td><span class='cursor customer' contenteditable='true' data-field='name' data-id='"+cursor.value.id+"'>"+cursor.value.name+"</span></td>";
output += "<td><span class='cursor customer' contenteditable='true' data-field='email' data-id='"+cursor.value.id+"'>"+cursor.value.email+"</span></td>";
output += "<td><img class='thumbnail' src='"+cursor.value.id+"'></img></td>";
output += "<td><a onclick='removeCustomer("+cursor.value.id+")' href=''>Delete</a></td>";
output += "</tr>";
cursor.continue();
}
$('#customers').html(output);
}
}
//Delete a Customer
function removeCustomer(id){
var transaction = db.transaction(["customers"],"readwrite");
//Ask for ObjectStore
var store = transaction.objectStore("customers");
var request = store.delete(id);
//Success
request.onsuccess = function(){
console.log('Customer '+id+' Deleted');
$('customer_'+id).remove();
}
//Error
request.onerror = function(e){
alert("The customer was not removed")
console.log('Error', e.target.error.name);
}
}
//Clear All Customers
function clearCustomers(){
indexedDB.deleteDatabase('customermanager');
window.location.href="index.html";
}
//Update Customers
$('#customers').on('blur','.customer',function(){
//Newly entered text
var newText = $(this).html();
//Field
var field = $(this).data('field');
//Customer ID
var id = $(this).data('id');
//Get Transaction
var transaction = db.transaction(["customers"],"readwrite");
//Ask for ObjectStore
var store = transaction.objectStore("customers");
var request = store.get(id);
request.onsuccess = function(){
var data = request.result;
if(field == 'name'){
data.name = newText;
} else if(field == 'email'){
data.email = newText;
}
//Store Updated Text
var requestUpdate = store.put(data);
requestUpdate.onsuccess = function(){
console.log('Customer field updated...');
}
requestUpdate.onerror = function(){
console.log('Customer field NOT updated...');
}
}
});
Here is an image of what indexedDB looks like in the console and the way it looks in my index.html:
Here is an image of what my add.html looks like:
Any guidance would be greatly appreciated.
UPDATE 1
Based off of some additional research I will not be able to do what I want with the code I have due to security reasons I won't be able to find the true path without using other methods. I am struggling with finding a way to implement the example from this link https://hacks.mozilla.org/2012/02/storing-images-and-files-in-indexeddb/.
Upvotes: 2
Views: 3036
Reputation: 18690
This is an incomplete answer, just some notes. If you insist on using indexedDB, you probably want to store the uploaded files as blobs within indexedDB. Next, you want to set an image's src to the blob's url, using URL.createObjectURL
. Keep in mind that some browsers have issues with storing blobs, so whether this works depends on which browsers you are supporting.
Here is some rough code to get you started:
addEventListener('DOMContentLoaded', event => {
attachUploadHandler();
});
function attachUploadHandler() {
const input = document.querySelector('#uploader');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/png, image/gif, image/jpeg');
input.onchange = onchange;
}
function openDb() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onupgradeneeded = onupgradeneeded;
request.onsuccess = _ => resolve(request.result);
request.onerror = _ => reject(request.error);
});
}
function onupgradeneeded(event) {
const conn = event.target.result;
conn.createObjectStore('store');
}
// Handle file uploader change event
async function onchange(event) {
const files = event.target.files;
const conn = await openDb();
await new Promise((resolve, reject) => {
const tx = conn.transaction('store', 'readwrite');
tx.oncomplete = resolve;
tx.onerror = _ => reject(new Error('file storage error'));
const store = tx.objectStore('store');
for(const file of files) {
store.put(file);
}
});
conn.close();
}
Next you probably want to use out-of-line keys with the image store, then store image id in customer store. then you load all the customers, and for each customer, query the image store for the corresponding image.
At the moment I forgot how to do out-of-line read/write stuff, so you'll have to figure that one out.
function getCustomers(conn) {
return new Promise((resolve, reject) {
const tx = conn.transaction(['customers', 'images']);
tx.oncomplete = resolve(customers);
const customer_store = tx.objectStore('customers');
const image_store = tx.objectStore('images');
function set_customer_image(customer, event) {
customer.image_blob = event.target.result;
}
const customers_request = customer_store.getAll();
customers_request.onsuccess = event => {
const customers = customers_request.result;
for(const customer of customers) {
const image_request = image_store.get(customer.image_id);
image_request.onsuccess = set_customer_image.bind(image_request, customer);
}
};
});
}
Then, when rendering, the trick is to use URL.createObjectURL for each blob, and set image_element.src to that.
function renderCustomers(customers) {
const image_placeholder_url_string = 'customer-image-placeholder.gif';
for(const customer of customers) {
const tr = new HTMLTableRowElement();
// now, the key part for the customer image cell
const td = new HTMLTableCellElement();
const image = new HTMLImageElement();
if(customer.image_blob) {
image.src = URL.createObjectURL(customer.image_blob);
} else {
image.src = image_placeholder_url_string;
}
td.appendChild(image);
tr.appendChild(td);
}
}
Upvotes: 1