JsForms в Showcase

Материал из Course Orchestra
Перейти к: навигация, поиск
Showcase

Создание решений на КУРС:Showcase

Общие сведения

JsForms представляют собой обычный HTML с возможностью, как и в обычном HTML, использовать любые внешние js библиотеки и компоненты. Изначально к ним уже подключены компоненты dojo для задания контролов.

Задание шаблонов

Шаблоны для JSForms хранится на диске в каталоге jsForms используемой userdata. В качестве шаблона может использоваться html-файл, содержащий в себе вставки javascript, но список действий в таком случае нужно хранить в отдельном файле, например, userdatas\default\data\jsform\nameOfHtmlTemplate.settings.xml. Если в качестве источника данных служит скрипт или хранимая процедура, то хранение данных для шаблона и задание списка действий нужно совместить в источнике данных шаблона.

Для использования внутри тега <script> функций dojo требуется следующая вставка:

require(["dijit/registry", "dijit/form/Button", "dojo/dom", "dojo/domReady!"], function(registry, Button, dom) {......})

Такая запись позволяет, например, обращаться к элементу dom-модели используя модуль dojo/dom в составе Dojo DOM API – это не только короче, но и работает во всех браузерах: dom.byId(…) вместо document.getElementById(…)

Для избежания ошибок при перезагрузке javascript-кода следует внутри function определять следующую вставку в самом начале скрипта (в данном случае шаблон содержит только один элемент с id=”id1”):

    var destroy = function(widget) {
      if (widget) {      
        widget.destroyRecursive(true);
      }
    }
    destroy(registry.byId("id1"));

Все кнопки на форме (выполняющие некие действия) задаются разработчиками приложения. Для создания кнопок применяется класс dijit/form/Button, при этом сама кнопка задается переменной в javascript-коде в блоке require внутри собственной функции:

  var myButton3 = new Button({
      label: "MyJSAction1",
      onClick: function(){
    	  jsFormAction('xformId','10');
      }
    }, "myButton3").startup();

Задание шаблона в формате html (файл simple_template.html)

<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <script charset="utf-8">
    
  </script><script type="text/javascript" charset="utf-8">
  
  require(["dijit/registry", "dijit/form/Button", "dojo/dom", "dijit/form/TextBox", "dojo/domReady!"], function(registry, Button, dom){ 
        var destroy = function(widget) {
      if (widget) {      
        widget.destroyRecursive(true);
      }
    }
    destroy(registry.byId("SynchButton"));
    destroy(registry.byId("ASynchButton"));
    destroy(registry.byId("dojoinput"));
    destroy(registry.byId("simpleinput"));
    
    function jsOnload() {
    document.getElementById("simpleinput").value = "javascript_onload_function";
    }      
    jsOnload();
  
      var callback = function(resulData) {
      if (resulData) {
        if (resulData.isError) {
          dom.byId("dojoinput").value = "Failure. Error message: '"+resulData.message+"'";
        } else {
          dom.byId("dojoinput").value = "Success! Received data: '"+resulData+"'";
        }
      } else {
        dom.byId("dojoinput").value += "Data not received";
      }    
    }
    
    var ASynchButton = new Button({
      label: "Asynch Submit",
      onClick: function(){  
          jsFormSubmitAsynch('13','mysubmit2', 'data', 
            function(data) {
              dom.byId("result").innerHTML += "Success! Received data: '"+data+"'";
            }, function(err) {
              dom.byId("result").innerHTML += "Failure. Error message: '"+err.message+"'";
            }
          );
      }
    }, "ASynchButton").startup();
    
    var SynchButton = new Button({
      label: "Synch Submit",
      onClick: function(){
          var resulData = jsFormSubmitSynch('13','mysubmit1', 'data');
          callback(resulData);
      }
    }, "SynchButton").startup();
    
  });
</script>

</head>

<body>
<input id="simpleinput" type="text" name="name1"><br><br>
<input id="dojoinput" data-dojo-type="dijit/form/TextBox" type="text" value="dojo textbox default value"></input><br><br>
<button id="ASynchButton" type="button"></button>
<button id="SynchButton" type="button"></button>
<div id="result"></div>
</body>

</html>

Сопутствующий этому шаблону элемент в датапанели выглядит следующим образом:

<tab id="331" name="JS Test">
    <element id="13" type="jsForm" template="MyTemplate.html" showLoadingMessage="true">
 	<proc id="mysubmit" name="myJSForm1" type="JSFORMSUBMIT" />
 	<proc id="mysubmitdb" name="myJSForm2" type="JSFORMSUBMIT" />       
     </element>
     <element id="235" type="webtext" proc="webtext_anton1" />
