Блог

    Caché (Кашэ́) — промышленная высокопроизводительная, объектная система управления базами данных, интегрированная с технологией разработки веб-приложений. Единая архитектура данных Caché позволяет разработчикам использовать одновременно объектный, реляционный (SQL) и прямой (NoSQL) доступ к одним и тем же данным, хранение которых обеспечивается ориентированным на транзакции многомерным ядром СУБД.

    http://www.intersystems.ru/cache/

Последние записи


Теги

Информация

$(REST - CSP - (-HyperEvents) + EasyUI + File Upload). Часть 3a[ключительная]

добавлено: 08 июл 15
понравилось:0
просмотров: 1632
комментов: 0

теги:

Автор: servit

Продолжим наш цикл статей, посвящённых созданию веб-приложения полностью на rest.

Интеграция с jQuery EasyUI (на примере datagrid и tree)


Желающие посмотреть демо-примеры offline и без серверной части, могут найти их в папках demo и demo-mobile в скачанном архиве.
Для пользователей IE возможно понадобится в настройках "Разрешить запуск активного содержимого файлов на моём компьютере", чтобы каждый раз не отвлекаться на всплывающее окно.
Также можно заменить файл jquery.min.js более свежим: для локальных тестов в случае IE11 это должна быть версия 1.11.x, так как в версии 2.1.x для IE11 не работает локальный AJAX.

Дополнительно можно посмотреть и демо в online, там же доступны учебники и документация.

Итак, первый на очереди у нас Datagrid.

Datagrid


Для начала создадим хранимый класс и заполним его данными:
Class my.a Extends %Persistent Final ]
{

Property idp As %Integer;

Property name As %String;

/// d ##class(my.a).Fill()
ClassMethod 
Fill(count 10)
{
  
..%KillExtent()
  
i=1:1:count ^my.aD(i)=$lb(,"name_"_i)
  
^my.aD=count
  
^my.aD(6)=$lb(,"<Привет> из <br><span style='color:red;'>Caché</span>")
  
  
^my.aD(1)=$lb(,"ROOT")
  
^my.aD(2)=$lb(1,"node2")
  
^my.aD(3)=$lb(2,"node3")
  
^my.aD(4)=$lb(1,"node4")
  
^my.aD(5)=$lb(4,"node5")
}
}

USER>##class(my.a).Fill()

Пересоздадим наш rest-класс, чтобы взгляд не отвлекался на не относящееся к сути темы:
Class my.rest Extends %CSP.REST
{

Parameter CHARSET = "UTF-8";

Parameter CONTENTTYPE = {..#CONTENTTYPEJSON};

Parameter UseSession As Integer = 1;

XData UrlMap [ XMLNamespace "http://www.intersystems.com/urlmap" ]
{
<
Routes>
  <
Route Url="/" Method="GET" Call="MainPage"/>
  <
Route Url="/(.*)" Method="GET" Call="StaticFiles"/>
</
Routes>
}

ClassMethod StaticFiles(urlAs %Status
{
  
%request.Data
  s 
%request.Data("FILE",1)=%request.URL
  
  d 
##class(%CSP.StreamServer).OnPreHTTP()
  
##class(%CSP.StreamServer).OnPage()
  
q $$$OK
}

ClassMethod MainPage() As %Status
{
  
%response.ContentType="text/html"
  
&html<
<!DOCTYPE html>
<
html>
  <
head>
    <
meta charset="#(..#CHARSET)#">
    <
meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <
meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1, user-scalable=no"/>
    <
link rel="stylesheet" type="text/css" href="easyui/themes/default/easyui.css">
    <
link rel="stylesheet" type="text/css" href="easyui/themes/icon.css">
    <
script type="text/javascript" src="easyui/jquery.min.js"></script>
    <
script type="text/javascript" src="easyui/jquery.json.min.js"></script>
    <
script type="text/javascript" src="easyui/jquery.easyui.min.js"></script>
    <
script type="text/javascript" src="easyui/locale/easyui-lang-#(%session.Language)#.js"></script>
    <
title>Главная страница</title>
    <
script type="text/javascript">
    
    
var MyApp={
      
    };

    (
function($){
      
      $.ajaxSetup({
        type: 
'POST',
        dataType: 
'json',
        cache: false,
        async: true,
        contentType: 
'application/json; charset=UTF-8',
        processData: false,
        beforeSend: 
function(jqXHR,settings) {
          settings.data
=$.toJSON(settings.data);
        },
        statusCode: {
          
401function() {alert('Вы не авторизованы')},
          
404function() {alert('Страница не найдена')},
          
405function() {alert('Метод запрещён')},
          
500function(jqXHR,textStatus,errorThrown) {alert($.toJSON(jqXHR.responseJSON,null,2))}
        },
        error: 
function(jqXHR, textStatus, errorThrown){
          
if (textStatus==='timeout') {alert('Не успели :(')}
        }
      });
      
    })(jQuery);

    $(
function(){
      
    });
    
    </
script>
  </
head>
  <
body>
  </
body>
</
html>>
  
q $$$OK
}

}
Добавим на страницу таблицу и метод для неё, возвращающий json-данные:
...
<body>
  <
table id="dg" class="easyui-datagrid" title="DataGrid" style="width:500px;height:350px" data-options="url:'getsqljson'">
    <
thead>
      <
tr>
        <
th data-options="field:'ID',width:60,align:'center'">ID</th>
        <
th data-options="field:'idp',width:60,align:'center'">Родитель</th>
        <
th data-options="field:'name',width:160,align:'right'">Имя</th>
      </
tr>
    </
thead>
  </
table>
</
body>
...

ClassMethod SrvGetSQLJSON() As %Status
{
  
##class(%ZEN.Auxiliary.jsonSQLProvider).%WriteJSONFromSQL(,"select * from my.a")
  
q $$$OK
}

...
<Routes>
<
Route Url="/getsqljson" Method="POST" Call="SrvGetSQLJSON"/>
...
Если теперь под отладкой попытаться обновить страницу, а значит и наш datagrid, то мы увидим в консоли ошибку.
Это потому, что json-данные возвращаются в поле "children", о котором jeasyui ничего не знаёт.

Что делать?

Переписывать загрузчик по-умолчанию, который поместим сразу за $.ajaxSetup:
...
  
function getJsonLoader(pluginName){
    
return function(param, success, error){
      
var opts $(this)[pluginName]('options');
      
if (!opts.url) return false;
      $.ajax({
        url: opts.url,
        data: param,
        success: 
function(data){
            success(data.children);
          },
        error: 
function(){
          error.apply(
this, arguments);
        }
      });
    }
  };

  $.fn.datagrid.defaults.loader 
getJsonLoader('datagrid');

})(jQuery);
Теперь данные не только загружаются, но и отображаются.

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

