Как я создаю своё первое мобильное приложение

Процесс шел с 24 октября по 19 ноября 2017 года, суть приложения — создание заметок, инструмент — Fuse, мой уровень знаний — базовое знание HTML/CSS

Меню

08.11.2017


9. Добавление нового стикера

Итак, текущая задача такова: в окне нового стикера вводить текст с клавиатуры, после нажатия «Save» сохранять этот текст в базу и выдавать поток с обновлённым потоком.

Поиск алгоритма в js-песочнице

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

var flow = ["Стикер", "Стикер 2", "Стикер 3"];

Операций с массивом можно проводить самые разные, но сейчас меня интересует добавление в конец, за что и отвечает команда push:

flow.push ("Новый стикер");

Полностью алгоритм будет выглядеть примерно так:

var flow = ["Стикер", "Стикер 2", "Стикер 3"];
alert(flow); // сначала видим изначальный поток
var new_sticker = prompt("Введите текст нового стикера"); // затем вводим текст нового стикера
flow.push (new_sticker); alert(flow); // после чего нажимаем Save и видим обновлённый поток

Можно попробовать у себя в браузере, нажимайте на Result:

Алгоритм ясен. Дальше надо понять, как это реализуется с помощью UX Markup.

Проба алгоритма в ux-песочнице

Сперва делаю разметку в документе:

<App>

<JavaScript>
    var flow = ["Мой первый стикер", "Второй", "Третий"]; 
    module.exports = {
        flow: flow,
    }
</JavaScript>

<StackPanel Alignment="Center" ItemSpacing="20">
   <TextInput PlaceholderText="Type text" PlaceholderColor="#ccc" />
    <Button Text="Clear" />
    <Button Text="Save" />
    <Each Items="{flow}">
      <Text FontSize="20" Value="{}" />
   </Each>
</StackPanel>

</App>

Получилась такая «болванка»:

Теперь добавляю функцию очистки текстового поля:

<App>

<JavaScript>
    var flow = ["Мой первый стикер", "Второй", "Третий"];
    var new_sticker_text = ""; // объявляю переменную текстового поля
    
    function clean_text() { // приписываю алгоритм очистки
      new_sticker_text.value = "";
    } 

    module.exports = {
        flow: flow,
// подключаю новые данные в приложение
   new_sticker_text: new_sticker_text,
        clean_text: clean_text
    }
</JavaScript>

<StackPanel Alignment="Center" ItemSpacing="20">
   <TextInput Value="{new_sticker_text}" PlaceholderText="Type text" PlaceholderColor="#ccc" /> <!-- связал значение поля с переменной -->
    <Button Text="Clear" Clicked="{clean_text}" /> <!-- после клика выполнять функцию очистки -->
    <Button Text="Save" />
    <Each Items="{flow}">
      <Text FontSize="20" Value="{}" />
   </Each>
</StackPanel>

</App>

Однако ничего не происходит:

Дело в том, что мы сейчас выводим данные (значение текстового поля и содержимое стикеров), не в «прямом эфире», т.е. мы просто подсоединили «провода» между значениями где и что отображать, и чтобы обновить эти данные — нужно перезагружать приложение, а это нам не подходит.

Я уже раньше говорил, что такая «привязка» значений в UX Markup называется data binding (связывание данных).

Но для отображения данных в реальном времени в UX Markup бытует другая, тоже очень важная тема — observable (наблюдаемый). Суть в том, что нужные нам объекты можно сделать «наблюдаемыми», т.е. их значения будут отслеживаться в реальном времени, и никакая перезагрузка приложения не потребуется, чтобы обновлять базу данных или какие-то значения.

Для этого в рамках js-кода надо интегрировать модуль, отвечающий за такие функции, и сделать нужные объекты «наблюдаемыми»:

var Observable = require("FuseJS/Observable"); //интегрируем модуль наблюдения 
var flow = Observable("Мой первый стикер", "Второй", "Третий"); //делаем поток наблюдаемым
var new_sticker_text = Observable(""); //делаем текстовое поле наблюдаемым

