# Урок 6. Главный экран

## Фильтры <a href="#filters" id="filters"></a>

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

В конце раздела главная форма будет иметь вид:



Первым делом скорректируем запрос AppOrderSelectSqlQuery для получение списка заказов. Ранее мы делали заглушку, чтобы запрос возвращал все заказы за текущую неделю. Теперь удалим ограничения по дате и будем возвращать все не удаленные заказы. В рамках учебного проекта такое допущение оправдано небольшим количеством заказов. В реальном приложении нужно использовать более гибкую систему фильтрации.&#x20;

{% hint style="info" %}
Платформа Workflow MobileForms поддерживает фильтры типов:

* _Глобальные фильтры_ - значение фильтра передается в параметре в PrimaryGetDataConnection и отправляется на сервер, где происходит первичная выборка данных.\
  Плюс глобальных фильтров в том, что с сервера загружаются только необходимые данные.
*   _Локальные фильтры_ - работают на форме с полученными данными без повторного обращения к серверу. Для этого используется вложенный тэг `<Filter>` у PrimaryGetDataConnection, SecondaryGetDataConnection и ConvertDataConnection.

    Плюс локальных фильтров в том, что сокращается количество обращений к серверу.
{% endhint %}



В запросе AppOrderSelectSqlQuery получаем дату заказа в виде строки, для фильтра понадобится исходное значение даты, для этого внесем в запрос изменения:

* Переименуем поле OrderDate в OrderDateString;
* Добавим в запрос поле OrderDate, в котором будет храниться исходное значение даты заказа.

Внесите соответствующие правки в xml-файл формы, а именно в OrderPrimaryGetDataConnection и OrderCollectionView.

