# Модели и&nbsp;представления

Copyright&nbsp;©&nbsp;2016–2023 ООО&nbsp;«Открытая мобильная платформа».
Этот документ предоставляется в&nbsp;соответствии
с&nbsp;[Публичной лицензией Creative Commons с&nbsp;указанием авторства версии&nbsp;4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).

## Repeater — дупликатор однотипных элементов

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

Он работает с моделью данных итеративно, как цикл for.
В простейшем случае модель представляет собой просто число, обозначающее количество циклов.
Модель Repeater может быть любой из поддерживаемых моделей данных.
Так же, как и делегаты для других представлений, делегат Repeater может получить доступ
к своему индексу, а также данные модели.

Свойства:

*	count : int - Это свойство содержит количество элементов в модели.
	Количество элементов в модели, о которых сообщается в счетчике, может отличаться
	от количества созданных делегатов, если Repeater находится в процессе создания экземпляров
	делегатов или неправильно настроен.
*	delegate : Component - делегат;
*	model : any - модель.

Сигналы:

*	itemAdded(int index, Item item) - Этот сигнал испускается, когда элемент добавляется в Repeater.
	Параметр index содержит индекс, в который элемент был вставлен в Repeater, а параметр item
	содержит элемент, который был добавлен.
*	itemRemoved(int index, Item item) - сигнал испускается, когда элемент удаляется.

Метод:

*	Item itemAt(index) - Возвращает элемент, созданный с заданным индексом,
	или null, если по индексу нет элемента.
 
## Пример заполнения таблицы

На слайде изображён пример использования Repeater для заполнения таблицы числами от 0 до 79.
 
## Model-View-Delegate

Шаблон проектирования MVC (Model-View-Controller) предполагает разделение данных приложения, 
пользовательского интерфейса и управляющей логики на три отдельных компонента:
Модель, Представление и Контроллер – таким образом, что модификация каждого компонента
может осуществляться независимо.

MVC позволяет снизить сложность и упростить архитектуру программ с графическим интерфейсом
при помощи того самого разделения ответственности.

*	Модель отвечает за данные и обеспечивает доступ к ним.
*	Представление отвечает за отображение данных, полученных из модели.
*	Контролер отвечает за взаимодействие с пользователем.
	Может изменять данные в модели.

В идеале эти модули должны быть независимы друг от друга и позволять вносить изменения
или даже полностью заменить какой-либо модуль без переделки остальных.
Это бывает необходимо при разработке разных версий одного и того же приложения.
Например, есть десктопная и мобильная версии одного и того же приложения.
Их Модель (ядро программы) должна быть одной и той же, поскольку приложения работают
с одними и теми же данными, а вот Представление у приложений должно отличаться.

Схема взаимодействия модулей представлена на слайде.
Предполагается, что пользователь может отдавать команды Контроллеру посредством Вида,
Контроллер обновляет данные Модели, Модель оповещает Контроллер о том, что необходимо
обновить Вид и Контроллер обновляет его.

Одним из основных предназначений Qt является разработка графических интерфейсов пользователя,
поэтому в нем без MVC тоже не обошлось и Qt включает в себя свою вариацию – Model-View шаблон.
Основной идеей является разделение данных и отображения.
Здесь Модель отвечает за данные и доступ к ним. В Qt за отображение элементов и получение ввода
от пользователя нередко отвечают одни и те же элементы, поэтому вполне логична идея объединить
Вид и Контроллер.
Но для того, чтобы из-за такого объединения не терять гибкость, было введено понятие делегата.
Делегат позволяет определять, как данные будут отображаться и как пользователь может их изменять.
Вид же, по сути, теперь является контейнером для экземпляров делегата.

Представление в MVC отображает данные. Оно определяет, как будут выглядеть данные и,
в конечном итоге, что увидит пользователь.

У представления в QML есть три задачи:

