Как мне использовать локальное хранилище для списка дел? - PullRequest
0 голосов
/ 21 июня 2020

Меня просят составить список дел и сохранить каждую задачу (как исходную, так и предоставленную пользователем) в локальном хранилище. Мой учитель провел очень простую демонстрацию чего-то совершенно другого, и я потратил несколько часов, пытаясь понять это. Когда я посмотрел на решение, честно говоря, не могу понять. Это выглядит действительно сложно, и я даже не знаю, с чего начать. Если бы кто-нибудь мог дать мне какие-либо подсказки, это было бы здорово!

Мой код:

let ul = document.querySelector('ul');

let newItem = document.querySelector('input[type=text]');
let checkbox = document.createElement('input');
checkbox.setAttribute('type', 'checkbox');

function output() {
    let newTodo = document.createElement('li');
    newTodo.innerText = newItem.value;
    newTodo.classList.add('todo');
    let ulAppend = ul.append(newTodo);
    ul.append(newTodo);
    let checkboxAppend = newTodo.append(checkbox);
    newTodo.append(checkbox);
    newItem.value = '';
}

let button = document.querySelector('.btn');
button.addEventListener('click', output);

ul.addEventListener('click', function(e) {
    if (e.target.tagName === 'LI') {
        e.target.remove();
    } else if (e.target.tagName === 'INPUT') {
        e.target.parentElement.classList.toggle('finished');
    }
});

Код моего учителя / решение для локального хранилища:

const todoForm = document.getElementById("newTodoForm");
const todoList = document.getElementById("todoList");

// retrieve from localStorage
const savedTodos = JSON.parse(localStorage.getItem("todos")) || [];
for (let i = 0; i < savedTodos.length; i++) {
  let newTodo = document.createElement("li");
  newTodo.innerText = savedTodos[i].task;
  newTodo.isCompleted = savedTodos[i].isCompleted ? true : false;
  if (newTodo.isCompleted) {
    newTodo.style.textDecoration = "line-through";
  }
  todoList.appendChild(newTodo);
}

todoForm.addEventListener("submit", function(event) {
  event.preventDefault();
  let newTodo = document.createElement("li");
  let taskValue = document.getElementById("task").value;
  newTodo.innerText = taskValue;
  newTodo.isCompleted = false;
  todoForm.reset();
  todoList.appendChild(newTodo);

  // save to localStorage
  savedTodos.push({ task: newTodo.innerText, isCompleted: false });
  localStorage.setItem("todos", JSON.stringify(savedTodos));
});

todoList.addEventListener("click", function(event) {
  let clickedListItem = event.target;

  if (!clickedListItem.isCompleted) {
    clickedListItem.style.textDecoration = "line-through";
    clickedListItem.isCompleted = true;
  } else {
    clickedListItem.style.textDecoration = "none";
    clickedListItem.isCompleted = false;
  }

  // breaks for duplicates - another option is to have dynamic IDs
  for (let i = 0; i < savedTodos.length; i++) {
    if (savedTodos[i].task === clickedListItem.innerText) {
      savedTodos[i].isCompleted = clickedListItem.isCompleted;
      localStorage.setItem("todos", JSON.stringify(savedTodos));
    }
  }
});

Хотя мой код проще (по крайней мере, из того, что я могу сказать), он работает точно так же, как его код.

Ответы [ 3 ]

0 голосов
/ 21 июня 2020

Важная часть - это (де) сериализация данных. Это означает:

  • чтение из localStorage (JSON.parse(localStorage.getItem("todos")) || [])

    Мы добавляем значение по умолчанию [], потому что, если ключ todos не существует, мы получит null, и мы ожидаем список

  • сохранение в localStorage (localStorage.setItem("todos", JSON.stringify(savedTodos)))

Нам нужно JSON.parse и его дополнительная операция JSON.stringify для синтаксического анализа и сохранения строк, потому что localStorage может хранить только строки.