</tab>

С нажатием кнопки может быть связан вызов сервлета через механизм submission и/или вызов одной из 4-х процедур JS, определенных в клиентском коде GWT.

Задание шаблона с помощью Jython-скрипта (в котором также задаются действия) – файл htmldatascript.py

# coding: utf-8
'''
Created on 14.04.2016

'''
from ru.curs.showcase.core.jython import JythonProc
from ru.curs.showcase.core.jython import JythonDTO


class htmldatascript(JythonProc):
    
    def templateJsForm(self, context, elementId):
        return doTemplateJsForm(context, elementId)

def doTemplateJsForm(context, elementId):
    data = u'''
      <script charset="utf-8">
    
  </script><script type="text/javascript" charset="utf-8">
  
  require(["dijit/registry", "dijit/form/Button", "dojo/dom", "dijit/form/TextBox", "dojo/domReady!"], function(registry, Button, dom){ 
        var destroy = function(widget) {
      if (widget) {      
        widget.destroyRecursive(true);
      }
    }
    destroy(registry.byId("ModalWindow"));
    destroy(registry.byId("JsFormAction"));
    destroy(registry.byId("SynchButton"));
    destroy(registry.byId("ASynchButton"));
    destroy(registry.byId("dojoinput"));
    destroy(registry.byId("simpleinput"));
    
    function jsOnload() {
    document.getElementById("simpleinput").value = "javascript_onload_function";
    }      
    jsOnload();
  
      var callback = function(resulData) {
      if (resulData) {
        if (resulData.isError) {
          dom.byId("dojoinput").value = "Failure. Error message: '"+resulData.message+"'";
        } else {
          dom.byId("dojoinput").value = "Success! Received data: '"+resulData+"'";
        }
      } else {
        dom.byId("dojoinput").value += "Data not received";
      }    
    }
    
    var ASynchButton = new Button({
      label: "Asynch Submit",
      onClick: function(){  
          jsFormSubmitAsynch('13','mysubmit2', 'data', 
            function(data) {
              dom.byId("result").innerHTML += "Success! Received data: '"+data+"'";
            }, function(err) {
              dom.byId("result").innerHTML += "Failure. Error message: '"+err.message+"'";
            }
          );
      }
    }, "ASynchButton").startup();
    
    var SynchButton = new Button({
      label: "Synch Submit",
      onClick: function(){
          var resulData = jsFormSubmitSynch('13','mysubmit1', 'data');
          callback(resulData);
      }
    }, "SynchButton").startup();
    
    var JsFormAction = new Button({
      label: "JsFormAction",
      onClick: function(){
          jsFormAction('13','10');
      }
    }, "JsFormAction").startup();
    
    
    var ModalWindow = new Button({
      label: "Open Modal Window",
      onClick: function(){
          jsFormAction('13','11');
      }
    }, "ModalWindow").startup();
    
    

  });
</script>
<input id="simpleinput" type="text" name="name1"><br><br>
<input id="dojoinput" data-dojo-type="dijit/form/TextBox" type="text" value="dojo textbox default value"></input><br><br>
<button id="ASynchButton" type="button"></button>
<button id="SynchButton" type="button"></button>
<button id="JsFormAction" type="button"></button>
<button id="ModalWindow" type="button"></button>
<div id="result"></div>'''
    settings = u'''<properties>
                <event name="single_click" linkId="10">
                <action>
                <main_context>new_main_context</main_context> 
                <navigator element="0901"/>
                </action>                        
                </event> 
                <event name="single_click" linkId="11">
                <action show_in="MODAL_WINDOW">
                    <main_context>current</main_context>
                    <modalwindow caption="myButton click." height="200" width="600"/>
                    <datapanel type="current" tab="current">
                        <element id="333">
                          <add_context>add_context33</add_context>
                        </element>
                    </datapanel>
                </action>
                </event>
    </properties>'''
    return JythonDTO(data, settings)    
 
if __name__ == "__main__":
    doTemplateJsForm(None, None)

В данном случае обязательно нужно определить функцию def templateJsForm(self, context, elementId), которая является частью интерфейса JythonProc.

Вызываемые процедуры

Процедура асинхронной отправки данных

jsFormSubmitAsynch('elementId', procId, DATA, onsuccessfunction, onfailurefunction) – функция возвращает ответ процедуры и в качестве параметров принимает текущий id элемента jsform, id процедуры, данные, a функции onsuccesfunction и onfailurefunction являются javascript-функциями обратного вызова, предусмотренными на случай успеха или неудачи в отправке данных. procId должна быть определена в датапанели. Для предотвращения ошибки в консоли, требуется в челеста-процедуре возвращать объект типа JythonDTO. Иначе действие выполняться будет, но в в консоли браузера и в логах сервера будут копиться ошибки.

