Создание пользовательского интерфейса для модификации данных, часть 1

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

На первом этапе мы просто создадим страницу, которая позволит нам отображать данные и переходить между записями:


Настройка файла

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


Создадим новую функцию person и добавим ее в скрипт allowed. Также запустим с ее помощью сервер. Остальное остается без изменений. Назовем новый файл family-edit.l.

### Modify / add following lines:

(allowed ("css/")
   "@lib.css" "!treeReport" "!person")
...

(de person ())
...

(de go ()
   (rollback)
   (server 8080 "!person") )
Вход в полноэкранный режим Выход из полноэкранного режима

Функция person должна быть функцией, в которой мы можем отобразить все необходимые данные о записи (т.е. человеке) одним взглядом. Мы хотим отображать данные в виде поля формы и иметь возможность переключаться между режимом чтения и редактирования с помощью кнопки «Редактировать».


Первое, что нам нужно, это создать стандартную HTML-функцию, включающую запуск сессии. На этой странице мы не будем использовать CSS-классы bootstrap, потому что мы будем использовать множество предопределенных функций PicoLisp gui, которые не очень хорошо работают с Bootstrap.

(de person () 
   (app)
   (action
      (html 0 "Person" "@lib.css" NIL
         (form NIL
]
Вход в полноэкранный режим Выход из полноэкранного режима

Скобка ] закрывает все открытые круглые скобки. Это может быть полезно при разработке.

Как и в предыдущих примерах, мы можем запустить ее с помощью $ pil family-edit.l -family~main -go + и когда мы направим браузер на http://localhost:8080, мы должны увидеть пустую вкладку с заголовком «Person».


Получение элемента базы данных

Как форма «узнает», какой элемент базы данных мы хотим отобразить? Это контролируется вызовом функции <id> где-то внутри формы. <id> связывает вывод с глобальной переменной *ID. Поэтому нам нужно сделать две вещи:

  1. Добавление <id> в нашу форму,
  2. добавление параметра *ID к URL.

Например, мы можем вызвать <id> в заголовке нашей формы. ( <id> (: nm )) установит *ID на текущий объект и отобразит его имя.

(form NIL
   (<h2> NIL (<id> (: nm)))
Вход в полноэкранный режим Выход из полноэкранного режима

Если теперь мы направим браузер на http://localhost:8080/?*ID=-A1, то увидим следующее:


Префиксный класс +E/R.

Теперь, когда мы указали текущий объект, мы можем отобразить его данные в форме. Для доступа к объекту базы данных мы будем использовать префиксные классы +E/R и +Obj.

Читайте здесь введение в графический интерфейс формы PicoLisp и здесь подробнее о префиксных классах.

Точно так же, как мы можем добавлять стили с помощью +Styles и привязывать переменные к элементу GUI с помощью +Val, мы можем подключить сущность базы данных с помощью класса +E/R. Синтаксис довольно прост:

(gui '(+E/R +TextField) '(nm : home obj) 40 "Name")
Войти в полноэкранный режим Выйти из полноэкранного режима

Это создает текстовое поле, которое связано со свойством nm («имя») объекта home obj. Объект home obj — это объект, принадлежащий текущей форме, то есть запись, указанная через глобальную переменную *ID.

Аналогичным образом мы можем получить дату рождения и смерти:

"born" (gui '(+E/R +DateField) '(dat : home obj) 10)
"died" (gui '(+E/R +DateField) '(fin : home obj) 10)
Войти в полноэкранный режим Выйти из полноэкранного режима


Элемент формы +ClassField

Элемент формы +ClassField можно использовать для отображения и изменения класса объекта. Например, в данном примере отображается выпадающее меню, которое показывает в качестве текущего значения либо «Male», либо «Female»:

(gui '(+ClassField) '(: home obj) '(("Male" +Man) ("Female" +Woman))) )
Войти в полноэкранный режим Выйти из полноэкранного режима


Префиксный класс +Obj

Префиксный класс +Obj используется для хранения определенного объекта. Таким образом, объект DB становится элементом GUI первого класса, так же как и примитивы string, number и т.д. Кроме того, +Obj предоставляет предложения из базы данных, используя dbHint.

Что это значит? Возьмем для примера поле «Партнер человека». Партнер человека — это тоже объект, связанный свойством mate. Поскольку мы используем +Obj, мы можем получить доступ к имени записи следующим образом:

(gui '(+E/R +Obj +TextField) '(mate : home obj) '(nm +Person) 30) )
Войти в полноэкранный режим Выйти из полноэкранного режима


+Obj также предлагает две приятные дополнительные функции:

  1. Предложения, если мы хотим изменить имя партнера (мы увидим это позже в режиме редактирования!),
  2. Карандаш, который выполняет предопределенную функцию, если мы нажмем на него. Если функция не указана, используется метод url>.

Допустим, мы хотим видеть обзор записей партнера, когда мы нажимаем на карандаш. Другими словами, мы хотим изменить *ID в URL на запись партнера.

Давайте определим метод url>, который делает именно это в нашей модели класса +Person:

### DB ###
(class +Person +Entity)
(rel nm (+Need +Sn +Idx +String))      # Name
...
(rel txt (+String))                    # Info

(dm url> (Tab)
   (list "!person" '*ID This) )
Вход в полноэкранный режим Выйти из полноэкранного режима

url> принимает аргумент (Tab), указывающий, какая вкладка должна быть открыта (что, очевидно, интересно только в приложении с несколькими вкладками — не так много в нашем одностраничном приложении).

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


Создание таблицы с помощью +Chart и <table>

Как уже было показано в примере приложения To-Do App, мы можем использовать +Chart в сочетании с <table> для отображения данных в сетке вместе с некоторыми предопределенными кнопками действий.

Сначала мы определяем данные, используемые в диаграмме, которые являются свойством kids текущего объекта. +Chart ожидает числовой аргумент в виде количества столбцов. Кроме того, мы можем вызвать две функции: Первая вызывается, когда график заполняется данными, вторая — когда данные считываются.

(gui '(+E/R +Chart) '(kids : home obj) 7
   '((This) (list NIL This (: dat) (: pa) (: ma)))
   cadr )
Вход в полноэкранный режим Выход из полноэкранного режима

Что это значит?

  • '((This) (list NIL This (: dat) (: pa) (: ma))) создает список списков из данных (kids : home obj, то есть ровно по одному значению на столбец и строку.

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


После того как мы определили входное значение, мы можем определить таблицу. Функция <table> принимает аргументы «attributes», «title», «head» и «program». Давайте установим атрибуты и заголовок в NIL и определим head как:

(<table> NIL NIL
   '(NIL (NIL "Children") (NIL "born") (NIL "Father") (NIL "Mother"))
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем создадим поля ввода, как указано выше:

(do 6
   (<row> NIL 
      (choPerson 1)
      (gui 2 '(+Obj +TextField) '(nm +Person) 20)
      (gui 3 '(+Lock +DateField) 10)
      (gui 4 '(+ObjView +TextField) '(: nm) 20)
      (gui 5 '(+ObjView +TextField) '(: nm) 20)
      (gui 6 '(+DelRowButton))
      (gui 7 '(+BubbleButton)) ) )
   (<row> NIL NIL (scroll 6 T)) )
Войти в полноэкранный режим Выход из полноэкранного режима

Пожалуйста, пока не обращайте внимания на choPerson, мы вернемся к этому в следующем посте.


Таблица вывода готова!

Давайте применим немного форматирования. В PicoLisp есть встроенная функция <grid>, которая форматирует в строки и столбцы, подобно таблице. Каждые четыре «элемента» будут размещены в одной строке, а ширина определяется самым длинным элементом.

(<grid> 4
   "Occupation" (gui '(+E/R +TextField) '(job : home obj) 20)
   "Father" (gui '(+E/R +Obj +TextField) '(pa : home obj) '(nm +Man) 30)
   "born" (gui '(+E/R +DateField) '(dat : home obj) 10)
   "Mother" (gui '(+E/R +Obj +TextField) '(ma : home obj) '(nm +Woman) 30)
   ... )
Вход в полноэкранный режим Выход из полноэкранного режима


Установка начальной точки по умолчанию

Наконец, давайте определим начальную точку по умолчанию для нашего приложения. Это означает, что запись по умолчанию должна загружаться, когда мы направляем браузер на http://localhost:8080.

Как мы можем это сделать? Прежде всего, нам нужно установить val нашей глобальной переменной *DB, например, на запись {A12} (Queen Elizabeth). В программе REPL:

$ family-edit.l -family~main +
family: (set *DB '{A12})
-> {A12}
family: (show *DB)
{1} {A12}
   +Woman {3}
   +Person {2}
   +Man {4}
-> {1}
Войдите в полноэкранный режим Выйти из полноэкранного режима

Далее, давайте загрузим это значение как *ID по умолчанию при запуске сессии. Сессия также должна запускаться сразу при загрузке страницы.

(when (app)
   (redirect (baseHRef) *SesId *Url "?*ID=" (ht:Fmt (val *DB))) )
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь мы также можем динамически изменить наш html-заголовок на имя человека:

html 0 (; *ID nm) "@lib.css" NIL
Вход в полноэкранный режим Выйти из полноэкранного режима

Таким образом, при посещении сайта http://localhost:8080 мы попадаем непосредственно на запись королевы Елизаветы.


Таким образом, мы получаем все данные записи в понятном и удобном для пользователя формате. Однако все поля ввода отключены, и мы не можем сохранить какие-либо изменения.

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


Исходный код до этого момента вы можете найти здесь.


Исходники

  • https://software-lab.de/doc/index.html
  • https://software-lab.de/doc/app.html

Оцените статью
Procodings.ru
Добавить комментарий