В вашем случае вам нужно прочитать данные из localStorage и отобразить начальный список. Чтобы сохранить его в localStorage, вам снова необходимо сериализовать данные. См. Следующие фрагменты (ссылка на рабочий JSFIDDLE , потому что приведенный ниже пример не работает в среде песочницы StackOverflow):

let ul = document.querySelector('ul');

let newItem = document.querySelector('input[type=text]');

const Store = {
  serialize () {
    return [].slice.call(document.querySelectorAll("li")).map(c => {
      return {
        text: c.textContent,
        finished: c.querySelector("input").checked
      }
    })
  },
  get () {
    return JSON.parse(localStorage.getItem("todos")) || []
  },
  save () {
    return localStorage.setItem("todos", JSON.stringify(Store.serialize()))
  }
}

const firstItems = Store.get()
firstItems.forEach(it => {
  output(it.text, it.finished)
})

function output(v, finished) {
  let newTodo = document.createElement('li');
  newTodo.innerText = v || newItem.value;
  newTodo.classList.add('todo');
  let ulAppend = ul.append(newTodo);
  ul.append(newTodo);

  // Create a checkbox for each item
  let checkbox = document.createElement('input');
  if (finished) {
    checkbox.checked = true
  }
  checkbox.setAttribute('type', 'checkbox');

  let checkboxAppend = newTodo.append(checkbox);
  newTodo.append(checkbox);
  newItem.value = '';
}

let button = document.querySelector('.btn');
button.addEventListener('click', () => {
    output()
  Store.save()
});

ul.addEventListener('click', function(e) {
  if (e.target.tagName === 'LI') {
    e.target.remove();
  } else if (e.target.tagName === 'INPUT') {
    e.target.parentElement.classList.toggle('finished');
  }
  // Update the value in localStorage when you delete or add a new item
  Store.save()
});
<ul></ul>
<input type="text" /> <button class="btn">Submit</button>

Я добавил переменную Store, чтобы упростить способ получения и установки данных в localStorage.

Метод сериализации будет читать TODO из списка. document.querySelectorAll("li") возвращает NodeList, но, выполняя [].slice.call(...), мы конвертируем его в Array.

0 голосов
/ 21 июня 2020

Я аннотировал решение, которое вы опубликовали, с некоторыми комментариями, которые помогут вам пройти через него.

// Retrieve elements and store them in variables
const todoForm = document.getElementById("newTodoForm");
const todoList = document.getElementById("todoList");

// Get data stored in localStorage under the key "todos".
// The data type will be a string (local storage can only store strings).
// JSON is a global object that contains methods for working with data represented as strings.
// The `||` syntax is an OR operator and is used here to set an empty array as a fallback in case `localStorage` is empty
const savedTodos = JSON.parse(localStorage.getItem("todos")) || [];

// Create a loop the same length as the list of todos
for (let i = 0; i < savedTodos.length; i++) {
  // Create an <li> element in memory (does not appear in the document yet)
  let newTodo = document.createElement("li");
  // Set the inner text of that new li with the contents from local storage.
  // The savedTodos[i] is accessing data in the localStorage array.
  // The [i] is a different number each loop.
  // The `.task` is accessing 'task' property on the object in the array.
  newTodo.innerText = savedTodos[i].task;
  // Create a new property on the element called `isCompleted` and assign a boolean value.
  // This is only accessible in code and will not show up when appending to the DOM.
  newTodo.isCompleted = savedTodos[i].isCompleted ? true : false;
  // Check the value we just set.
  if (newTodo.isCompleted) {
    // Create a style for the element if it is done (strike it out)
    newTodo.style.textDecoration = "line-through";
  }
  // Actually append the new element to the document (this will make it visible)
  todoList.appendChild(newTodo);
}

