I’ll try to explain what I’m looking for. A user can fill out a collection of form fields and save the information (in localStorage). This is easy to code. But I want to provide a button so the user can duplicate the collection of form fields and add different information, and those fields would be saved as well as the former. User should be able to delete collections at will.
I could hard-code the form for a number of times, but that would put a limit on how many they can save. I don’t want to enforce this limit.
How do we do the effect that duplicates fields for saving in localStorage?
You’ll need a template that can be duplicated and rendered with the data from localStorage. Loop through the array and render a set of fields for each. Have a go and post your code.
Thanks, Mark. I don’t have the code at hand; coming up in the project later. Didn’t think to put it all in an array. Will have to think about that approach. Thanks!
I might end up using webSql instead. I wrote about it in one of my step-by-step articles a while ago and it worked. The problem is, will webSql still be around later? Seems to be deprecated. (I would post the link to the article, but it could be construed as spam.)
From what I can see, it will never be around for IE, Edge or Firefox. It is also not supported from web workers. Future support in Chrome, Safari etc. is unknown.
indexedDB was announced as its replacement in November 2010.
Your main issue isn’t how to store the data, it’s rendering dynamic templates and loading data into them. After that’s working you can easily persist on a server or in localStorage.
But would that have to be dynamic templates? If the form itself is fixed, you could just store such form configurations as an array of objects of the form {inputId1: inputValue1, ...}… like
var configurations = localStorage.configurations ?
JSON.parse(localStorage.configurations) : [];
var form = document.getElementById('my-form');
var inputs = form.querySelectorAll('input');
var saveConfiguration = function() {
var configuration = {};
var input;
// Get the values of the input elements and store
// them in a configuration object
for (var i = 0; i < inputs.length; i++) {
input = inputs[i];
configuration[input.id] = input.value;
}
// Push it to the local storage configurations
configurations.push(configuration);
localStorage.configurations = JSON.stringify(configurations);
};
var restoreConfiguration = function(which) {
var configuration;
var element;
// Access the configuration by a key, e.g. from
// a select element
configuration = configurations[which];
// Iterate over the properties of the configuration
for (var i in configuration) {
// Fill the input fields with saved configuration
element = document.getElementById(i);
if (element) {
element.value = configuration[i];
}
}
};
var clearConfiguration = function() {
configurations = [];
localStorage.configurations = JSON.stringify(configurations);
};
This way it wouldn’t even matter if the form changes at some point, as only the matching fields will be filled. Here’s a small demo (doesn’t seem to work as a fiddle). Alternatively, you could set the input values as HTML attributes when saving the configuration, and store the form’s entire innerHTML… would be a bit dirty, but allow storing entirely arbitrary forms. Like (mutatis mutandis)
var saveConfiguration = function() {
var inputs = form.querySelectorAll('input');
// Set the value attributes of the input elements
// to store them with the form's innerHTML
for (var i = 0; i < inputs.length; i++) {
inputs[i].setAttribute('value', inputs[i].value)
}
// Push the form the local storage configurations
configurations.push(form.innerHTML);
localStorage.configurations = JSON.stringify(configurations);
};
var restoreConfiguration = function(which) {
// Set the form to the configuration
form.innerHTML = configurations[which];
};
Wow, this works well. Good demo. However, restoring a set, modifying one of its values, and saving should overwrite the set, not create a new set with the same name. And instead of clearing all fields for all sets, only the set selected in the dropdown.
“My understanding was that the aim was to show repeated sets of fields, templates simplify this greatly.”
The demo given was sufficient. It will show repeated sets of fields after selecting any of them from a drop-down box. That’s a good way to keep them in order, and allows them to make as many sets as they wish.
When restoring a set, you’d remember the index (which could be undefined otherwise), and later save it to configurations[index] instead of .push()ing it.
Instead of storing (only) the values, you’d store the checked property here. Like
var radios = form.querySelectorAll('input[type="radio"]');
for (var i = 0; i < radios.length; i++) {
radio = radios[i];
configuration[radio.id] = radio.checked;
}
When restoring such a configuration, you’d have to check whether the input is a radio then, like (in the for loop as above)
if (element) {
if (element.type === 'radio') {
element.checked = configuration[i];
} else {
element.value = configuration[i];
}
}
Gist (download and add .html to end of filename to open):
Right now I’m trying to get all the localstorage info into a single textarea field for export. This is successful. However, I only want the checked inputs, not all the inputs. What changes do I need to narrow that down?
Well, the data model above was very simple to just get you started. :-) If you want to perform further operations on the input elements (depending on their type, checked-state etc.) and sets themselves you might need a more appropriate format to to store them. Maybe something like
Actually, you’d save it just the same way – by stringify()ing the entire set array. What does require a bit more logic is storing the set itself. Here’s another take:
// Restore the sets from the local storage, if available
var sets = localStorage.sets ?
JSON.parse(localStorage.sets) : [];
// The form to store
var form = document.getElementById('my-form');
// The input elements
var inputs = form.querySelectorAll('input');
// The select to restore and delete sets
var select = document.getElementById('select');
var addOption = function(set) {
// Create a new option for the set
var option = document.createElement('option');
// The value of the option references the set
// by its index
option.value = set.setId;
// As label we'll arbitrarily choose the `foo` value
option.textContent = 'Set ' + set.setId;
// Append it to the select
select.appendChild(option);
// Enable the restore button
restore.removeAttribute('disabled');
};
var saveSet = function() {
var set = {setId: Date.now(), inputs: []};
var input;
// Get the relevant properties of the input
// elements and store them in the set object
for (var i = 0; i < inputs.length; i++) {
input = inputs[i];
set.inputs.push({
id: input.id,
value: input.value,
checked: input.checked
});
}
// Push it to the local storage sets
sets.push(set);
localStorage.sets = JSON.stringify(sets);
// Append an option to the select
addOption(set);
};
var restoreSet = function() {
var set;
var input;
var item;
// Access the set by the select value
set = sets.find(function(el) {
return el.setId === parseInt(select.value);
});
if (!set) return;
// Iterate over the input elements of the form
for (var i = 0; i < inputs.length; i++) {
input = inputs[i];
// Get the corresponding item in the set
item = set.inputs.find(function(el) {
return el.id === input.id;
});
if (item) {
// Set the input value to the value in the stored set
input.value = item.value;
// If it's a radio or checkbox, also apply the checked state
if (input.type === 'radio' || input.type === 'checkbox') {
input.checked = item.checked;
}
}
}
};
var removeSet = function() {
// The selected option
var option = select.querySelector('option[value="' + select.value + '"]');
// Remove the selected set
sets = sets.filter(function(el) {
return el.setId !== select.value;
});
localStorage.sets = JSON.stringify(sets);
// Also remove the corresponding select option
select.removeChild(option);
};
I’ve also updated the above demo link. If you have difficulties understanding one line or the other, don’t hesitate to ask! :-)
I tried to modify so the user can create his own form set name:
HTML (ignore the spaces after the brackets to make the HTML show up):
< div>
< label for=“setname”>Setname< /label>
< input type=“text” id=“setname” name=“setname”>
< /div>
JS:
var saveSet = function() { var setname = document.getElementById(‘setname’);
var set = {setId: setname, inputs: };
var input;
But this returned a very unhelpful set name in the dropdown. After restoring the original code, this edit did not work either:
// As label we’ll arbitrarily choose the foo value var setname = document.getElementById(‘setname’);
option.textContent = setname + set.setId;