Python REST API с Flask, Connexion и SQLAlchemy-Часть 4

В части 3 этой серии вы добавили отношения к REST API и к базе данных, которая его поддерживает. Это дало вам мощный инструмент, который вы можете использовать для создания интересных программ, которые отправляют постоянные данные, а также отношения между этими данными, в систему баз данных. Наличие REST API дает вам возможность создать одностраничное приложение (SPA) с HTML, CSS и JavaScript. Это хорошая отправная точка, прежде чем вы сделаете переход к более мощным front-end фреймворкам, таким как Angular или React.
В этой статье вы узнаете, как сделать следующее:

  • Структурировать HTML-файл, чтобы он действовал как шаблон одностраничного веб-приложения
  • Используйть каскадные таблицы стилей (CSS) для стилизации представления приложения
  • Используйть собственный JavaScript для добавления интерактивности в приложение
  • Используйть JavaScript для выполнения запросов HTTP AJAX к REST API, разработанному вами в 3 части этой серии

Для Кого Предназначена Эта Статья

Часть 1 этой серии помогла вам построить REST API, а часть 2 показала, как подключить этот REST API к базе данных. В части 3 Вы добавили отношения к API REST и поддерживающей базе данных.

Эта статья посвящена представлению этого REST API пользователю в виде веб-приложения на основе браузера. Эта комбинация дает вам как передние, так и задние способности, что является полезным и мощным набором навыков.

Создание Одностраничных Приложений

В части 3 этой серии вы добавили отношения к API REST и базе данных для представления заметок, связанных с людьми. Другими словами, вы создали своего рода мини-блог. Веб-приложения, построенные в части 3, показали вам один из способов представления и взаимодействия с REST API. Вы переходили между тремя одностраничными приложениями (SPA), чтобы получить доступ к различным частям REST API.

Хотя вы могли бы объединить эту функциональность в один спа-центр, такой подход сделал бы концепции стиля и интерактивности более сложными, без особой дополнительной ценности. По этой причине каждая страница была полным, автономным спа-салоном.

В этой статье вы сосредоточитесь на спа-центре People, который представляет список людей в базе данных и предоставляет функцию редактора для создания новых людей и обновления или удаления существующих. Главная страница и страницы заметок концептуально похожи.

Какие существуют структуры?

Существуют библиотеки, которые обеспечивают встроенную и надежную функциональность для создания СПА-систем. Например, Библиотека Bootstrap предоставляет популярную структуру стилей для создания последовательных и красивых веб-приложений. Он имеет расширения JavaScript, которые добавляют интерактивность стилизованным элементам DOM.

Существуют также мощные фреймворки веб-приложений, такие как React и Angular, которые дают вам полные системы разработки веб-приложений. Они полезны, когда вы хотите создать большие многостраничные спа-центры, которые было бы громоздко строить с нуля.

Зачем Строить Свою Собственную?

При наличии инструментов, подобных перечисленным выше, почему бы вам не создать спа-центр с нуля? Возьмем, к примеру, Бутстрэп. Вы можете использовать его для создания спа-салонов, которые выглядят превосходно, и вы, безусловно, можете использовать его с вашим кодом JavaScript!

Проблема в том, что Bootstrap имеет крутую кривую обучения, на которую вам нужно будет подняться, если вы хотите хорошо ее использовать. Он также добавляет множество специфичных для Bootstrap атрибутов к элементам DOM, определенным в вашем HTML-контенте. Кроме того, такие инструменты, как React и Angular, также имеют значительные кривые обучения, которые вам нужно будет преодолеть. Однако все еще есть место для веб-приложений, которые не полагаются на такие инструменты, как эти.

Часто, когда вы создаете веб-приложение, вы хотите сначала построить доказательство концепции, чтобы увидеть, является ли приложение вообще полезным. Вам нужно будет быстро запустить его, чтобы вы могли быстрее свернуть свой собственный прототип и обновить его позже. Поскольку вы не будете вкладывать много времени в прототип, не будет слишком дорого начинать все сначала и создавать новое приложение с поддерживаемым полнофункциональным фреймворком.

