Урок 2. SAPUI5. Model View Controller. Модуляризация.

MVC sapui5

SAPUI5 поддерживает схему Model View Controller. Мы разделяем данных приложения, пользовательского интерфейса и управляющей логики на три отдельных компонента: модель (Model), представление (View) и контроллер (Controller) — таким образом, мы можем делать изменения в одном компоненте, не изменяя другой компонент (модификация каждого компонента может осуществляться независимо).

Главная задача MVC — отделении бизнес-логики (модели) от её визуализации (представления).

  1. Модель (Model) предоставляет данные и методы работы с ними: запросы в базу данных, проверка на корректность.
    Модель не зависит от представления — не знает как данные визуализировать — и контроллера — не имеет точек взаимодействия с пользователем — просто предоставляя доступ к данным и управлению ими.
  2. Представление (View) отвечает за получение необходимых данных и визуализацию (отображение на экране пользователю) данных модели, реагируя на изменения модели.
  3. Контроллер (Controller) отвечает за обработку действий пользователя, оповещая модель о необходимости изменений.
  4. Контроллер связывает между собой пользователя и систему. Обеспечивает обмен данными между пользователем и системой. Использует модель и представление для реализации необходимого действия.

Model View Controller

Пользователь использует контроллер, который управляет моделью, которая обновляет представление, которое визуализирует данные для пользователя. Круг замкнулся :).

Хватит теории, перейдем к делу!

Вспомним нашу первую программу.
В ней мы все части уместили в одном файле.
Давайте разделим и реализуем архитектурный паттерн MVC (Model, View, Controller) в SapUI5!

Создадим 3 файла:

create MVC sapui5

index.html:

<!DOCTYPE HTML>
<html>

<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8"/>
<title>sap.hello</title>

<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m"
data-sap-ui-theme="sap_belize"
data-sap-ui-compatVersion="edge"
data-sap-ui-resourceroots='{"sap.hello": ""}'>
</script>

<link rel="stylesheet" type="text/css" href="css/style.css"/>

<script>
var oView = sap.ui.view({
id: "idMain",
viewName: "sap.hello.view.main",
type: sap.ui.core.mvc.ViewType.XML
});

oView.placeAt("content");
</script>
</head>

<body class="sapUiBody" id="content">
</body>

</html>

/controller/main.controller.js:

sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/Device",
"sap/hello/lib/MessageManager"
], function(Controller, Device, MessageManager) {
"use strict";

return Controller.extend("sap.hello.controller.main", {
onInit: function() {
this.getView().addStyleClass(
Device.support.touch ? "sapUiSizeCozy" : "sapUiSizeCompact");
},

onPressBtn: function () {
MessageManager.reportSuccess("привет sap!", "приветствие");
},


onPressBtn_2: function () {
sap.ui.require(["sap/hello/lib/MessageManager"], function(MessageManager_2) {
MessageManager_2.reportSuccess("привет мир", "заголовок");
});
}
});
});

/view/main.view.xml:

<mvc:view controllerName="sap.hello.controller.main" xmlns:html="http://www.w3.org/1999/xhtml" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">

<label text="text" class="sapUiSmallMargin"></label>

<button text="click me" icon="sap-icon://accept" type="Emphasized" press="onPressBtn"></button>

<button text="click me 2" icon="sap-icon://chalkboard" type="Emphasized" press="onPressBtn_2"></button>

</mvc:view>

Так же создадим папку lib и в ней два файла, о назначении которых мы поговорим позже:
mvc sapui5

/lib/Formatter.js:

sap.ui.define([], function(){
return {
capitalizeFirstLetter: function(sValue) {
return sValue.charAt(0).toUpperCase() + sValue.slice(1);
}
};
});

/lib/MessageManager.js:

sap.ui.define (["sap/m/MessageBox","sap/hello/lib/Formatter"], function(MessageBox, Formatter){
return{
reportSuccess: function(sMsg, sTitle){
MessageBox.show(Formatter.capitalizeFirstLetter(sMsg), {
title: Formatter.capitalizeFirstLetter(sTitle)
});
}
};
});

Попробуем запустить:
click me

Разберем пример более подробно!

В index.html загружаем sapui5.
Видим новую опцию при загрузки sapui5:
data-sap-ui-resourceroots='{"sap.hello": ""}'
Этой записью мы сообщаем ядру SapUI5, что ресурсы в пространстве имен «sap.hello» находятся в той же папке, что и index.html.

Что такое «пространство имен в SapUI5»?

Пространство имен в SapUI5 — это своеобразный ярлык (ссылка) на папку.

Например, если мы напишем:
data-sap-ui-resourceroots='{"sap.test2": "./work"}'
мы обозначим, что ресурсы в пространстве имен sap.test2 находятся в корневой папке work.

Там же появляется новая конструкция:
<script>
var oView = sap.ui.view({
id: "idMain",
viewName: "sap.hello.view.main",
type: sap.ui.core.mvc.ViewType.XML
});

oView.placeAt("content");
</script>

Мы сообщаем SapUI5, что наше представление (View) имеет название sap.hello.view.main (sap.hello — пространство имен, которое ссылается на корень. view.main — папка view, файл main.view).

Тип представления — sap.ui.core.mvc.ViewType.XML. То есть SapUI5 будет искать файл view/main.view.xml.
В SapUI5 можно задавать представления не только в виде JavaScript (как мы познакомились в первом уроке), но и в XML, JSON, HTML.