*	создавать экземпляры делегата для каждого элемента в модели;
*	расположить эти элементы требуемым образом;
*	обеспечить навигацию по элементам.

## Стандартные представления

Стандартные представления:

*	ListView — вертикальный или горизонтальный список;
*	GridView — таблица;
*	PathView — траектория.

Общие свойства стандартных представлений:

*	model : model — модель с данными;
*	delegate : Component —  делегат, отвечающий за представление элемента модели;
*	currentItem : Item —  текущий элемент в представлении;
*	currentIndex : int — индекс текущего элемента;
*	count : int — общее количество элементов в модели.

## Типы моделей

Модель отвечает за доступ к данным.

Модель может быть реализована как в самом QML, так и на C++.
Выбор тут больше всего зависит от того, где находится источник данных.
Если в качестве источника данных используется код на C++, то там удобнее сделать и модель.
Если же данные поступают напрямую в QML, то лучше и модель реализовать на QML. 
В QML в качестве моделей используются компоненты ListModel, XmlListModel,
а также JavaScript-модели.
Причем модель, загружаемую из xml-файла, редактировать нельзя.

## ListModel

ListModel – QML-компонент для создания модели данных.
Он простой и функциональный, является контейнером для объектов ListElement.
ListElement не имеет собственных свойств и методов, служит для описания полей модели,
как показано на слайде.
Свойства text и color определяем мы сами.
ListModel имеет методы для добавления нового объекта к модели, для вставки нового объекта
в указанное место, для перемещения нескольких объектов из одного места в модели в другое,
для удаления несколько числа объектов из модели, а также для получения объекта по указанному
индексу и для удаления всех объектов из модели.

Описанный тип jsobject представляет собой стандартный JavaScript-объект.
Описывается он как ассоциативный массив с помощью фигурных скобок.

## Модель данных — список объектов

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

## Модель данных — список объектов [2]

У каждого элемента модели имеются поля name и surfaceColor.
Внутри делегата к этим полям мы обращаемся по именам напрямую.

## Пример с изменением модели

Пример добавления объектов в модель после нажатия на кнопку.

Здесь к свойствам модели обращаются через свойство model у ListView.

## ObjectModel: свойства

ObjectModel содержит визуальные элементы, которые будут использоваться в представлении.
Когда ObjectModel используется в представлении, представление не требует делегата,
так как ObjectModel уже содержит визуальный делегат (элементы).

*	count : int - Количество элементов в модели.
	Это свойство только для чтения.
*	ObjectModel.index : int - Это присоединенное свойство содержит индекс элемента этого делегата в модели.
	Он прикрепляется к каждому экземпляру делегата.

## ObjectModel: методы

*	append(object item) - Добавляет новый элемент в конец модели.
*	object get(int index) - Возвращает элемент по индексу в модели.
	Индекс должен быть элементом в списке.
*	insert(int index, object item) - Вставляет новый элемент в модель по индексу позиции.
	Индекс должен соответствовать существующему элементу в списке или следовать
	за концом списка (что эквивалентно добавлению).
*	move(int from, int to, int n = 1) - Перемещает n элементов из одной позиции в другую.
	Можно использовать диапазоны от и до; например, чтобы переместить первые 3 элемента в конец модели.
*	remove(int index, int n = 1) - Удаляет элементы по индексу из модели.
*	clear() - Удаляет все элементы из модели.

## ObjectModel: пример

Пример помещает три цветных прямоугольника в ListView.

## XmlListmodel

При работе с веб-ресурсами нередко применяется формат XML. 
Он используется в таких вещах, как RSS, различных подкастах.
Значит, у нас появляется задача получить этот файл и его распарсить.

Перебирать дерево элементов и наполнять модель вручную неудобно, так что нужна возможность
выбрать элементы из XML-файла автоматически и представить их в виде модели. 
Эти задачи и реализует XmlListModel.

XmlListModel – QML-компонент для определения модели данных.
Компонент позволяет создавать только Модель для чтений.
Запись в нее не будет поддерживаться.