Теперь данные будут отображаться в реальном времени, и после клика на «Clear» текстовое поле будет не просто очищаться, как в прошлом примере, но и отображаться в новом состоянии:

Остаётся прописать функцию для сохранения данных с текстового поля в массив. В UX Markup js-команда push не работает, вместо этого используется add, не знаю почему, может это следствие использования Uno, который является диалектом C#, а там как раз с помощью add элемент добавляется в массив. Теперь код принимает такой вид:

<App>

<JavaScript>
    var Observable = require("FuseJS/Observable");
    var flow = Observable("Мой первый стикер", "Второй", "Третий");
    var new_sticker_text = Observable("");
    
    function clean_text() {
      new_sticker_text.value = "";
    }

   function add_sticker() {
   flow.add(new_sticker_text.value); //добавить в массив flow значение new_sticker_text
   clean_text(); //использую уже прописанную выше функцию
}

    module.exports = {
        flow: flow,
      new_sticker_text: new_sticker_text,
        clean_text: clean_text,
        add_sticker: add_sticker // делаю "видимым" для приложения
    }
</JavaScript>

<StackPanel Alignment="Center" ItemSpacing="20">
   <TextInput Value="{new_sticker_text}" PlaceholderText="Type text" PlaceholderColor="#ccc" />
    <Button Text="Clear" Clicked="{clean_text}" />
    <Button Text="Save" Clicked="{add_sticker}" />
    <Each Items="{flow}">
      <Text FontSize="20" Value="{}" />
   </Each>
</StackPanel>

</App>

И новый текст будет добавляться в массив и отображаться в реальном времени:

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

Интеграция решения в текущую версию приложения

Итак, код прошлой версии выглядит следующим образом:

<App>
<JavaScript>
    var storage = ["Мой первый стикер", "Второй", "Третий", "Четвертый"];
    var goto_newsticker = function() { router.goto("NewSticker"); };
    var goto_flow = function() { router.goto("Flow"); };
    module.exports = {
        sticker_text: storage,
        goto_newsticker: goto_newsticker,
        goto_flow: goto_flow
    }
</JavaScript>
<!-- Шаблоны (компоненты) -->
<Image ux:Class="icon" Width="30" Margin="10" Color="#C4C4C4" />
<Panel ux:Class="sticker" Width="220" Height="50" Margin="0,0,0,10" Padding="5,5,5,10" Color="#fff">  
    <string ux:Property="sticker_text" />
    <Text FontSize="16" Value="{ReadProperty sticker_text}" Color="#000" />
</Panel>
<Panel ux:Class="button_new" Alignment="BottomCenter" Color="#fff" Width="220" Height="78">
    <string ux:Property="button_name" />
    <Text ux:Name="link" Value="{ReadProperty button_name}" FontSize="24" Color="#157EFB" Alignment="Center" />
    <WhilePressed>
        <Change this.Color="#157EFB" Duration="0.1" DurationBack="0.3" />
        <Change link.Color="#fff" Duration="0.4" DurationBack="0.3" />
    </WhilePressed>
</Panel>
<!-- Палитра слоёв (экземпляры) -->
<Router ux:Name="router" />
    <Navigator DefaultPath="Flow">
        <Page Name="Flow">
        <Transition>
            <Change Flow.Opacity="0.3" Duration="0.7" />
        </Transition>
            <icon File="assets/flows.png" Alignment="TopLeft" />
            <icon File="assets/package.png" Alignment="TopRight" />
            <button_new button_name="New sticker" Clicked="{goto_newsticker}"/>
            <ScrollView>
                <StackPanel>
                    <Each Items="{sticker_text}">
                        <sticker sticker_text="{}"/>
                    </Each>
                </StackPanel>
            </ScrollView>
            <Rectangle Color="#EAEAEA" />
        </Page>
        <Page Name="NewSticker" Color="#fff">
        <Transition>
            <Scale Factor="0.7" Easing="ElasticIn" />
            <Move Y="1" RelativeTo="Size" Duration="0.4" Easing="ElasticOut" />
        </Transition>
            <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
            <Text Alignment="TopRight" Margin="10" FontSize="24" Clicked="{goto_flow}">Cancel</Text>
            <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB" Clicked="{goto_flow}">
                <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
            </Button>
        </Page>
    </Navigator>