Существует разрыв между тем, что вы собираетесь разрабатывать с помощью приложения People в этой статье, и тем, что вы могли бы построить с помощью полного фреймворка. Это зависит от вас, чтобы решить, где находится переломный момент между предоставлением функциональности самостоятельно или принятием фреймворка.

Части одностраничных приложений

Существует несколько основных форм интерактивности в традиционных веб-системах. Вы можете перемещаться между страницами и отправлять страницу с новой информацией. Вы можете заполнить формы, содержащие поля ввода, переключатели, флажки и многое другое. Когда вы выполняете эти действия, веб-сервер отвечает, отправляя новые файлы в ваш браузер. Затем ваш браузер снова отображает содержимое.

Одностраничные приложения нарушают этот шаблон, загружая все, что им нужно в первую очередь. Затем любая интерактивность или навигация обрабатывается JavaScript или вызовами на сервер за кулисами. Эти действия динамически обновляют содержимое страницы.

Существует три основных компонента одностраничного приложения:

  1. HTML предоставляет содержимое веб-страницы или то, что отображается вашим браузером.
  2. CSS обеспечивает представление или стиль веб-страницы. Он определяет, как должно выглядеть содержимое страницы при отображении вашим браузером.
  3. JavaScript обеспечивает интерактивность веб-страницы. Он также обрабатывает связь с внутренним сервером.

Далее вы более подробно рассмотрите каждый из этих основных компонентов.

HTML

HTML-это текстовый файл, доставляемый в ваш браузер и предоставляющий основное содержимое и структуру для одностраничного приложения. Эта структура включает определения атрибутов id и class, которые используются CSS для стилизации содержимого и JavaScript для взаимодействия со структурой. Ваш браузер анализирует HTML-файл для создания объектной модели документа (DOM), которая используется для отображения содержимого на дисплее.

Разметка в HTML-файле включает теги, например теги абзацев p и теги заголовка h1. Эти теги становятся элементами внутри DOM, когда ваш браузер анализирует HTML и выводит его на дисплей. HTML-файл также содержит ссылки на внешние ресурсы, которые ваш браузер будет загружать при анализе HTML-кода. Для спа-центра, который вы создаете в этой статье, эти внешние ресурсы являются файлами CSS и JavaScript.

CSS

Каскадные таблицы стилей (CSS) - это файлы, содержащие информацию о стиле, которая будет применена к любой структуре DOM, отображаемой из HTML-файла. Таким образом, содержание веб-страницы может быть отделено от ее представления.

В CSS стиль для структуры DOM определяется селекторами. Селектор-это просто метод сопоставления стиля с элементами внутри DOM. Например, селектор p в приведенном ниже блоке кода применяет информацию о стилизации ко всем элементам абзаца:

    p {
    font-weight: bold;
    background-color: cyan;
}

Приведенный выше стиль будет применяться ко всем элементам абзаца в DOM. Текст будет выделен жирным шрифтом и иметь цвет фона голубого цвета.

Каскадная часть CSS означает, что стили, определенные позже или в файле CSS, загруженном после другого, будут иметь приоритет над любым ранее определенным стилем. Например, вы можете определить второй стиль абзаца после приведенного выше стиля:

    p {
    font-weight: bold;
    background-color: cyan;
}

p {
    background-color: cornflower;
}

Это новое определение стиля изменит существующий стиль так, что все элементы абзаца в DOM будут иметь цвет фона василька. Это переопределяет цвет фона предыдущего стиля, но оставляет настройку веса шрифта нетронутой. Вы также можете определить новый стиль абзаца в собственном файле CSS.

Атрибуты id и class позволяют применять стиль к определенным отдельным элементам в DOM. Например, HTML для рендеринга нового DOM-кода может выглядеть следующим образом:

    

This is some introductory text

This is some text contained within a panel

Это создаст два элемента абзаца в DOM. У первого нет атрибута класса, но у второго есть атрибут класса панели. Затем вы можете создать такой стиль CSS, как этот:

    p {
    font-weight: bold;
    width: 80%;
    margin-left: auto;
    margin-right: auto;
    background-color: lightgrey;
}

.panel {
    border: 1px solid darkgrey;
    border-radius: 4px;
    padding: 10px;
    background-color: lightskyblue;
}