// `addEventListener` is a function that registers some actions to take when an event occurs.
// The following tells the browser - whenever a form is submitted, run this function.
todoForm.addEventListener("submit", function(event) {
  // Don't try to send the form data to a server. Stops page reloading.
  event.preventDefault();
  // Create a <li> element in memory (not yet visible in the document)
  let newTodo = document.createElement("li");
  // Find element in the document (probably a input element?) and access the text value.
  let taskValue = document.getElementById("task").value;
  // Set the text of the <li>
  newTodo.innerText = taskValue;
  // Set a property on the <li> call `isCompleted`
  newTodo.isCompleted = false;
  // Empty out all the input fields in the form
  todoForm.reset();
  // Make the new <li> visible in the document by attaching it to the list
  todoList.appendChild(newTodo);

  // `push` adds a new element to the `savedTodos` array. In this case, an object with 2 properties.
  savedTodos.push({ task: newTodo.innerText, isCompleted: false });
  // Overwrite the `todos` key in local storage with the updated array.
  // Use the JSON global object to turn an array into a string version of the data
  // eg [1,2,3] becomes "[1,2,3]"
  localStorage.setItem("todos", JSON.stringify(savedTodos));
});

// This tells the browser - whenever the todoList is clicked, run this function.
// The browser will call the your function with an object that has data about the event.
todoList.addEventListener("click", function(event) {
  // the `target` of the event is the element that was clicked.
  let clickedListItem = event.target;

  // If that element has a property called `isCompleted` set to true
  if (!clickedListItem.isCompleted) {
    // update the styles and toggle the `isCompleted` property.
    clickedListItem.style.textDecoration = "line-through";
    clickedListItem.isCompleted = true;
  } else {
    clickedListItem.style.textDecoration = "none";
    clickedListItem.isCompleted = false;
  }

  // The code above changes the documents version of the data (the elements themselves)
  // This loop ensures that the array of todos data is kept in sync with the document
  // Loop over the array
  for (let i = 0; i < savedTodos.length; i++) {
    // if the item in the array has the same text as the item just clicked...
    if (savedTodos[i].task === clickedListItem.innerText) {
      // toggle the completed state
      savedTodos[i].isCompleted = clickedListItem.isCompleted;
      // Update the localStorage with the new todos array.
      localStorage.setItem("todos", JSON.stringify(savedTodos));
    }
  }
});

Имейте в виду, что в вашем списке задач есть 2 источника состояния. Один - как выглядит документ, а другой - массив todos данных. Множество проблем возникает из-за того, чтобы убедиться, что эти 2 синхронизируются c.

Если каким-то образом в документе один из элементов списка был перечеркнут, но ваш массив данных показывает, что все todos соответствуют не завершено какая версия правильная? Здесь нет правильного ответа, но управление состоянием - это то, что вы могли бы рассмотреть при разработке приложений в будущем. Redux - хорошая библиотека js с хорошо понятным шаблоном, помогающим решить эту проблему. Надеюсь, этот последний комментарий не слишком смущает. Удачи!

0 голосов
/ 21 июня 2020

Локальное хранилище сохраняет объект JSON на компьютер пользователя. Вы должны создать массив задач, добавлять этот массив к каждой новой задаче, а затем установить этот элемент в локальное хранилище.

let ul = document.querySelector('ul');

const savedTodos = JSON.parse(localStorage.getItem("todos")) || []; // Retrieves local storage todo OR creates empty array if none exist
let newItem = document.querySelector('input[type=text]');
let checkbox = document.createElement('input');
checkbox.setAttribute('type', 'checkbox');

function output() {
    let newTodo = document.createElement('li');
    newTodo.innerText = newItem.value;
    newTodo.classList.add('todo');
    ul.append(newTodo);
    newTodo.append(checkbox);

    savedTodos.push({task: newItem.value, isCompleted: false}); // Appends the new todo to array
    localStorage.setItem("todos", JSON.stringify(savedTodos)); //Converts object to string and stores in local storage

    newItem.value = '';
}
...