Dziennik krok 7: ten sam model na stronie

Projekt: dziennik ocen krok po kroku - JavaScript od podstaw: konsola (HTML/CSS/JS)

Ostatni krok: klasa `Gradebook` przechodzi na stronę bez żadnej zmiany. Widokiem jest teraz tabela HTML budowana przez `createElement`, a ocenę dopisuje formularz. Pola formularza są sklejane w wiersz `uczeń;przedmiot;ocena` i sprawdzane tą samą metodą `parseLine`, więc błędy wyglądają tak samo jak w konsoli. Spróbuj dodać ocenę 7 albo zostawić puste imię.

<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="utf-8">
  <title>Dziennik</title>
  <style>
    body { font-family: sans-serif; max-width: 36em; }
    table { border-collapse: collapse; margin: 0.5em 0; }
    th, td { border: 1px solid #94a3b8; padding: 0.2em 0.6em; }
    input, button { font: inherit; padding: 0.2em 0.4em; width: 8em; }
    button { width: auto; }
    #msg { min-height: 1.4em; }
  </style>
</head>
<body>
  <h1>Dziennik</h1>
  <p>
    <input id="student" placeholder="Uczeń">
    <input id="subject" placeholder="Przedmiot">
    <input id="grade" placeholder="Ocena">
    <button id="add">Dodaj</button>
  </p>
  <p id="msg"></p>
  <table id="report">
    <thead><tr><th>Uczeń</th><th>Oceny</th><th>Średnia</th></tr></thead>
    <tbody></tbody>
  </table>
  <script>
    const DATA = [
      'Ola;matematyka;5', 'Jan;matematyka;3', 'Ola;polski;4', 'Ewa;matematyka;6',
      'Jan;polski;2', 'Ewa;polski;5', 'Ola;matematyka;6', 'Jan;matematyka;4',
    ].join('\n');
    class Gradebook {
      #records = [];

      static parseLine(line) {
        const parts = line.split(';');
        if (parts.length !== 3) throw new Error('oczekiwano 3 pól');
        const [name, subject, gradeText] = parts.map(p => p.trim());
        if (name === '' || subject === '') throw new Error('puste imię lub przedmiot');
        const grade = Number(gradeText);
        if (!Number.isInteger(grade) || grade < 1 || grade > 6) {
          throw new Error(`ocena spoza skali 1-6: "${gradeText}"`);
        }
        return { name, subject, grade };
      }

      static average(grades) {
        return grades.reduce((a, b) => a + b, 0) / grades.length;
      }

      add(record) {
        this.#records.push(record);
      }

      load(text) {
        const errors = [];
        text.split('\n').forEach((line, i) => {
          try {
            this.add(Gradebook.parseLine(line));
          } catch (e) {
            errors.push(`wiersz ${i + 1}: ${e.message}`);
          }
        });
        return errors;
      }

      gradesBy(key) {
        const groups = {};
        for (const r of this.#records) {
          if (!(r[key] in groups)) groups[r[key]] = [];
          groups[r[key]].push(r.grade);
        }
        return groups;
      }

      best() {
        let best = null;
        for (const [name, grades] of Object.entries(this.gradesBy('name'))) {
          const avg = Gradebook.average(grades);
          if (best === null || avg > best.avg) best = { name, avg };
        }
        return best;
      }

      get records() {
        return [...this.#records];
      }
    }
    const book = new Gradebook();
    book.load(DATA);
    const msg = document.getElementById('msg');

    function cells(tr, texts) {
      for (const text of texts) {
        const td = document.createElement('td');
        td.textContent = text;
        tr.appendChild(td);
      }
    }

    function render() {
      const tbody = document.querySelector('#report tbody');
      tbody.replaceChildren();
      for (const [name, grades] of Object.entries(book.gradesBy('name'))) {
        const tr = document.createElement('tr');
        cells(tr, [name, grades.join(' '), Gradebook.average(grades).toFixed(2)]);
        tbody.appendChild(tr);
      }
    }

    document.getElementById('add').addEventListener('click', () => {
      const line = ['student', 'subject', 'grade'].map(id => document.getElementById(id).value).join(';');
      try {
        book.add(Gradebook.parseLine(line));
        msg.textContent = 'Dodano ocenę';
        render();
      } catch (e) {
        msg.textContent = 'Błąd: ' + e.message;
      }
    });

    render();
  </script>
</body>
</html>

Ładowanie interaktywnego edytora... Jeśli się nie pojawi, włącz JavaScript.

Wszystkie lekcje kursu JavaScript od podstaw: konsola·Zaloguj się, aby zapisać postęp na koncie.