Здесь вы определяете стиль для всех элементов, имеющих атрибут panel. Когда ваш браузер отображает DOM, два элемента абзаца должны выглядеть следующим образом:

Оба элемента абзаца имеют первое определение стиля, примененное к ним, поскольку селектор p выбирает их оба. Но только второй абзац имеет свое значение .стиль панели применяется к нему, потому что это единственный элемент с панелью атрибутов класса, который соответствует этому селектору. Второй абзац получает новую информацию о стиле от самого автора .стиль панели и переопределяет стиль цвета фона, определенный в стиле p.

JavaScript

JavaScript предоставляет все интерактивные функции для SPA, а также динамическую связь с REST API, предоставляемым сервером. Он также выполняет все обновления для DOM, позволяя SPA действовать во многом как полное графическое приложение пользовательского интерфейса (GUI), такое как Word или Excel.

По мере развития JavaScript становится все проще и последовательнее работать с DOM, предоставляемым современными браузерами. Вы будете использовать несколько соглашений, таких как пространства имен и разделение проблем, чтобы помочь сохранить ваш код JavaScript от конфликта с другими библиотеками, которые вы можете включить.

Примечание. Вы будете создавать одностраничные приложения с использованием собственного JavaScript. В частности, вы будете использовать версию ES2017, которая работает со многими современными браузерами, но может быть проблематичной, если ваша цель - поддерживать более старые версии браузеров.

Modules and Namespaces

Возможно, вы уже знаете о пространствах имен в Python и о том, почему они ценны. Короче говоря, пространства имен дают вам возможность сохранить имена в вашей программе уникальными, чтобы предотвратить конфликты. Например, если вы хотите использовать log () из обоих модулей math и cmath, то ваш код может выглядеть примерно так:

    >>> import math
    >>> import cmath
    >>> math.log(10)
    2.302585092994046
    >>> cmath.log(10)
    (2.302585092994046+0j)

Приведенный выше код Python импортирует оба модуля math и cmath, а затем вызывает log(10) из каждого модуля. Первый вызов возвращает действительное число, а второй-комплексное число, для которого cmath имеет функции. Каждый экземпляр log() уникален для своего собственного пространства имен (math или cmath), что означает, что вызовы log () не конфликтуют друг с другом.

Современный JavaScript имеет возможность импортировать модули и назначать им пространства имен. Это полезно, если вам нужно импортировать другие библиотеки JavaScript, где может возникнуть конфликт имен.

Если вы посмотрите на конец людей.JS файл, тогда вы увидите это:

// Создание компонентов MVC
const model = new Model();
const view = new View();
const controller = new Controller(model, view);

// Экспорт компонентов MVC по умолчанию
export default {
    model,
    view,
    controller
};

Приведенный выше код создает три компонента системы MVC, которые вы увидите далее в этой статье. Экспорт по умолчанию модуль представляет собой литерал объекта JavaScript. Вы импортируете этот модуль в нижней части списка людей.HTML-файл:


    // Даем  MVC пространство имен
    import * as MVC from "/static/js/people.js";

    // Создаем глобальную переменную, ссылающуюся на импорт
    window.mvc = MVC;

Вот как работает этот код:

  • Строка 50 использует type= "module", чтобы сообщить системе, что файл является модулем, а не просто файлом JavaScript.
  • Строка 52 импортирует объект по умолчанию из people.js и присваивает ему имя MVC. Это создает пространство имен, называемое MVC. Вы можете присвоить импортируемому объекту любое имя, которое не конфликтует с другими библиотеками JavaScript, которые вы, возможно, не контролируете.
  • Строка 55 создает глобальную переменную, что является удобным шагом. Это можно использовать для проверки объекта mvc с помощью отладчика JavaScript и просмотра модели, представления и контроллера.

Примечание: поскольку MVC является импортированным модулем, а не просто включенным файлом, JavaScript по умолчанию будет работать в строгом режиме, что имеет некоторые преимущества перед нестрогим режимом. Одна из самых больших проблем заключается в том, что вы не можете использовать неопределенные переменные.

Без включения строгого режима это совершенно законно:

var myName = "Hello";
myNane = "Hello World";
    