Давайте сделаем кнопку, при нажатии которой произойдёт обновление части данных.
Для этого перепишем сам запрос для задействования входных параметров и добавим саму кнопку, но не простую..
ClassMethod SrvGetSQLJSON() As %Status
{
  
p1=##class(%ZEN.Auxiliary.parameter).%New()
  
p2=##class(%ZEN.Auxiliary.parameter).%New()
  
p1.value=1
  
p2.value=10

  
if $IsObject(%request.Content{
    
##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(%request.Content,,.params)

    
s:params.p1'="" p1.value=params.p1
    s
:params.p2'="" p2.value=params.p2
  
}

  
p=##class(%ZEN.Auxiliary.jsonSQLProvider).%New()
  
p.sql="select %ID,idp,name from my.a where %ID between ? and ?"
  
p.parameters.SetAt(p1,1)
  
p.parameters.SetAt(p2,2)
  
p.%Format=""
  
p.maxRows=0
  
p.%DrawJSON()
  
  
q $$$OK
}

var MyApp={
  toolbar: [{
    id:
'tbReload',
    text:
'Обновить',
    iconCls:
'icon-reload',
    handler:
function(){$('#dg').datagrid('load', {p1:2,p2:6})}
  }]
};


<table id="dg" class="easyui-datagrid" title="DataGrid" style="width:500px;height:350px" data-options="url:'getsqljson',toolbar:MyApp.toolbar">
Теперь при начальной загрузке страницы будут отображаться записи с id [1,10], а при нажатии кнопки - с id [2,6].
Важно:
Всегда используйте параметризованные запросы, а не склейку самого текста запроса, иначе код будет подвержен sql-инъекциям.

Tree


Здесь многое аналогично вышеприведённому, но с небольшими нюансами.
Для начала добавим само дерево в нашу страничку и переопределим загрузчик:
...
  </table>
  <
div title="Tree" style="width:300px;height:350px">
    <
ul id="tree" class="easyui-tree" data-options="url:'getsqljson'"></ul>
  </
div>
</
body>
...

...
$.fn.datagrid.defaults.loader getJsonLoader('datagrid');
$.fn.tree.defaults.loader 
getJsonLoader('tree');
...
Кое-что мы уже видим, но вовсе не то, на что рассчитывали.
Это происходит потому, что в загрузчике пока не был задействован метод _makeTree, исправим это (предполагается, что он у нас находится в объекте MyApp):
...
success: ((pluginName
=='tree')||(pluginName=='treegrid'))?
  
function(data){
    success(MyApp._makeTree({q:data.children, id:
'ID', parentid:'idp'}));
  }
:
  
function(data){
    success(data.children);
  },

...
Структура дерева уже отображается правильно, но вот сам текст узлов..
Согласно документации на tree мы должны возвращать следующие поля:

Every node can contains following properties:
•  id: node id, which is important to load remote data
•  text: node text to show
•  state: node state, 'open' or 'closed', default is 'open'. When set to 'closed', the node have children nodes and will load them from remote site
•  checked: Indicate whether the node is checked selected.
•  attributes: custom attributes can be added to a node
•  children: an array nodes defines some children nodes
То есть нам нужно поле "name" заменить на "text" и "ID" на "id".

Легко:
...
success: ((pluginName
=='tree')||(pluginName=='treegrid'))?
  
function(data){
    success(JSON.parse($.toJSON(csprest._makeTree({q:data.children, id:
'ID', parentid:'idp'})), function(k, v) {
      
if (k === "ID"
        
this.id v;
      
else if (k === "name")
        
this.text v.replace('<br>','&nbsp;');
      
else
        return 
v;
    }));
  }
:
  
function(data){
    success(data.children);
  },
...
Теперь дерево отображается полностью как задумывалось.

Повесим на кнопку загрузку данных по требованию и для дерева:
...
handler:
function(){
  $(
'#dg').datagrid('load', {p1:2,p2:6});
  $(
'#tree').tree({queryParams:{p1:1,p2:7}});
}
...
myrest.zip

Комментарии




Необходимо войти на сайт, чтобы оставлять комментарии