</App>

С учетом новых знаний корректирую код и провожу следующие операции:

  • подключаю модуль «наблюдения» (строка 3);
  • делаю нужные объекты и переменные наблюдаемыми (строки 4-5);
  • чтобы было меньше путаницы в именах, переименовываю
    storage и sticker_text в flow (строки 4, 20, 54);
  • на экране создания стикера к «Save» прикручиваю новую функцию (строка 70), добавляю кнопку очистки (строка 69) и «наблюдаемое» текстовое поле (строка 68);
  • для лаконичности переписываю функцию функцию перехода на поток (7 и 16 строка).
<App>
<JavaScript>
    var Observable = require("FuseJS/Observable");
    var flow = Observable("Мой первый стикер", "Второй", "Третий");
    var new_sticker_text = Observable("");
    var goto_newsticker = function() { router.goto("NewSticker"); };
    function goto_flow() { router.goto("Flow"); };

    function clean_text() {
        new_sticker_text.value = "";
    }

    function add_sticker() {
        flow.add(new_sticker_text.value);
        clean_text();
        goto_flow();
    }

    module.exports = {
        flow: flow,
        goto_newsticker: goto_newsticker,
        goto_flow: goto_flow,
        new_sticker_text: new_sticker_text,
        add_sticker: add_sticker,
        clean_text: clean_text
    }
</JavaScript>
<!-- Шаблоны (компоненты) -->
<Image ux:Class="icon" Width="30" Margin="10" Color="#C4C4C4" />
<Panel ux:Class="sticker" Width="220" Height="50" Margin="0,0,0,10" Padding="5,5,5,10" Color="#fff">  
    <string ux:Property="sticker_text" />
    <Text FontSize="16" Value="{ReadProperty sticker_text}" Color="#000" />
</Panel>
<Panel ux:Class="button_new" Alignment="BottomCenter" Color="#fff" Width="220" Height="78">
    <string ux:Property="button_name" />
    <Text ux:Name="link" Value="{ReadProperty button_name}" FontSize="24" Color="#157EFB" Alignment="Center" />
    <WhilePressed>
        <Change this.Color="#157EFB" Duration="0.1" DurationBack="0.3" />
        <Change link.Color="#fff" Duration="0.4" DurationBack="0.3" />
    </WhilePressed>
</Panel>
<!-- Палитра слоёв (экземпляры) -->
<Router ux:Name="router" />
    <Navigator DefaultPath="Flow">
        <Page Name="Flow">
        <Transition>
            <Change Flow.Opacity="0.3" Duration="0.7" />
        </Transition>
            <icon File="assets/flows.png" Alignment="TopLeft" />
            <icon File="assets/package.png" Alignment="TopRight" />
            <button_new button_name="New sticker" Clicked="{goto_newsticker}"/>
            <ScrollView>
                <StackPanel>
                    <Each Items="{flow}">
                        <sticker sticker_text="{}"/>
                    </Each>
                </StackPanel>
            </ScrollView>
            <Rectangle Color="#EAEAEA" />
        </Page>
        <Page Name="NewSticker" Color="#fff">
        <Transition>
            <Scale Factor="0.7" Easing="ElasticIn" />
            <Move Y="1" RelativeTo="Size" Duration="0.4" Easing="ElasticOut" />
        </Transition>
            <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
            <Text Alignment="TopRight" Margin="10" FontSize="24" Clicked="{goto_flow}">Cancel</Text>
            <TextInput Alignment="Top" Margin="10,70" Value="{new_sticker_text}" PlaceholderText="Type text" PlaceholderColor="#ccc" />
            <Button Alignment="Left" Margin="10" Text="Clear" Clicked="{clean_text}" />
            <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB" Clicked="{add_sticker}">
                <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
            </Button>
        </Page>
    </Navigator>
</App>

Теперь приложение может создавать новые стикеры:

Скачать архив проекта

Что дальше

После создания стикера напрашиваются функции редактирования и удаления, что и будет предметом следующего шага.