Особенности разработки клиентских приложений ui-organizer с серверным рендерингом
1. Добавляйте модули приложения в метод AppManager.init()
Для правильной работы серверного рендеринга необходимо в клиентском приложении:
- передать в AppManager все модули используемые приложением;
- экспортировать все используемые функции (добавить приставку export).
В примера в статье "Начало работы" мы описали в файле simple.ts форму и инициировали AppManager. Т.е. все приложение у нас поместилось в одном файле. Для использования серверного рендеринга необходимо либо разделить приложение на модули, либо добавить описание формы внутрь namespace.
Разделение на модули...
Для серверного рендеринга в файле simple.ts в метод AppManager.init()
добавим:
- модуль, например,
[MyFormModule]
- и url приложения, например,
http://localhost
.
AppManager.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
Можем разделить наше приложение на модули. Создайте файл form.ts и перенесите туда описание переменной form.
import type {IForm, IButton, IProperty} from "ui-organizer";
import { Input, Position, AlignSelf} from "ui-organizer";
export var form: IForm = <IForm>{
type: 'IForm',
name: 'simpleForm',
alignSelf: AlignSelf.topCenter,
elements: [
<IProperty>{
type: 'IProperty',
name: 'email',
caption: 'Логин',
input: Input.text,
captionPosition: Position.none,
placeholder: 'Логин',
},
<IProperty>{
type: 'IProperty',
name: 'password',
caption: 'Пароль',
input: Input.password,
captionPosition: Position.none,
placeholder: 'Пароль',
},
<IButton>{
type: 'IButton',
name: 'sendData',
caption: 'Отправить',
onClick: async function () {
alert("Data sent!");
}
},
]
}
Исправьте файл simple.ts
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';
import { AppManager} from "ui-organizer";
import * as MyFormModule from './form';
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;
AppManager.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
.then(()=>{
AppManager.add([MyFormModule.form]);
AppManager.open('simpleForm', {});
})
Перенос описания формы внутрь namespace...
Для серверного рендеринга в файле simple.ts в метод AppManager.init()
добавим:
- модуль
[MyFormModule]
- и url приложения, например,
http://localhost
.
AppManager.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
Можем поместить описание формы в отдельный namespace. Исправьте файл simple.ts добавив в него namespace MyFormModule.
import './index.html';
import 'ui-organizer/page.css';
import 'ui-organizer/style.css';
import 'ui-organizer/vars.css';
import type {IForm, IButton, IProperty} from "ui-organizer";
import { AppManager, Input, Position, AlignSelf} from "ui-organizer";
namespace MyFormModule {
export var form: IForm = <IForm>{
type: 'IForm',
name: 'simpleForm',
alignSelf: AlignSelf.topCenter,
elements: [
<IProperty>{
type: 'IProperty',
name: 'email',
caption: 'Логин',
input: Input.text,
captionPosition: Position.none,
placeholder: 'Логин',
},
<IProperty>{
type: 'IProperty',
name: 'password',
caption: 'Пароль',
input: Input.password,
captionPosition: Position.none,
placeholder: 'Пароль',
},
<IButton>{
type: 'IButton',
name: 'sendData',
caption: 'Отправить',
onClick: async function () {
alert("Data sent!");
}
},
]
}
}
var Global: any = window;
Global.AppManager = AppManager;
Global.onpopstate = AppManager.onPopState;
AppManager.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
.then(()=>{
AppManager.add([MyFormModule.form]);
AppManager.open('simpleForm', {});
})
2. Отслеживайте место исполнения кода
Свойство AppMAnager.onServer
Вот этот скрипт при серверном рендеринге будет выполнятся и на клиенте и на сервере.
AppManager.init(document.querySelector('#app'))
.then(()=>{
AppManager.open('simpleForm', {});
})
Метод AppManager.init()
- на сервере создает объекты элементов управления и формирует html,
- на клиенте получает готовый html и готовые объекты элементов управления, сформированные на сервере,
- и (на клиенте) связывает html элементы с объектами элементов управления.
Также метод AppManager.init
заполняет свойство AppManager.onServer
для того, чтобы отследить где выполняется скрипт.
Пример:
AppManager
.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
.then(async () => {
AppManager.add([new MyForm.Form(Global.command)]);
let data: any;
if (AppManager.onServer) {
data = await MyForm.xmlHttpRequest(`${AppManager.serverUrl}/${dataJsonFile}`);
if (data) data = JSON.parse(data);
else console.log(`Нет данных ${dataJsonFile}`);
}
AppManager.open('simpleForm', data);
})
Свойства event.onServer и event.serverSideRendering
AppManager.open одинаково работает на сервере и на клиенте и при открытии формы последовательно вызывает:
Поэтому, и на сервере и на клиенте вызываются методы onBeforeLoad, onBeforeSetData и другие. Чтобы во время исполнения программы отслеживать где выполняется код используйте свойство onServer
и serverSideRendering
, которые доступны в объектах событий load и setData.
Пример:
async onBeforeSetData(event, form, elem){
if(event.onServer != event.serverSideRendering) {
event.stop();
return;
}
}
При серверном рендеринге:
- на сервере:
- event.onServer == true;
- event.serverSideRendering == true;
- на клиенте:
- event.onServer == false;
- event.serverSideRendering == true;
Без серверного рендеринга:
- на клиенте:
- event.onServer == false;
- event.serverSideRendering == false;
3. Указывайте url приложения в Renderer и в самом приложении
Указывайте serverUrl в Renderer сервера (в папке my-poject-server) и в клиентском приложении (в папке my-poject).
В серверном приложении передаем url в конструктор Renderer. Это необходимо, чтобы программа понимала, что запросы ресурсов на этот url выбираем из папки distDir:
const distDir: string = Path.join(__dirname, `../test/dist`);
const url: string = `http://localhost:${port}`;
const renderer = new Renderer(distDir, url);
var httpserver: http.Server = http.createServer((req, res)=>{
renderer.response(req, res);
});
httpserver.listen(port, () => console.log(`UI app listening on port ${port}!`));
В клиентском приложении передаем url в метод AppManager.init(). Это необходимо, чтобы во время инициализации на клиенте метод AppManager.init()
запросил данные с сервера.
AppManager
.init(document.querySelector('#app'), [MyFormModule], 'http://localhost')
.then(async () => {
AppManager.add([new MyFormModule.Form(Global.command)]);
AppManager.open('simpleForm', data);
})
4. Нельзя добавлять функции в Runtime
В приложении, которое рендерится на сервере нельзя, например, делать так:
async onAfterLoad(event, form, elem){
this.myFunction = function(){
}
}
Надо сделать вот так:
export let myElement = <IElement>{
type: 'IElement',
async onAfterLoad(event, form, elem){
this.myFunction = myFunction;
}
}
export function myFunction(){
}