Для фильтрации заказов создадим объект [Calendar](https://wfsys.gitbook.io/workflow-mobile-forms-syntax/workflow_mobile_forms/objects/calendar), добавим в ContentPanel его описание:

{% code title="TemplateMainForm.xml" %}
```xml
<MyObject Name="Calendar" Type="Calendar" Assembly="BaseControls">
  <Top>0</Top>
  <Left>0</Left>
  <Height>330</Height>
  <Width>
    <Object Name="ContentPanel">
      <Property Name="Width" />
    </Object>
  </Width>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="AppThemeDarkEqualCondition" />
        </When>
        <Then>Black</Then>
      </Case>
      <Case>White</Case>
    </Switch>
  </BackColor>
  <DaysTitleHeight>15</DaysTitleHeight>
  <DayViewSize>35</DayViewSize>
  <CalendarSectionShown>False</CalendarSectionShown>
  <TodayOutlineColor>SoftBlue</TodayOutlineColor>
  <EventIndicatorColor>SoftBlue</EventIndicatorColor>
  <SelectedDayBackgroundColor>SoftBlue</SelectedDayBackgroundColor>
  <EventList>
    <DataConnection SourceDataConnection="OrderPrimaryGetDataConnection">
      <Fields>
        <Field Name="OrderDate"/>
        <Field Name="OrderId" />
      </Fields>
    </DataConnection>
  </EventList>
</MyObject>
```
{% endcode %}

Объект Calendar имеет свойство CalendarSectionShown, которое отвечает за формат отображения объекта. Если свойство имеет значение False, то объект отображается в виде выбранной даты, иначе  в виде календаря. Режимы отображения можно переключать жестами. Если тапнуть по дате, то объект развернется в календарь. А если сделать свайп по календарю снизу вверх, то календарь свернется в режим отображения даты. По умолчанию для календаря задали режим отображения только даты, т.к. при запуске приложения пользователю важно видеть заказы на текущую дату.

В тэге EventList передаем таблицу из двух колонок: дата и идентификатор заказа. Эти данные будут использоваться для отметки дней в календаре, на которые есть события. Такие дни будут помечаться небольшой точкой под датой.

Для объекта OrderCollectionPanel скорректируем координаты Top и Height с учетом календаря.



Отлично!

&#x20;командой [AnimatedValueSetCommand](https://wfsys.gitbook.io/workflow-mobile-forms-syntax/workflow_mobile_forms/commands/animated_value_set_command), которая позволяет изменять свойства объектов через анимацию.



```xml
<Command Name="CalendarHeightAnimatedValueSetCommand" Type="AnimatedValueSetCommand" Assembly="Commands">
  <Object Name="Calendar">
    <Property Name="Height" />
  </Object>
  <StartValue>
    <Object Name="Calendar">
      <Property Name="Height" />
    </Object>
  </StartValue>
  <FinishValue>
    <Input />
  </FinishValue>
  <Length>300</Length>
  <Easing>Linear</Easing>
</Command>
```





Теперь необходимо создать SecondaryGetDataConnection, в котором будем фильтровать исходный список заказов по выбранной в календаре дате:

{% code title="TemplateMainForm.xml" %}
```xml
<DataConnection Name="OrderSecondaryGetDataConnection" Type="SecondaryGetDataConnection" Assembly="DataConnections">
  <SourceDataConnection Name="OrderPrimaryGetDataConnection" />
  <Filter>
    <Field NativeName="OrderDate" />
    <Value>
      <Object Name="Calendar">
        <Property Name="SelectedDate" />
      </Object>
    </Value>
    <DataType Type="DateDataType" />
  </Filter>
</DataConnection>
```
{% endcode %}

У объекта Calendar используем get-проперти SelectedDate, которое возвращает значение выбранной даты.

В качестве источника данных для OrderCollectionView укажем новый DataConnection.

Запустим приложение и проверим работу фильтра по дате:



## Автоматическое обновление данных <a href="#auto-update" id="auto-update"></a>

### UpdateInterval <a href="#update_interval" id="update_interval"></a>

PrimaryGetDataConnection имеет необязательный вложенный тэг `<UpdateInterval>`, который включает механизм автоматического обновления данных и задает интервал обновлений.

Перейдем в файл стартовой формы и в описание OrderPrimaryGetDataConnection добавим вложенный тэг:

```xml
<UpdateInterval Seconds="20" />
```

Таким образом, мы задаем интервал обновления в 20 секунд, и DataConnection уже самостоятельно следит за выполнением обновлений.

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

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

Уберем внесенные изменения.

### Timer

В WT-платформе реализован объект Timer, выполняющий последовательность команд с заданным интервалом. Этот объект можно использовать для обновления данных в таблице. Например, вместо подписки главной формы на событие, можно с помощью объекта Timer задать интервал выполнения команды OrderDataConnectionRefreshCommand.

Давайте посмотрим, как это можно реализовать.

Вернемся в файл главного экрана и создадим объект типа Timer:

{% code title="TemplateMainForm.xml" %}
```xml
<MyObject Name="Timer" Type="Timer" Assembly="SimpleControls">
  <ManualStart>True</ManualStart>
  <Interval>20000</Interval>
  <Commands>
    <Command Name="OrderDataConnectionRefreshCommand" />
  </Commands>
</MyObject>
```
{% endcode %}

В тэге `<Commands>` прописали вызов команды на обновление данных о заказах. А в качестве интервала для тестов указали 20 секунды.

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

Минус такого обновления данных тот же: клиентская часть будет всегда обращаться на сервер за данными, даже если данные не изменились.

Уберем внесенные изменения.

## Рассылка событий <a href="#dispatch-events" id="dispatch-events"></a>

Самым лучшим способом обновления данных является механизм подписки форм на событие, которое генерирует серверная часть при выполнении запроса на изменении данных в базе. Механизм состоит из двух частей: генерация события сервером и подписка формы на событие.

### Генерация события <a href="#event-generation" id="event-generation"></a>

Для тэга [`<SqlQuery>`](https://wfsys.gitbook.io/workflow-engine-syntax/workflow_engine/sql_queries/sql_query) существует вложенный тэг [`<Events>`](https://wfsys.gitbook.io/workflow-engine-syntax/workflow_engine/sql_queries/sql_query#events), содержащий список событий, которые будут переданы на все клиентские приложения после выполнения запроса.

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

Каждое событие, описываемое тэгом `<Event>`, содержит набор параметров, передаваемых на форму, подписанную на это событие.

В серверном xml-файле есть запрос **OrderUpdateSqlQuery**, который генерирует событие **ChangedNumberOfOrders**:

{% code title="Template.xml" %}
```xml
<SqlQuery Name="OrderUpdateSqlQuery">
  <Events>
    <Event Name="ChangedNumberOfOrders">
      <Parameters>
        <Parameter Name="NumberOfOrders">
          SELECT
            count(*)
          FROM
            template.order O
          WHERE
            O.added AND
            NOT O.deleted;
        </Parameter>
      </Parameters>
    </Event>
  </Events>
  <Text>
    UPDATE template.order
    SET
      order_number = {OrderNumber},
      order_date = {OrderDate},
      client_id = {ClientId},
      description = {Description},
      added = true
    WHERE
      order_id = {OrderId};
  </Text>
</SqlQuery>
```
{% endcode %}

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

### Подписание формы на событие <a href="#signing-form-for-event" id="signing-form-for-event"></a>

Чтобы подписать форму на какое-либо событие, необходимо создать объект типа Subscriber, в котором указать имя события или имена нескольких событий.

Давайте перейдем в файл главной формы (TemplateMainForm.xml) и подпишем ее на событие ChangedNumberOfOrders:

{% code title="TemplateMainForm.xml" %}
```xml
<MyObject Name="NumberOfOrdersSubscriber" Type="Subscriber" Assembly="SimpleControls">
  <Subscribes>
    <Subscribe>ChangedNumberOfOrders</Subscribe>
  </Subscribes>
</MyObject>
```
{% endcode %}

Теперь необходимо проверять, что событие сработало. Для этого воспользуемся get-проперти Subscribe, которое возвращает название события последнего принятого сообщения:

{% code title="TemplateMainForm.xml" %}
```xml
<Condition Name="NumberOfOrdersSubscriberChangedCondition" Type="ChangedCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="NumberOfOrdersSubscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
  </Items>
</Condition>
```
{% endcode %}

Так как у нас только одно событие, на которое подписана главная форма, то достаточно использовать условие ChangedCondition.

Если бы главная форма была подписана на несколько событий, то мы бы использовали EqualCondition, чтобы сопоставлять значение get-проперти с именами событий:

```xml
<Condition Name="ChangedNumberOfOrdersSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="Subscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
    <Item>ChangedNumberOfOrders</Item>
  </Items>
</Condition>

<Condition Name="SecondEventNameSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="Subscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
    <Item>SecondEventName</Item>
  </Items>
</Condition>
```

А еще можно использовать get-проперти Parameter, чтобы получать значение конкретного параметра и применять его в условии.

Создадим Execution, который по условию срабатывания события будет вызывать команду на обновление:

{% code title="TemplateMainForm.xml" %}
```xml
<Execution>
  <ConditionExpression>
    <Condition Name="NumberOfOrdersSubscriberChangedCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="OrderDataConnectionRefreshCommand" />
  </Commands>
</Execution>
```
{% endcode %}

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

## Итоги <a href="#results" id="results"></a>

На уроке мы рассмотрели&#x20;

## Ответы <a href="#answer" id="answer"></a>

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

{% file src="broken-reference" %}

{% hint style="warning" %}
Архив _не содержит_ xml-файлы форм десктопного приложения.
{% endhint %}