Видите ли вы эту ошибку? Первая строка создает переменную под названием myName и присваивает ей литеральную строку "Hello". Вторая строка выглядит так, как будто она изменяет содержимое переменной на "Hello World", но это не так!

Во второй строке, "Здравствуй, Мир" присваивается переменной myNane имя, которое написано с ошибкой с N. В нестрогом языке JavaScript, это создает два переменных:

  1. Правильная переменная myName
  2. Непреднамеренная опечатка версии myNane

Представьте себе, что эти две строки кода JavaScript были разделены многими другими. Это может создать ошибку во время выполнения, которую трудно найти! Когда вы используете строгий режим, вы устраняете ошибки, подобные этой, вызывая исключение, если ваш код попытается использовать необъявленную переменную.

Соглашение об именах

По большей части, код JavaScript, который вы используете здесь, находится в camel case. Это соглашение об именовании широко используется в сообществе JavaScript, поэтому примеры кода отражают это. Однако ваш код Python будет использовать случай змеи, который более обычен в сообществе Python.

Эта разница в именовании может привести к путанице там, где ваш код JavaScript взаимодействует с кодом Python, и особенно там, где общие переменные входят в интерфейс REST API. Будьте уверены, чтобы сохранить эти различия в виду, как вы пишете свой код.

Разделение проблем

Код, который управляет SPA, может быть сложным. Вы можете использовать архитектурный шаблон Model–View–Controller (MVC), чтобы упростить процесс, создав разделение проблем. Спа-центры Home, People и Notes используют следующий шаблон MVC:

  • Модель предоставляет весь доступ к REST API сервера. Все, что представлено на дисплее, исходит от модели. Любые изменения данных проходят через модель и возвращаются в REST API.
  • Представление управляет всеми обработками отображения и обновлениями DOM. Представление - это единственная часть SPA, которая взаимодействует с DOM и заставляет браузер визуализировать и повторно визуализировать любые изменения на дисплее.
  • Контроллер обрабатывает все действия пользователя и любые введенные пользовательские данные, например события щелчка мыши. Поскольку контроллер реагирует на пользовательский ввод, он также взаимодействует с моделью и представлением, основанными на этом пользовательском вводе.

Вот визуальное представление концепции MVC, реализованной в коде SPA:

На приведенном выше рисунке контроллер имеет прочную связь как с моделью, так и с видом. Опять же, это связано с тем, что любое пользовательское взаимодействие, которое обрабатывает контроллер, может потребовать обращения к REST API для получения или обновления данных. Это может даже потребовать обновления дисплея.

Пунктирная линия, идущая от модели к контроллеру, указывает на слабое соединение. Поскольку вызовы REST API являются асинхронными, данные, которые модель предоставляет контроллеру, возвращаются позже.

Создание People SPA

Ваше демонстрационное приложение для мини-блога имеет страницы для дома, людей и заметок. Каждая из этих страниц является полноценным, автономным СПА. Все они используют одинаковый дизайн и структуру, поэтому, даже если вы сосредоточитесь здесь на приложении People, вы поймете, как их все создать.

People HTML

Веб-фреймворк Python Flask предоставляет движок шаблонов Jinja2, который вы будете использовать для People SPA. Есть части СПА, которые являются общими для всех трех страниц, поэтому каждая страница использует функцию наследования шаблона Jinja2 для совместного использования этих общих элементов.

Вы предоставите HTML-контент для People SPA в двух файлах: parent.html и люди.HTML-файл. Вы можете получить код для этих файлов по ссылке ниже:
Вот как будет выглядеть ваш parent.html:




    
    {% block head %}
    {% block title %}{% endblock %} Page
    {% endblock %}




{% block body %}
{% endblock %}


{% block javascript %}
{% endblock %}


parent.html имеет несколько основных элементов:

  • Строка 1 устанавливает тип документа как . Все новые HTML-страницы начинаются с этой декларации. Современные браузеры знают, что это означает использование стандарта HTML 5, в то время как старые браузеры прибегают к последнему стандарту, который они могут поддерживать.
  • Строка 4 сообщает браузеру использовать кодировку UTF-8.
  • Строки с 10 по 19 определяют панель навигации.
  • Строки 21 и 22 - это маркеры блоков Jinja2, которые будут заменены содержимым в people.html.
  • Строки 25 и 26 являются блочными маркерами Jinja2, которые выполняют роль заполнителя для кода JavaScript.