Процедура синхронной отправки данных

jsFormSubmitSynch('elementId', procId, DATA) - функция возвращает ответ процедуры (до получения ответа выполнение блокируется) и в качестве параметров принимает текущий id элемента jsform, id процедуры и данные. procId должна быть определена в датапанели. Для предотвращения ошибки в консоли, требуется в челеста-процедуре возвращать объект типа JythonDTO. Иначе действие выполняться будет, но в в консоли браузера и в логах сервера будут копиться ошибки.

Процедура запуска действия

jsFormAction('elementId', linkId) – требуется только текущий id элемента jsform и id конкретного действия, которое должно быть указано внутри источника данных шаблона в теге settings. Данная функция не может быть вызвана из html-шаблона.

Процедура закрытия текущего модального окна

jsFormCloseModalWindow() – не принимает параметров.

Подключение внешних js-библиотек

Задача решена средствами dojo. В шаблоне jsForm внутри тега <script> для подключения библиотеки, например viewer.js, используется следующий код:

require({
	baseUrl: "solutions/",
packages: [
	{ name: "view", location: "default/js/scripts/viewerjs" }
     ]
	}, [ "view/viewer" ], function(v){
           alert("Loaded!!!");
           var vi = new v(document.getElementById('image'));
           vi.show();
});

Что есть что?

baseUrl - адрес, указываемый для того, чтобы выйти за пределы контекста dojo, а именно, за пределы папки js.

В разделе packages мы указываем алиас (name) для пути (location) до нашей библиотеки.

В квадратных скобках указываем расположение нашей библиотеки (расширение .js не пишем) относительно адреса, соответствующего алиасу.

Таким образом полный путь до библиотеки будет: solutions/default/js/scripts/viewerjs/viewer.js

Т.к. в библиотеке viewer.js возвращается объект Viewer, то переменная v (аргумент функции) и является этим объектом, и мы можем оперировать им, как написано в документации к библиотеке.

File Upload

Закачка файлов на сервер происходит с помощью сервлета с url: /fileupload. Имя celesta-процедуры, передаваемой сервлету, задаётся в файле generalapp.properties в свойстве file.upload.proc. Данная процедура принимает на вход объект InputStream, соответствующий закачиваемому файлу, а возвращает объект JythonDTO, причём параметр data этого объекта должен содержать сообщение об окончании закачки файла на сервер. Пример соответствующей js-формы:

  <!DOCTYPE html>
  <html>
   <head>
    <meta charset="utf-8">
    <title>test file</title>
   </head>
   <body>
 
     <iframe name="iframefile" src="javascript:''" style="position:absolute;width:0;height:0;border:0"> </iframe>
  
     <form enctype="multipart/form-data" method="post" action="fileupload" target="iframefile">
     <p><input type="file" name="f">
     <p><input type="text" name="fe">
     <input type="submit" value="ok"></p>
    </form> 
   </body>
  </html>

Обработка в JSForms ошибок, приходящих с сервера

Если на сервере в celesta-процедуре происходит ошибка, то в jsForm-у приходит полный текст страницы обработчика внутренней ошибки сервера, например, ошибок 404 или 500. Т.к. страницы error404.jsp и error500.jsp кастомизируются разработчиком решения, проще всего извлечение текста сообщения об ошибке осуществить непосредственно в jsForm-е. Допустим в челеста-процедуре генерируется ошибка с помощью вызова:

context.error(u'Мое сообщение')

Тогда обработку ошибки в jsForm-е можно осуществить с помощью кода:

var callback = function(resulData) {
 if (resulData) {
    if (resulData.isError) {
     var index1 = resulData.message.indexOf("<strong>Error message:</strong>");
     var index2 = resulData.message.indexOf("</body>");

     var errorMessage = resulData.message.substring(index1+ "<strong>Error message:</strong>".length, index2).trim();
     dom.byId("result1").innerHTML += "Failure. Error message: '"+errorMessage+"'";
 
    showErrorMessageWindow("Error message", errorMessage);
}
}
}

Здесь переменная errorMessage содержит в итоге текст ошибки, а вызов функции showErrorMessageWindow("Error message", errorMessage); выдаёт модальное окно с текстом ошибки. А функцию callback() можно использовать затем для обработки ошибок при вызове стандартных jsForm-функций jsFormSubmitAsynch() и jsFormSubmitSynch(), одним из параметров которых и является указанная celesta-процедура, генерирующая ошибку.