Перейдем к представлению view/main.view.xml.

<mvc:view controllerName="sap.hello.controller.main" xmlns:html="http://www.w3.org/1999/xhtml" displayBlock="true" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">

Мы говорим, что у данного представления будет контроллер sap.hello.controller.main (который в пространстве имен sap.hello и находиться в папке {пространство имен}/controller/main.controller.js).
xmlns=»sap.m» означает, что элементы, которые мы используем в представлении, а в данном примере button и label, мы будем брать из библиотеки sap.m.

И вводим «ссылку на библиотеку» mvc=»sap.ui.core.mvc».
Говоря другими словами, исходная запись должна выглядеть следующим образом:
<sap.ui.core.mvc.view controllerName="sap.hello.controller.main"
но мы решили сократить запись и ввели «ссылку mvc«: mvc=»sap.ui.core.mvc»:
<mvc:view controllerName="sap.hello.controller.main"

Далее объявили label с предопределенным классом sapUiSmallMargin.

И два Button с иконками и событиями press (нажатие на кнопку).

Оба элемента мы брали из библиотеки sap.m (помните атрибут xmlns=»sap.m»).

Если есть необходимость обратиться к элементам из другой библиотеки. Например, к table из библиотеки sap.ui.table, то либо делаем xmlns:t=»sap.ui.table» и в представлении обращаемся как t:Table, либо обращаемся к полному имени sap.ui.table.Table.

Перейдем к контроллеру controller/main.controller.js.

sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/Device",
"sap/hello/lib/MessageManager"
], function(Controller, Device, MessageManager) {
"use strict";

При помощи sap.ui.define (jQuery.sap.declare — синхронная загрузка) мы определяем сам контроллер. Cообщаем ему, какие модули мы планируем асинхронно загрузить и использовать:

  • sap/ui/core/mvc/Controller -> Controller
  • sap/ui/Device -> Device
  • sap/hello/lib/MessageManager -> MessageManager (самописный модуль, файл lib/MessageManager.js в пространстве имен sap/hello)

Функция обратного вызова (Callback):
function(Controller, Device, MessageManager)
сработает сразу после успешной загрузки перечисленных модулей.

Чтобы перевести код в режим полного соответствия современному стандарту, нужно указать специальную директиву use strict (подробней про use strict — https://learn.javascript.ru/strict-mode).

Объявили три функции:
onInit: function() {
this.getView().addStyleClass(
Device.support.touch ? "sapUiSizeCozy" : "sapUiSizeCompact");
},

onPressBtn: function () {
MessageManager.reportSuccess("привет sap!", "приветствие");
},


onPressBtn_2: function () {
sap.ui.require(["sap/hello/lib/MessageManager"], function(MessageManager_2) {
MessageManager_2.reportSuccess("привет мир", "заголовок");
});
}

  • onInit — содержимое отработает при инициализации.
  • onPressBtn — нажатие на первую кнопку.
  • onPressBtn_2 — нажатие на вторую кнопку, в котором вызываем sap.ui.require (подгружаем модуль lib/MessageManager.js пространства имен sap/hello)

Кнопки с событиями onPressBtn и onPressBtn_2 делают одно и то же: Вызывают метод reportSuccess, который описан в sap/hello/lib/MessageManager.
Только для onPressBtn мы описали модуль MessageManager глобально. В onPressBtn_2 описали модуль локально, внутри события.

Посмотрим что делает lib/MessageManager.js.

sap.ui.define (["sap/m/MessageBox","sap/hello/lib/Formatter"], function(MessageBox, Formatter){

Определяем модуль и асинхронно загружаем стандартную sap/m/MessageBox и еще один самописный модуль sap/hello/lib/Formatter.

После загрузки содержимое функции
function(MessageBox, Formatter){
отработает.

Описали метод reportSuccess:
reportSuccess: function(sMsg, sTitle){
MessageBox.show(Formatter.capitalizeFirstLetter(sMsg), {
title: Formatter.capitalizeFirstLetter(sTitle)
});
});

который вызывает сообщение MessageBox.show и в качестве параметров передает возвращаемое значение Formatter.capitalizeFirstLetter: sMsg и Formatter.capitalizeFirstLetter: title.

Formatter.capitalizeFirstLetter (модуль Formatter.js) берет строку (sValue), первую (элемент №0 в строке) букву (sValue.charAt(0)) поднимает в верхний регистр (toUpperCase()), прибавляет к строке без первой буквы (sValue.slice(1)) и возвращает результат:
capitalizeFirstLetter: function(sValue) {
return sValue.charAt(0).toUpperCase() + sValue.slice(1);
}

Отдельно отметим, что модуль Formatter.js начинается с пустого:
sap.ui.define([], function(){

В итоге

1. Мы разделили приложение на 2 части:

  • Вынесли отдельно часть, отвечающую за отображение информации на экране — Представление (view) /view/main.view.xml
  • Вынесли отдельно часть, отвечающую за обработку действий пользователя — Контроллер (controller) /controller/main.controller.js

2. Реализовали модульность.
Вынесли в отдельный модуль (файл MessageManager.js) функцию показа сообщения при нажатии на кнопку и вынесли в отдельный модуль (файл Formatter.js) функцию обработки строки (первая буква в строке — заглавная).

Исходники к уроку №2 можно скачать по ссылке https://github.com/kannade/modularization_sapui5.