Файл people.html наследует код parent.html.

{% extends "parent.html" %}
{% block title %}People{% endblock %}
{% block head %}
{% endblock %}
{% block page_name %}Person Create/Update/Delete Page{% endblock %}

{% block body %}
    
Person ID:


People
Creation/Update Timestamp Person
{% endblock %}

people.html имеет только два основных различия:

  • Строка 1 сообщает Jinja2, что этот шаблон наследуется от шаблона parent.html.
  • Строки с 7 по 45 создают тело страницы. Это включает в себя раздел редактирования и пустую таблицу для представления списка людей. Это содержимое вставлено в раздел {% block body%} {% endblock%} файла parent.html.

HTML-страница, сгенерированная родителем.html и люди.html не содержит никакой информации о стиле. Вместо этого страница отображается в стиле по умолчанию любого браузера, который вы используете для ее просмотра. Вот как выглядит ваше приложение при отображении в браузере Chrome:

Это не очень похоже на одностраничное приложение! Давайте посмотрим, что вы можете с этим сделать.

People CSS

Чтобы стилизовать People SPA, сначала необходимо добавить таблицу стилей normalize.css. Это гарантирует, что все браузеры будут последовательно отображать элементы в соответствии со стандартами HTML 5. Специальный CSS для People SPA поставляется с двумя таблицами стилей:

  1. родитель.css, который вы вытаскиваете вместе с родителем.HTML
  2. люди.css, который вы тянете с людьми.HTML

Вы можете получить код для этих таблиц стилей по ссылке ниже:
Вы добавите normalize.css и parent.css в раздел ... файла parent.html:


    
    {% block head %}
    {% block title %}{% endblock %} Page
    
    
    {% endblock %}

Вот что делают эти новые линии:

  • Строка 5 получает normalize.css из сети доставки контента (CDN), поэтому вам не нужно загружать ее самостоятельно.
  • Строка 6 получает parent.css из статической папки вашего приложения.

По большей части, родитель.css задает стили для элементов навигации и ошибок. Он также изменяет шрифт по умолчанию на шрифт Roboto от Google, используя эти строки:

