1

Having an HTML table in which I have some checkboxes, I would like to save on a local .JSON file, thus not localStorage the state of each checkbox and, when I reload the page, I would like automatically to reload (if exists) the saved JSON file, in order to retrieve the checkboxes states previously configured.

The table has the structure as the one here below. It's enclosed into a form. The number of the lines are dynamically built, thus is variable.

LocalStorage method, doesn't solve the problem because I cannot port that file form a PC to another, retrieving the state of the checkboxes. Which is my need.

With the code here below, I was able to write the file. But when I try to load it, it fails returning:

Uncaught SyntaxError: Unexpected token i in JSON at position 1

But from what I saw, the JSON file is correctly formed. I can see it in Console (Chrome) as well.

JSON

{"checkfield0":true,"checkfield1":true,"checkfield2":false,"checkfield3":false}

JQUERY

(function($) {
    $.fn.toJSON = function() {
        var $elements = {};
        var $form = $(this);
        $form.find('input, select, textarea').each(function(){
            var name = $(this).attr('name')
            var type = $(this).attr('type')
            if(name){
            var $value;
            if(type == 'radio'){
                $value = $('input[name='+name+']:checked', $form).val()
            } else if(type == 'checkbox'){
                $value = $(this).is(':checked')
            } else {
                $value = $(this).val()
            }
            $elements[$(this).attr('name')] = $value
            }
        });
        return JSON.stringify( $elements )
    };
    $.fn.fromJSON = function(json_string) {
        var $form = $(this)
        var data = JSON.parse(json_string)
        $.each(data, function(key, value) {
            var $elem = $('[name="'+key+'"]', $form)
            var type = $elem.first().attr('type')
            if(type == 'radio'){
            $('[name="'+key+'"][value="'+value+'"]').prop('checked', true)
            } else if(type == 'checkbox' && (value == true || value == 'true')){
            $('[name="'+key+'"]').prop('checked', true)
            } else {
            $elem.val(value)
            }
        })
    };
}( jQuery ));


// TESTING CODE.
// IT WORKS ONLY ON SAVE. NOT ON LOAD
$(document).ready(function(){
    $("#SaveFile").on('click', function(){
        console.log("Saving form data...")
        var data = $("form#TableForm").toJSON()
        console.log(data);
        //localStorage['form_data'] = data;
        function saveText(text, filename){
        var a = document.createElement('a');
        a.setAttribute('href', 'data:text/plain;charset=utf-8,'+encodeURIComponent(text));
        a.setAttribute('download', filename);
        a.click()
        }
        saveText( data, "FORM.fields.json" );

        
        return false;
    })
    
    $("#LoadFile").on('click', function(){
    
        console.log("Loading form data...")  
        console.log("file:///k:/projects/FORM.fields.json")// in CONSOLE the JSON is correctly loaded
        $("form#TableForm").fromJSON("file:///k:/projects/FORM.fields.json")

    })
});

HTML

<form  class="center" action="#" method="get" id="TableForm">
    <button id="SaveFile">Save</button>
    <button id="LoadFile">Load</button>
    <input type="reset">

    <table class="center">
        <tbody>
            <tr>
                <td><input type="checkbox" name="checkfield0" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield1" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield2" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield3" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
        </tbody> 
    </table>
</form>
Tormy Van Cool
  • 658
  • 10
  • 29
  • Have a look [here](https://stackoverflow.com/a/372333/2159528). – Louys Patrice Bessette May 30 '21 at 15:00
  • `console.log("file:///k:/projects/FORM.fields.json")// in CONSOLE the JSON is correctly loaded` This would log the path to the JSON, not the JSON itself. Same problem with `fromJSON`; it's trying to do `JSON.parse("file:///k:/projects/FORM.fields.json")` which gives that exact error. – Heretic Monkey May 30 '21 at 21:41
  • The console.log shows me, in the console window, the entire content of the file. Hence it reads the JSON, not only the path. – Tormy Van Cool May 30 '21 at 22:02
  • Unfortunately no, it doesn't. The answer of @PoorlyWrittenCode fully answers to my question – Tormy Van Cool May 31 '21 at 08:57

1 Answers1

1

That is some interesting code you have here. I'm guessing your challenge is coming from the fact that, for security reasons, javascript can't just read a local file. It has to be a user initiated event, and you need to use <input type="file"> and the FileReader API

Here is a demonstration:

//prevent the form from submitting on button click
$('form button').on('click', e => e.preventDefault());

//open file loader
$('button.file-load').on('click', function() {
  $('.file-loader').click();
});

//uncheck all checkboxes
$('button.reset').on('click', function() {
  $('table input[type="checkbox"]').each(function() {
    this.checked = false;
  });
});

//convert checkbox states to json and save
$('button.file-save').on('click', function() {
  let a = document.createElement('a'),
      states = {};
      
  $('table input[type="checkbox"]').each(function() {
    states[this.name] = this.checked
  });
      
  a.setAttribute('href', 'data:text/plain;charset=utf-8,' + JSON.stringify(states));
  a.setAttribute('download', 'checkbox_saved_states.json');
  a.click();
  a.remove();
});

//load saved states
$('.file-loader').on('change', function() {
  let reader = new FileReader();

  reader.onload = function(file) {
    $.each(JSON.parse(file.target.result), function(id, state) {
      $(`form input[name="${id}"]`).prop('checked', state);
    });
  };

  reader.readAsText(this.files[0]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form  class="center" action="#" method="get" id="TableForm">
    <button class="file-save">Save</button>
    <button class="file-load">Load</button>
    <!-- if <input type="file"> isn't hidden, it shows the current file selected -->
    <input class="file-loader" type="file" hidden>
    <button class="reset">Reset</button>

    <table class="center">
        <tbody>
            <tr>
                <td><input type="checkbox" name="checkfield0" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield1" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield2" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
            <tr>
                <td><input type="checkbox" name="checkfield3" /></td>
                <td>Some descriptive element here</td>
                <td>Some descriptive element here</td>
            </tr>
        </tbody> 
    </table>
</form>
PoorlyWrittenCode
  • 1,003
  • 1
  • 7
  • 10