Свойство source необходимо для указания пути к XML-файлу с данными.
Это может быть путь к локальному файлу или же путь к файлу на каком-нибудь веб-ресурсе.

Свойство query позволяет указать XPath запрос на получение элементов модели из XML дерева. 

Данные элементы имеют свойства/атрибуты (поля модели), для извлечения которых из XML
используется компонент XmlRole.
Объект XmlRole определяет поля Модели. 

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

А также свойство query, в котором указывается запрос на извлечение значения данного
атрибута из XML.

## Пример модели xml

Пример xml-файла и кода, который считывает и обрабатывает данную модель.

В качестве примера создадим собственный XML файл со каталогом книг.

Наш XML будет иметь структуру, представленную на слайде.
Корневой элемент <catalog> и внутри него несколько элементов <book>.

У каждого элемента <book> есть свои дочерние элементы: <title>, <year> и <author>.

Итак мы имеем каталог книг, где у каждой книги определено название, год издания и автор.

Объект XmlListModel, созданный для парсинга данного файла, также представлен на слайде.

В качестве свойства source указывается имя файла при условии, что файл исходного кода,
где используется XmlListModel и XML-файл лежат в одной директории.

В качестве свойства query указывается XPath запрос к элементу <book>.
Таким образом модель получит доступ к любому объекту <book> внутри XML.

Далее необходимо получить доступ к элементам-атрибутам каждой книги.
Для этого используются объекты XmlRole.
Свойства name объектов XmlRole определяют имена свойств модели, по которым мы будем
к ним обращаться внутри представления.

А свойства query задают запрос к каждому атрибуту внутри элемента <book>.
Например, чтобы обратиться к элементу <title> внутри <book> необходимо описать запрос
“title/string()”, где string означает, что данное поле текстовое.
В случае с атрибутом year, после символа / указывается number(), что означает,
что данный атрибут числовой.

## Пример модели xml [2]

Продолжение примера. Отображение xml-модели на странице приложения.

Код на слайде демонстрирует простейший пример использования XmlListModel
на странице со списком записей. 
Здесь наглядно видно, что определенные нами, с помощью XmlRole, “имена” свойств модели
(title, year и author) используются в делегате компонента SilicaListView. 
Также стоит отметить, что XmlListModel и XmlRole вынесены в отдельный модуль
QtQuick.XmlListModel 2.0 и его нужно дополнительно импортировать.

## Javascript-модели. Список строк

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

Рассмотрим в качестве модели JavaScript-массив.

Для каждого элемента массива будет создан делегат и данные самого элемент массива
будут доступны в делегате через свойство modelData. 

Если в массиве находятся объекты, то modelData тоже будет объектом и будет содержать
все свойства исходного объекта. 
 
## Javascript-модели. Массивы

В примере на слайде JavaScript-массив dataModel используется в качестве модели
для SilicaListView.
Внутри массива объявлены два JavaScript-объекта.
Один имеет свойство color, второй имеет два свойства color и text.
Доступ к свойствам осуществляется в делегате с помощью объекта modelData.
У одного из объектов модели не определено свойство text, в таком случае вместо него
отображается строка “empty text”.

Все Javascript-модели являются пассивными.
При изменении элементов и их добавлении/удалении представление не будет знать, что они поменялись.
Так происходит потому, что у свойств JavaScript-объектов нет сигналов, которые вызываются
при изменении свойства, в отличие от QML-объектов.

Обратите внимание, что к полям модели мы обращаемся через modelData.
 
## Javascript-модели. Целые числа

В качестве модели также можно использовать целое число.
Это число является количеством элементов модели.
Так можно напрямую передать свойству model целое число, как это сделано в примере на слайде.

В делегате будет доступно свойство modelData, которое содержит число.
Индекс будет доступен через model.index.
Никакой другой информации такая модель содержать не может.