@import url(http://fonts.googleapis.com/css?family=Roboto:400,300,500,700);

body, .ui-btn {
    font-family: Roboto;
}

Вы извлекаете шрифт Roboto из CDN Google. Затем вы применяете этот шрифт ко всем элементам в теле SPA, которые также имеют класс .ui-btn.

Аналогично, people.css содержит информацию о стилях, специфичную для элементов HTML, которые создают People SPA. Вы добавляете people.css в файл people.html внутри раздела Jinja2 {% block head%}:

{% block head %}
    {{ super() }}
    
{% endblock %}

Файл содержит несколько новых строк:

  • В строке 2 есть вызов {{super ()}}. Это говорит Jinja2 включить все, что существует в разделе {% block head%} файла parent.html.
  • Строка 3 извлекает файл people.css из статической папки вашего приложения.

После того, как вы включите таблицы стилей, ваш People SPA будет выглядеть примерно так:

People SPA выглядит лучше, но все еще не завершен. Где в таблице находятся данные о людях? Все кнопки в разделе редактора включены, так почему они ничего не делают? Вы исправите эти проблемы в следующем разделе с помощью некоторого JavaScript.

People JavaScript

Вы будете извлекать файлы JavaScript в People SPA, как вы это делали с файлами CSS. Вы добавите следующий фрагмент кода в конец файла people.html:

{% block javascript %}
{{ super() }}

    // Give the imported MVC components a namespace
    
    import * as MVC from "/static/js/people.js";

    // Create an intentional global variable referencing the import
    window.mvc = MVC;

{% endblock %}

Обратите внимание на объявление type = "module" на открывающем теге в строке 50. Это говорит системе, что скрипт является модулем JavaScript. Синтаксис импорта ES6 будет использоваться для перетаскивания экспортированных частей кода в контекст браузера.

People MVC

Все страницы SPA используют вариацию шаблона MVC. Вот пример реализации в JavaScript:

// Создайте компоненты MVC
const model = new Model();
const view = new View();
const controller = new Controller(model, view);

// Экспорт компонентов MVC по умолчанию
export default {
    model,
    view,
    controller
};

Этот код пока ничего не делает, но вы можете использовать его, чтобы увидеть следующие элементы структуры и реализации MVC:

  • Строка 2 создает экземпляр класса Model и назначает его модели.
  • Строка 3 создает экземпляр класса View и назначает его для просмотра.
  • Строка 4 создает экземпляр класса Controller и назначает его контроллеру. Обратите внимание, что вы передаете и модель, и представление конструктору. Так контроллер получает ссылку на модель и просматривает переменные экземпляра.
  • Строки с 7 по 11 экспортируют литеральный объект JavaScript как экспорт по умолчанию.

Поскольку вы загружаете people.js внизу people.html, JavaScript выполняется после того, как ваш браузер создает элементы SPA DOM. Это означает, что JavaScript может безопасно получить доступ к элементам на странице и начать взаимодействовать с DOM.

Опять же, приведенный выше код пока ничего не делает. Чтобы это работало, вам нужно определить свою модель, представление и контроллер.

Модель People

Модель отвечает за связь с REST API, предоставляемым сервером Flask. Любые данные, которые поступают из базы данных, и любые данные, которые SPA изменяет или создает, должны проходить через Модель. Все взаимодействие с REST API осуществляется с помощью HTTP-вызовов AJAX, инициируемых JavaScript.

Современный JavaScript предоставляет fetch (), который вы можете использовать для выполнения вызовов AJAX. Код для вашего класса Model реализует один метод AJAX для чтения конечной точки URL REST API / api / people и получения всех людей в базе данных:

class Model {
    async read() {
        let options = {
            method: "GET",
            cache: "no-cache",
            headers: {
                "Content-Type": "application/json"
                "accepts": "application/json"
            }
        };
        // Вызовите конечную точку REST и дождитесь данных
        let response = await fetch(`/api/people`, options);
        let data = await response.json();
        return data;
    }
}

Вот как работает этот код:

  • Строка 1 определяет класс Model. Это то, что будет экспортировано позже как часть объекта mvc.
  • В строке 2 начинается определение асинхронного метода с именем read (). Ключевое слово async перед read () сообщает JavaScript, что этот метод выполняет асинхронную работу.
  • Строки с 3 по 9 создают объект параметров с параметрами для вызова HTTP, такими как метод и то, что вызов ожидает для данных.
  • Строка 12 использует fetch () для выполнения асинхронного HTTP-вызова к конечной точке REST URL / api / people, предоставленной сервером. Ключевое слово await перед fetch () указывает JavaScript асинхронно ждать завершения вызова. Когда это закончено, результаты присваиваются ответу.
  • Строка 13 асинхронно преобразует строку JSON в ответе в объект JavaScript и назначает ее данным.
  • Строка 14 возвращает данные вызывающей стороне.

По сути, этот код сообщает JavaScript для выполнения HTTP-запроса GET к / api / people, и что вызывающая сторона ожидает данные типа Application / json и json Content-Type. Напомним, что HTTP-вызов GET приравнивается к Read в CRUD-ориентированной системе.

На основании конфигурации Connexion, определенной в swagger.yml, этот HTTP-вызов вызовет def read_all (). Эта функция определена в people.py и запрашивает базу данных SQLite, чтобы создать список людей, чтобы вернуться к вызывающей стороне.
В браузере JavaScript выполняется в одном потоке и предназначен для реагирования на действия пользователя. Из-за этого плохая идея блокировать выполнение JavaScript, которое ожидает выполнения чего-либо, например HTTP-запроса к серверу.

Что, если запрос поступил через очень медленную сеть, или сам сервер не работал и никогда не отвечал? Если JavaScript блокирует и ожидает завершения HTTP-запроса в таких условиях, то он может завершиться в считанные секунды, минуты или, возможно, вовсе не завершиться. Пока JavaScript заблокирован, ничто другое в браузере не будет реагировать на действия пользователя!

Чтобы предотвратить это поведение блокировки, HTTP-запросы выполняются асинхронно. Это означает, что HTTP-запрос возвращается в цикл событий непосредственно перед его завершением. Цикл событий существует в любом приложении JavaScript, которое выполняется в браузере. Цикл непрерывно ожидает завершения события, поэтому он может запустить код, связанный с этим событием.

Когда вы помещаете ключевое слово await перед fetch (), вы указываете в цикле событий, куда возвращаться после завершения HTTP-запроса. На этом этапе запрос завершен, и любые данные, возвращаемые вызовом, назначаются для ответа. Затем контроллер вызывает this.model.read () для получения данных, возвращаемых методом. Это создает слабую связь с контроллером, так как модель ничего не знает о том, как она была вызвана, что она вернула тому вызывающему.

Просмотр People

this.view отвечает за взаимодействие с DOM, которое отображается на дисплее. Он может изменять, добавлять и удалять элементы из DOM, которые затем повторно отображаются на дисплее. Контроллер выполняет вызовы методов представления для обновления отображения. View - это еще один класс JavaScript с методами, которые может вызывать контроллер.

Ниже приведена несколько упрощенная версия класса View для People SPA:

class View {
    constructor() {
        this.table = document.querySelector(".people table");
        this.person_id = document.getElementById("person_id");
        this.fname = document.getElementById("fname");
        this.lname = document.getElementById("lname");
    }

    reset() {
        this.person_id.textContent = "";
        this.lname.value = "";
        this.fname.value = "";
        this.fname.focus();
    }

    buildTable(people) {
        let tbody,
            html = "";

        // Перебирай людей и создавай таблицу
        people.forEach((person) => {
            html += `
            
                ${person.timestamp}
                ${person.fname} ${person.lname}
            `;
        });
        // В настоящее время в таблице есть кто-то еще?
        if (this.table.tBodies.length !== 0) {
            this.table.removeChild(this.table.getElementsByTagName("tbody")[0]);
        }
        // Обновите tbody нашим новым контентом
        tbody = this.table.createTBody();
        tbody.innerHTML = html;
    }
}

Вот как работает этот код:

  • Строка 1 начинается с определения класса.
  • Строки 2-7 определяют конструктор класса, так же как определение def __init __ (self): определение в классе Python. Конструктор получает элементы из DOM и создает псевдонимы для использования в других частях класса. Это перед этими именами переменных очень похож на себя. в Python. Он обозначает текущий экземпляр класса при использовании.
  • Строки с 9 по 14 определяют reset (), который вы будете использовать, чтобы вернуть страницу в состояние по умолчанию.
  • Строки с 16 по 36 определяют buildTable (), которая строит таблицу людей на основе данных о людях, переданных ей.

Переменные псевдонима создаются для кэширования объектов DOM, возвращаемых вызовами document.getElementByID () и document.querySelector (), которые являются относительно дорогими операциями JavaScript. Это позволяет быстро использовать переменные в других методах класса.

Давайте подробнее рассмотрим build_table (), который является вторым методом в классе View:

buildTable(people) {
    let tbody,
        html = "";

    // Перебирай людей и создавай таблицу
    people.forEach((person) => {
        html += `
        
            ${person.timestamp}
            ${person.fname} ${person.lname}
        `;
    });
    // В настоящее время в таблице есть кто-то еще?
    if (this.table.tBodies.length !== 0) {
        this.table.removeChild(this.table.getElementsByTagName("tbody")[0]);
    }
    // Обновите tbody нашим новым контентом
    tbody = this.table.createTBody();
    tbody.innerHTML = html;
}

Вот как работает эта функция:

  • Строка 16 создает метод и передает переменную people в качестве параметра.
  • Строки с 21 по 27 перебирают данные о людях, используя функции стрелок JavaScript, чтобы создать функцию, которая строит строки таблицы в переменной html.
  • Строки с 29 по 31 удаляют все элементы tbody в таблице, если они существуют.
  • Строка 33 создает новый элемент tbody в таблице.
  • Строка 34 вставляет HTML-строку, ранее созданную в элемент tbody, как HTML.

Эта функция динамически создает таблицу в People SPA из данных, переданных ей, которые являются списком людей, пришедших из вызова API / api / people / REST. Эти данные используются вместе со строками шаблона JavaScript для генерации строк таблицы для вставки в таблицу.

Контроллер People

Контроллер является центральным расчетным центром реализации MVC, поскольку он координирует деятельность как модели, так и представления. Таким образом, код для его определения немного сложнее. Вот упрощенная версия:

class Controller {
    constructor(model, view) {
        this.model = model;
        this.view = view;

        this.initialize();
    }
    async initialize() {
        await this.initializeTable();
    }
    async initializeTable() {
        try {
            let urlPersonId = parseInt(document.getElementById("url_person_id").value),
                people = await this.model.read();

            this.view.buildTable(people);

            // Мы сориентировались здесь с выбранным человеком?
            if (urlPersonId) {
                let person = await this.model.readOne(urlPersonId);
                this.view.updateEditor(person);
                this.view.setButtonState(this.view.EXISTING_NOTE);

            // В противном случае, нет, так что оставьте редактор пустым
            } else {
                this.view.reset();
                this.view.setButtonState(this.view.NEW_NOTE);
            }
            this.initializeTableEvents();
        } catch (err) {
            this.view.errorMessage(err);
        }
    }
    initializeCreateEvent() {
        document.getElementById("create").addEventListener("click", async (evt) => {
            let fname = document.getElementById("fname").value,
                lname = document.getElementById("lname").value;

            evt.preventDefault();
            try {
                await this.model.create({
                    fname: fname,
                    lname: lname
                });
                await this.initializeTable();
            } catch(err) {
                this.view.errorMessage(err);
            }
        });
    }
}

Вот как это работает:

  • Строка 1 начинает определение класса Controller.
  • Строки 2-7 определяют конструктор класса и создают переменные экземпляра this.model и this.view с соответствующими параметрами. Он также вызывает this.initialize (), чтобы настроить обработку событий и создать начальную таблицу людей.
  • Строки с 8 по 10 определяют initialize () и помечают его как асинхронный метод. Он вызывает this.initializeTable () асинхронно и ожидает его завершения. Эта упрощенная версия включает только один вызов, но полная версия кода содержит другие методы инициализации, используемые для остальной части настройки обработки событий.
  • Строка 11 определяет initializeTable () как асинхронный метод. Это необходимо, потому что он вызывает model.read (), который также является асинхронным.
  • В строке 13 объявляется и инициализируется переменная urlPersonId со значением скрытого HTML-ввода url_person_id.
  • Строка 14 вызывает this.model.read () и асинхронно ожидает его возвращения с данными о людях.
  • Строка 16 вызывает this.view.buildTable (people) для заполнения таблицы HTML данными о людях.
  • Строки с 19 по 28 определяют, как обновить часть страницы редактора.
  • Строка 29 вызывает this.initializeTableEvents (), чтобы установить обработку событий для таблицы HTML.
  • Строка 31 вызывает this.view.errorMessage (err) для отображения ошибок в случае их возникновения.
  • Строки с 34 по 49 устанавливают обработчик события нажатия на кнопку создания. Это вызывает this.model.create (...), чтобы создать нового человека с помощью REST API, и обновляет таблицу HTML новыми данными.

Основная часть кода контроллера выглядит следующим образом, устанавливая обработчики событий для всех ожидаемых событий на странице People SPA. Контроллер продолжает создавать функции в этих обработчиках событий для организации вызовов this.model и this.view, чтобы они выполняли правильные действия при возникновении этих событий.

Когда ваш код будет завершен, ваша страница People SPA будет выглядеть следующим образом:

Содержание, стиль и функциональность - все готово!

Вывод

Вы освоили много нового и должны гордиться тем, что узнали! Может быть сложно переключаться между Python и JavaScript для создания законченного одностраничного приложения.

Если вы разделяете ваш контент (HTML), презентацию (CSS) и взаимодействие (JavaScript), то вы можете существенно снизить сложность. Вы также можете сделать кодирование JavaScript более управляемым, используя шаблон MVC, чтобы еще больше снизить сложность взаимодействия с пользователем.

Вы видели, как использование этих инструментов и идей может помочь вам создать достаточно сложные одностраничные приложения. Теперь вы лучше подготовлены к тому, чтобы принимать решения о том, создавать ли приложение таким образом или погрузиться в более масштабную среду!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *