Olivier
Olivier

Reputation: 655

How to restore the state of checkboxes using WebExtensions storage API

I'm building a firefox web extension and I'd like to give its users some options that they can check/uncheck. The state of the checkboxes is saved locally via the WebExtensions storage API, but I'm not quite sure how to restore the saved state for each option.

Here is the options.html file:

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
  </head>

<body>
    <form method="post">
        <fieldset>
                <legend>My favorite veggies</legend>
                        <input type="checkbox" name="eggplant" id="eggplant" />Eggplant<br />
                        <input type="checkbox" name="zucchini" id="zucchini" />Zucchini<br />
                        <input type="checkbox" name="tomatoe" id="tomatoe" />Tomatoe<br />
                        <input type="submit" value="Submit" />
        </fieldset>
    </form>
    <script src="options.js"></script>
</body>

</html>

The options.js file that saves/restores the state of the checkboxes is as follows:

function onError(error) {
  console.log(`Error: ${error}`);
}

function saveOptions(e) {
  // List of search engines to be used
  let eggplant = {
      name: "Eggplant", 
      imageUrl: "https://www.organicfacts.net/wp-content/uploads/2013/06/Eggplant-1020x765.jpg",
      show: form.eggplant.checked
  };
  let zucchini = {
      name: "Zucchini", 
      imageUrl: "https://www.organicfacts.net/wp-content/uploads/zucchini.jpg",
      show: form.zucchini.checked
  };
  let tomatoe = {
      name: "Tomatoe", 
      imageUrl: "https://www.organicfacts.net/wp-content/uploads/2013/05/Organictomato1-1020x765.jpg",
      show: form.tomatoe.checked
  };
  let setting = browser.storage.sync.set({
    eggplant,
    zucchini,
    tomatoe
  });
  setting.then(null,onError);
  e.preventDefault();
}

function restoreOptions() {
  var gettingItem = browser.storage.sync.get({
    'show'
  });
  gettingItem.then((res) => {
    document.getElementById("eggplant").checked = eggplant.show;
    document.getElementById("zucchini").checked = zucchini.show;
    document.getElementById("tomatoe").checked = tomatoe.show;
  });
}

document.addEventListener('DOMContentLoaded', restoreOptions);
document.querySelector("form").addEventListener("submit", saveOptions);

Upvotes: 0

Views: 216

Answers (1)

Xan
Xan

Reputation: 77523

Here's the state of your storage after the set operation:

{
  eggplant: {
    name: "Eggplant", 
    imageUrl: "https://www.organicfacts.net/wp-content/uploads/2013/06/Eggplant-1020x765.jpg",
    show: true
  },
  zucchini: { /* ... */ },
  tomatoe: { /* ... */ }
}

However, in restoreOptions, you query the storage with the key "show" - or, more specifically, with a syntactically incorrect object literal {"show"}.

Even if you fix it to be "show" is no such top-level key (and that's the only type of query you're allowed), so the result comes up as empty.

  • If you want to query the entire storage, pass null.
  • If you want to query a single top-level key, pass it as a string.
  • If you want a set of top-level keys you know in advance, pass an array of key strings.
  • If you want to provide a default in case the key is not in storage, pass an object mapping keys to defaults.

Here's how to organize your code with using defaults:

const defaults = {
  eggplant: {
    name: "Eggplant", 
    imageUrl: "(redacted for brevity)",
    show: false
  },
  zucchini: {
    name: "Zucchini", 
    imageUrl: "(redacted for brevity)",
    show: false
  },
  tomatoe: {
    name: "Tomatoe", 
    imageUrl: "(redacted for brevity)",
    show: false
  }
};

function saveOptions() {
  let settings = {}
  for (let setting in defaults) {
    settings[setting] = {
      ...defaults[setting],       // spread the default values
      show: form[setting].checked // override show with real data
    }
  } 
  browser.storage.sync.set(settings).then(null, onError);
}

function restoreOptions() {
  browser.storage.sync.get(defaults).then(
    (data) => {
      document.getElementById("eggplant").checked = data.eggplant.show;
      document.getElementById("zucchini").checked = data.zucchini.show;
      document.getElementById("tomatoe").checked = data.tomatoe.show;
    }
  );
}

Of note: don't use submit and preventDefault, just use type="button" inputs and click.

Small edit: Object spread (...defaults[setting]) is still experimental (though supported in recent Chrome/FF), a more ES6-compliant code would be

for (let setting in defaults) {
  // shallow copy default settings
  settings[setting] = Object.assign({}, defaults[setting]);
  // override show property
  settings[setting].show = form[setting].checked;
}

Less concise, somewhat more compatible.

Upvotes: 1

Related Questions