Now Loading ...
-
SAP UI5 란
SAP UI5
SAP UI5 란
HTML5, CSS, JavaScript로 구성된 클라이언트 UI 기술이다.
SAP UI5로 개발된 앱들은 우리가 사용하는 장치의 브라우저 안에서 실행한다.
사용자들이 앱을 액세스 할 때, 해당 서버로 애플리케이션을 브라우저에 로드하라는 요청을 전송, View는 SAP UI5에 관련된 라이브러리를 액세스한다. 모델은 또한 인스턴스화되고, 비즈니스데이터는 데이터베이스로부터 가져온다.
SAP UI5가 사용되는 환경에 따라, 라이브러리 또는 애플리케이션을 SAP NetWeaver Application Server 또는 SAP Business Technology Platform 등에 저장할 수 있으며, 비즈니스 데이터(예: SAP Gateway)를 통해 OData 모델을 사용하여 액세스할 수 있다.
프레임워크의 아티팩트는 라이브러리라는 최상의 구조단위로 되어있으며, 라이브러리는 확장성 개념을 가진 아티팩트이다. 이들은 공통으로 사용되는 기본 라이브러리와 함께 미리 정의된다. 라이브러리는 타입들의 제어, 그리고 웹 어플리케이션에 의해 사용되도록 묶어놓았다. 동시에 미리 정의된 sap.m이라는 라이브러리와 나란히 쓰고 사용하기 쉽게 만들어 첫번째로 커스텀 ui 라이브러리로 취급된다.
UI element는 사용자 인터페이스가 기본으로 되어 있으며 속성, 이벤트, 메소드, 관계와 같은 엔티티들을 재사용하며 최고로 중요한 관계는 다른 UI 엘리먼트들을 트리 구조로의 방식으로 생성될 수 있게 집계한다.
개발자가 보는 화면의 관점으로부터 제어 요소는 가장 중요한 아티팩트이다. 이것은 사용자 화면에 나타내는 모든 컨트롤을 제어하는 객체이며 이것은 각 트리구조의 루트로 사용될 수 있는 유저인터페이스 요소의 종류이다. 이 방식은, 특히 렌더링을 위한 엔트리 포인트을 제공한다. 이들은 루트로서 사용될 수 없는 트리구조의 비제어 요소들이다. 그러나 단지 몇몇 부분은 종속적으로 사용된다.
데이터 타입들은 메타 모델 안의 첫번째 클래스로 정의된 엔티티들이다. 타입 시스템의 확장성과 라이브러리 교차타입을 재사용하도록 허락한다. sap.ui.core 라이브러리는 다른 라이브러리들에 사용될 수 있는 타입으로 미리 정의한다.
Bootstrapping
브라우저 실행되자마자 스크립트를 로드함으로 SAP UI5 실행이 자동으로 페이지에 실행한다. 단순한 SAP UI5 기본 설치를 사용하는 경우에 UI를 실행하기에 충분하다. 이외에도 세팅을 통해 너의 어플리케이션 테마나 라이브러리를 설정할 수 있다.
<script id="sap-ui-bootstrap" type="text/javascript"
src="resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize" // 기본 테마 설정
data-sap-ui-libs="sap.m" // 라이브러리 설정
data-sap-ui-compatVersion="edge">
</script>
SAP UI5는 여러 부트스트랩을 제공하고 있다.
sap-ui-core.js : 기본 부트스트랩 파일이며, SAP에서 이 타입을 쓸 것을 추천한다. 이것은 이미 jquery 와 core library의 최소한의 요구를 이미 정의하고 있다. 파일 요구는 XHR 을 다양하게 로드된다.
Content Delivery Network (CDN) : CDN을 이용해서 다양한 라이브러리를 엑세스 할 수 있다.
sap-ui-core-nojQuery.js : SAP 제공 jquery가 아닌 다른 버전의 jquery를 실행할 때 사용한다.
sap/ui/core/library-preload.js : 미리 파싱해놓고 필요할 때 호출한다.
sap-ui-core-lean.js : jquery나 sap ui5 즉시 로드되고 다른 파일은 다이나믹하게 로드된다.
Components
SAPUI5 어플리케이션의 컴포넌트는 독립적이며, 부분 재 사용 가능하다.
SAP UI5는 어플리케이션이 동작하는 곳으로 부터 다른 위치의 컴포넌트 사용할 수 있다.
따라서 컴포넌트는 다른 팀과 협업적으로 사용할 수 있다.
캡슐화 지원으로 어플리케이션 구조와 코드를 더 쉽게 이해할 수 있다.
SAP UI5는 두 가지 컴포넌트 타입을 지원한다.
Faceless components (class: sap.ui.core.Component)
유저 인터페이스를 사용하지 않으며, UI 엘리먼트가 필요하지 않은 곳에서 코딩이 사용된다. faceless 컴포넌트들은 ComponentContainer에 추가할 수 없다.
UI components
Component들을 상속받으며 컴포넌트의 기술적인 렌더링을 추가한다. 유저인터페이스의 각각의 설정, 메타데이터에 따라 화면 영역이나 요소들을 나타낸다.
sap.ui.core.UIComponent는 sap.ui.core를 상속받으며 기능요소들을 렌더링한다.
컴포넌트는 sap.ui.component란 팩토리를 통해 생성되고 불러온다.컴포넌트들은 에 지정된 디스크립터(manifest.json)를 통해 불러올 수 있다. SAP는 디스크립터를 통해 컴포넌트 파일들을 호출하는 것을 추천한다. 프로세스가 로딩 중에 최적화 되어 퍼포먼스를 증가시키기 때문이다. 디스크립터는 메타데이터 요소들을 포함하며 또한 의존성과 설정에 대한 구성을 표현한다. 컴포넌트의 필수와 옵션 리소스는 구성요소의 네임스페이스에 조직적이여야 된다.
디스크립터를 로딩한 후에 컴포넌트 팩토리는 의존된 라이브러리나 컴포넌트들을 평행화 한 후 로드할 수 있다.
Component 구조
컴포넌트는 유니크한 네임스페이스로 조직적이며, 컴포넌트의 네임스페이스가 컴포넌트 이름과 동일하다.
기본적으로 컴포넌트는 컴포넌트컨트롤러와 디스크립터로 구성한다. 컴포넌트컨트롤러는 필수지만, SAP 는 디스크립터도 또한 사용하길 추천한다.
디스크립터는 컴포넌트 메타데이터를 포함하며, 또한 컴포넌트의 의존성, 설정을 나타낸다. 컴포넌트의 모든 구성요소들은 Component.js와 manifest.json이 나타낸다.
SAP UI5에서의 다른 개념
복합 제어
두 개념은 싱글 인터페이스 뒤에서 제어하는 걸 제공한다. 복합 제어는 컨트롤러 내에서 재사용을 목적으로 하며, 컴포넌트는 응용프로그램 개발을 재사용으로 하며 복합제어는 기존 제어를 포함한다.
UI 라이브러리
컨트롤 주위에 배포 가능한 단위이다. 컨트롤은 전혀 독립적이지 않고 제어 라이브러리의 부분으로서 배포된다. 그러나 컴포넌트는 자체 포함되므로, 컨트롤들을 배포하는데 사용하면 안된다.
MVC
MVC 개념은 뷰, 컨트롤러를 정의해 부품을 구조화하여 재사용되게 허락한다. MVC는 따로따로 배포되며, 스타일과 스크립트의 정의하는 것에 의미가 없기에 이 컨셉은 다른 어플리케이션에서의 사용을 제한한다.
MVC
SAP UI5 에서 model, View, Controller개념은 표현을 구분하며 사용자와 상호작용을 하기 위해 사용된다. 이 구분은 개발과 부분 독립적 변화에 용이하다.
view는 UI에 렌더링하는 일을 담당한다.
model은 어플리케이션 데이터를 관리한다.
controller는 view와 model에 대한 이벤트에 반응하고 변화를 상호작용한다.
UI에 안에 데이터를 바인딩 하는 목적은 사용자 인터페이스, 어플리케이션에 의한 데이터 노출, 데이터 프로세스에 의한 비즈니스 로직 코드에 대한 정의를 분리하기 위해서다. 분리를 통해서 가독성, 유지보수성, 확장성을 제공한다. 그리고 비즈니스 로직을 건들지 않고 view를 바꾸며 동일 데이터에 대해 여러 view를 분리한다.
View와 Controller는 1:1 매핑관계이다. 그러나 이것은 컨트롤러 단에서는 관계를 유지하지 않아도 된다. 이러한 컨트롤러는 어플리케이션 컨트롤러이다. 기술적 관점에서는 SAPUI5 모델을 제어하거나 상속할 수가 있다.
View와 Controller는 재사용이 가능하며 지원이 매우 좋다.
프로세스 초기화
SAP 안에 있는 JQuery 플러그인은 모듈화 개념, 로깅 프레임워크, 성능 측과 같은 기능을 제공한다.
global 오브젝트인 sap가 정의된다.
sap.ui.core.Core 클래스가 모든 의존성들과 함께 실행된다.
실행 설정이 다른 소스로부터 결정된다.
모든 라이브러리와 모듈 설정은 그들의 의존성들과 함께 호출된다.
테마설정으로 로드된 각각의 라이브러리와 css 파일이 설정된 테마를 로드한다.
모든 라이브러리와 문서가 로드되어 기다릴 때, core에 있는 initEvent 함수가 실행되며 모든 등록된 헨들러들이 실행된다.
초기화 준비
이를 통해 프레임 워크를 초기화 하며 3가지 방법으로 연결할 수 있다.
ComponentSupport 모듈
sap.ui.core.CommponentSupport를 사용하는 것이다. 프레임 워크를 초기화 한 후에 코드를 실행하는 것이다.
ComponentSupport를 통해 UIComponent를 선택하여 실행하는 방법이다.
ComponentSupport를 사용하면 필요한 ComponentContainer를 생성하여 하나 이상의 UIComponent를 정의하는 걸 허락한다.
Standalone data-sap-ui-oninit 모듈
모듈 외에 ComponentSupport 사용은, script 요소의 부트스트랩에서 init을 부를수 있는 데이터 요소로 정의할 수 있다.
oninit 모듈은 프레임워크의 프로세스가 초기화될 때의 타이밍에 로드되고 실행한다. 그 다음 우리는 모듈 안에서 추가적으로 어플리케이션을 실행할 수 있다. ( ex. 새로운 xml View 인스턴스 생성)
추가적으로 전용 init 모듈은 추가 <script> 인라인 태그가 필요하지 않으므로 요구사항에 따라 SAP 사이트에 게시된 CSP(Content Security Policy)를 반영할 수 있다.
attachInit 함수
attachInit 함수는 프레임워크가 초기화 된 후 바로 실행할 함수이다. 이 코드는 스크립트 인라인 태그를 나타내는 main HTML 파일안에 쓰여질 수 있다.
부트스트래핑중 추가 리스소 로딩
/<context-path>/resources/<library-name>/library.js
sap.ui.getCore().initLibrary 메서드를 호출하여 빌드 중 파일이 자동생성되게한다.
` //resources//themes//library.css`
css 또한 이 경로를 통해 실행한다.
라이브러리 동적 로드
sap.ui.getCore().loadLibrary() 메서드를 통해 런타임에 라이브러리를 로드한다.
이렇게 동적으로 라이브러리를 추가했을 땐, document.ready 이벤트 후에 엑세스가 가능하다.
SAP UI5 구성도
manifest.json
sap에선 이 파일을 app 세팅 설정이나 app이 실행되기에 필요한 중요 정보를 모아놓으라고 추천한다.
이러한 접근을 사용하는 것은 어플리케이션 코드를 작성하기 필요한 셋팅을 미리 준비하거나, manifest.json에 미리 인스턴스화하여 앱 실행 전에 미리 정보를 엑세스 할 수 있다.
몇몇 속성은 각각 SAPUI5 버전에 필요 정보의 목적이며, 외부 구성요소와 어플리케이션을 정확하게 통합하지만, 대부분의 속성들은 실제로 최소한의 UI5 version에 필요하도록 설정이 사용된다.
Root View (App.view.xml)
App.view.xml는 app에서의 root view를 정의한다. 대부분의 경우 App Control 또는 root control로서의 SplitApp control에 포함한다.
SAP UI5는 다양한 뷰타입들을 지원한다. 컨트롤러 파일 안에 정의된 View로부터 컨트롤러 로직을 작동하기 위해 SAP에선 XML을 추천한다. ex) App.controller.js
Component.js
Component.js 파일은 앱 설정을 위한 파일이며 컴포넌트가 인스턴스화 되었을 때 SAPUI5에서 자동으로 init 함수가 실행된다.
(우리가 생성하는 Component는 UIComponent에 상속되어있으며, 만약 init을 오버라이딩 하겠다면, 너는 라우터의 초기화와 UIComponent의 init 함수가 확실히 불려져야 한다.)
컴포넌트의 metadata 섹션에서는, 디스크립터 참조를 정의하며, 컴포넌트가 인스턴스화 되었을 때, 이 디스크립터는 자동으로 로드된다.
HTML Page
App for FLP
모든 앱은 SAP UI5 그리고 그 요소를 사용하는 HTML 페이지로 시작된다. 그리고 이는 FLP 와 standalone app 라는 2가지 옵션이 있다.
FLP는 디스크립터 파일에서 주어진 정보를 기반으로 요소들을 인스턴스화한다. FLP는 같은 시간에 여러 앱들을 포함하며, 각각의 앱들은 각자의 테마와 지원장치들을 각각 로컬 셋팅으로 정의할 수 있다.
Standalone app
standalone을 실행하길 원하면 component.index.html을 인스턴스한 파일을 생성하는 것이 필요하다.
순서
SAP UI5 Tutorial 참조 사이트 (hello world 호출 까지의 과정))
ui5 serve 명령어를 실행하면 제일 먼저 index.html 파일을 로드한다.
1. index.html
(html content ...)
<script
id="sap-ui-bootstrap"
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-libs="sap.m"
data-sap-ui-compatVersion="edge"
data-sap-ui-async="true"
data-sap-ui-resourceroots='{"sap.ui.demo.walkthrough": "./"}'
data-sap-ui-oninit="module:sap/ui/demo/walkthrough/index">
</script>
(html contents ...)
<body class="sapUiBody" id="content">
(html contents ...)
index.html에 설정 해 놓은 <script> 태그 내 실행, 속성 값
src : SAP UI5 에서 지원하는 CDN 경로 호출
theme : ui에 사용할 테마 설정
libs : 기본으로 사용할 라이브러리 설정
compatVersion : 호환성 정의
async : 비동기 설정 유무
resource-root : 네임스페이스 경로 alias 설정
oninit : 첫 시작 모듈 설정
data-sap-ui-resourceroots='{"sap.ui.demo.walkthrough": "./"}'
data-sap-ui-oninit="module:sap/ui/demo/walkthrough/index
sap/ui/demo/walkthrough 경로를 ./ 로 설정하였고,
oninit 속성에 따라 페이지 실행 시 현재 파일 경로에 있는 index.js 파일을 실행한다.
2. index.js
sap.ui.define([
"sap/ui/core/mvc/XMLView"
], function(XMLView) {
'use strict';
XMLView.create({
viewName: "sap.ui.demo.walkthrough.view.App"
}).then(function(oView) {
oView.placeAt("content");
});
});
);
sap.ui.define을 통해 호출 시, 정의할 내용을 입력한다.
sap/ui/core/mvc/XMLView는 core.js에서 지원하는 XMLView 타입을 선언하고
함수를 통해 XMLView를 생성하게 한다.
create 함수로 viewName 을 정의하며 sap.ui.demo.walkthrough.view.App 경로에 정의한 App 이란 이름의 빈 xml을 생성한다.
3. App.View.xml
<mvc:View
controllerName="sap.ui.demo.walkthrough.controller.App"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Button
text="click"
press=".onShowHello"/>
</mvc:View>
View와 Controller는 1:1 매핑이므로 controllerName으로 App.controller.js를 매핑하며
App.view.xml이 페이지에 로드되었을 때 App.controller.js 가 인스턴스화된다.
4. App.Controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller",
.
. 정의 타입들
.
], function ( 정의 타입들) {
"use strict"; // 코드 검사를 강화시켜 스크립트 오류 방지
return Controller.extend("sap.ui.demo.walkthrough.controller.App", {
onInit : function () {
... Controller 호출 시 실행될 코드
},
onShowHello : function () {
MessageToast.show("Hello World");
}
});
});
Controller에서도 각종 sap 지원 기능 태그들을 정의해주며
함수를 통해 Controller.extend 를 호출하면서 각종 XML 출력에 필요한 로직들을 진행한다.
생각
잠깐 동안 UI5 만졌을 땐, 현 웹에서 사용하는 구조와 크게과 다르지 않았으며, 눈에띄는 차이는 view에서 xml을 호출할 때 Controller를 인스턴스화 하는 방식에 조금 신기했었다. hello world 출력을 기반으로 아직은 장단점에 대해 크게 파악하기 어려웠다,
제가 생각하는 장점에선 SAP UI5가 고퀄리티 프레임워크라 느꼈던 것이 각 하나하나의 View의 태그 생성하는 과정에 대해 디테일한 설정을 가져갈 수 있다는 것과 프론트 단 부분을 sap.m 같은 라이브러리를 통해 대부분 구성해 놓은 것을 사용할 수 있다는 점에서 UI5를 만들 때 신경을 많이 두었다는 느낌이 들었다.
또한 설정에 대한 내용을 디스크립터에 넣는 방식으로 일일히 설정에 대해 파고 들어갈 이유가 없었던 것도 좋은 방식이라 생각한다.
허나 sap로 만든 사이트중 하나를 들어가 봤는데 페이지 로딩 속도가 느렸다. 제가 생각하는 이유는 UI5도 하나의 프레임워크 방식이기에 페이지 로딩 방식이 고정되어 있어, javascript의 DOM을 BOM을 받아 파싱해서 쉽고 빠르게 화면에 뿌려주는 것을 생각하면 템플릿에 맞춰야 하는 상황이 느릴 수도 있다 생각한다. 또한 View 타입의 형태에 맞춰 Controller를 진행하는 과정이 끝날 때까지 기다려서 출력하는 구조라고 생각한다. 그렇기 에 때문에 대표님께서 말씀하신 NextJs 같이 SSR을 통해 템플릿을 프리로딩하고 이후 CSR을 진행하는 방식을 접했을 때 어느 정도까지의 퍼포먼스를 보여줄 수 있을 지 궁금하다.
-
OData V2 Model
oData V2 model
oData V2 모델을 사용하면 oData Service로 부터 데이터 제어를 가져올 수 있다.
oData V2 모델은 서버측의 모델이며 데이터 셋은 단지 서버에서 사용가능하고 클라이언트들은 단순히 요청된 데이터만 알 수 있다. 각 정렬과 필터링 작동은 서버 위에 올라간다. 클라이언트는 서버에 요청을 보내고 응답한 데이터를 보인다.
백앤드 요청은 바인딩 목록, 컨텍스트 바인딩들, 그리고 CRUD 함수들이 ODataModel에 의해 제공된다. 속성 바인딩은 요청이 되지 않는다.
OData model은 현재 2.0 버전까지 지원한다.
2가지 버전의 OData 모델은 sap.ui.model.odata.ODataModel 과 sap.ui.model.odata.v2.ODataModel을 참조한다. v2.ODataModel은 기능이 더해졌고, 참조된 이 모델 안에서 새로운 기능을 참조만 할 것이다. sap.ui.model.odata.ODataModel은 지원되지 않으며 SAP는 v2.ODataModel 만 사용할 것을 추천한다.
기능
v2.ODataModel
ODataModel
OData version 지원
2.0
2.0
JSON format
Yes
Yes
XML format
Yes
Yes
양방향 바인딩 지원
속성 변경만 가능
하나의 엔티티 속성만 동시변경 가능
기본 바인딩
단방향
단방향
클라이언트 사이드 정렬, 필터링
Yes
NO
batch
모든 요청들은 배치될 수 있다.
단지 메뉴얼 배치 요청만 가능
데이터 캐시
모델 안의 모든 데이터가 캐시된다.
메뉴얼적 요청된 데이터는 캐시되지 않는다.
자동 새로고침
yes
yes
메세지 핸들링
yes
no
다른 도메인이나 사이트에서의 백앤드가 엑세스 하는 것을 막는 Same-Origin-Policy 보안개념을 유의해야한다.
패치 데이터의 서비스 요청들은 데이터 바인딩을 제어를 위해 정의를 기반으로하여 자동으로 만들어졌다.
ODataModel 생성자
new sap.ui.model.odata.v2.ODataModel(vServiceUrl, mParameters?)
vServiceUrl : 추가된 URL 파라미터들은 모든 요청을 덧붙일 것이다.
만약 오브젝트를 전달하고 싶을 땐, 이것은 두번째 파라미터 오브젝트로써 해석될 것이다. 그땐 mParameter.serviceUrl은 필수 파라미터가 된다.
모델 인스턴스 생성
하나의 OData 모델 인스턴스는 단지 하나의 OData service를 커버할 수 있다.
다양한 서비스를 엑세스 하기 위해선 다양한 OData Model을 인스턴스 해야한다.
// "ODataModel" required from module "sap/ui/model/odata/v2/ODataModel"
var oModel = new ODataModel("http://services.odata.org/Northwind/Northwind.svc/");
var oModel = new ODataModel({serviceUrl: "http://services.odata.org/Northwind/Northwind.svc"});
ODataModel 인스턴스를 생성할 때 서비스 메타데이터를 검색하라는 요청이 전송된다.
Service Metadata
서비스 메타데이터는 서비스 URL 별로 캐싱된다. 같은 서비스에서 사용하는 여러 OData 모델은 이 메타데이터를 공유할 수 있다.
첫번째 모델 인스턴스만 메타데이터 요청을 작동한다. JSON 서비스 메타데이터 응답은 getServiceMetadata() 라는 OData 모델 인스턴스의 메소드를 호출함으로 엑세스 될 수 있다.
var oMetadata = oModel.getServiceMetadata();
v2.ODataModel 안에서 서비스 메타데이터는 비동기식으로 불러지며 동기식으로 불러올 수 없다. 로딩이 끝났을 때, metadataLoaded 이벤트가 붙는다.
추가 URL 파라미터
OData Service는, URL 파라미터를 설정해서 사용할 수 있다. UI5는 각각의 바인딩에 따라서 파라미터들을 자동적으로 설정한다.
인증토큰이나 일반 설정 옵션들 같은 경우엔 요청 URL에 요소들을 추가할 수 있다.
몇몇 파라미터들은 모든 요청에 포함되선 안되며, $expand 또는 $select와 같은 특정 목록이나 컨텍스트 바인딩에 추가되어야한다. 바인딩 메서드는 특별한 바인딩을 위한 모든 요청을 포함할 수 있는 파라미터 맵을 전달하는 옵션을 제공한다. OData 모델은 현재 $expand와 $select를 지원한다.
URL 파라미터들을 요청하는데 2가지 방법이 있다.
파라미터들을 service URL에 추가
new ODataModel("http://myserver/MyService.svc/?myParam=value&myParam2=value");
이 파라미터들은 OData 서버에 보내지는 모든 요청이 포함될 것이다.
mparameter map과 함께 URL 파라미터 전달
$metadata 요청(metadataUrlParams)이 사용되며 게다가 데이터 요청에 포함된 URL 파라미터(serviceUrlParams)를 전달할 수 있다. 파라미터들은 Map 형식으로 보내진다.
// "ODataModel" required from module "sap/ui/model/odata/v2/ODataModel"
var oModel = new ODataModel({
serviceUrl: "http://services.odata.org/Northwind/Northwind.svc",
serviceUrlParams: {
myParam: "value1",
myParam2: "value2"
},
metadataUrlParams: {
myParam: "value1",
myParam2: "value2"
}
});
HTTP HEADER
각 요청에 보내어지는 커스텀 헤더를 추가할 수 있다.
맵 파라미터와 함꼐 커스텀 헤더로 전달
var oModel = new sap.ui.model.odata.v2.ODataModel({
headers: {
"myHeader1" : "value1",
"myHeader2" : "value2"
}
});
모델 인스턴스를 글로벌 커스템헤더로 셋팅
oModel.setHeaders({"myHeader1" : "value1", "myHeader2" : "value2"});
커스텀 헤더 추가 시 이전 모든 커스텀 헤더들은 지워진다. 일부 헤더들은 private이며 즉, OData모델 내부적으로 설정되어 설정할 수 없다.
엔티티 주소 : 바인딩 경로 구문
OData 모델의 바인딩 경로 구문은 특정 엔티티들이나 엔티티 집합을 엑세스한 OData 안의 사용되는 서비스 URL과 상대적 URL 경로와 일치한다.
서비스의 메타데이터 안에 정의된 OData 서비스의 구조에 따른 OData에 의해 제공되어진다.
필터들과 같은 URL 파라미터들은 바인딩 경로를 추가할 수 없다. 바인딩 경로는 절대적이거나 상대적이다. 절대적 바인딩 경로는 즉시 리졸브 되지만, 상대경로는 절대경로에서 절대적 바인딩 경로를 자동적으로 전환될 수 있을 때 리졸브 될 수 있다. 만약 예를 들어 속성이 상대 경로에 바인딩 되고 그 부모 제어가 절대적 경로에 바운드 된다면, 상대적 경로는 절대적 경로를 통해 해결될 수 있다.
OData Model 내 바인딩 샘플은 Northwind 데모 서비스에서 부터 가져온다.
절대적 바인딩 경로
"/Customers"
"/Customers('ALFKI')/Address"
컨텍스트에서 확인할 수 있는 상대적 바인딩 경로
"CompanyName"
"Address"
"Orders"
전체 경로
"/Customer('ALFKI')/CompanyName"
"/Customer('ALFKI')/Address"
"/Customer('ALFKI')/Orders"
단일 엔티티 또는 엔티티들의 컬렉션이 사용되는 탐색 속성
"/Customers('ALFKI')/Orders"
"/Products(1)/Supplier"
OData Model로부터 데이터 엑세스
OData 서비스에서 요청한 데이터는 OData 모델에 캐시된다. getProperty() 메서드에 의해 엑세스 될 수 있으며, 엔티티 객체 또는 값을 리턴한다. 이 메서드는 백앤드로부터 요청한 데이터가 아니며, 단지 미리 요청되거나 캐시된 엔티티들로 엑세스 될 수 있다.
oModel.getProperty("/Customer('ALFKI')/Address")
이 메소드들로 싱글엔티티들과 속성들을 엑세스 할 수 있다. 엔티티 집합들을 엑세스하면, 바인딩 목록으로 읽기 전용 엔티티들을 바인딩 컨텍스트로 가져올 수 있다. 이 메서드가 리턴한 값들은 JSON 모델 안에서 참조된 것이 아닌 카피된 데이터이다.
엔티티 생성
createEntity() 메서드를 호출하면 명시된 엔티티 집합에 대한 엔티티들을 호출한다. 이 메서드는 새로 만들어진 엔티티를 가리키는 컨텍스트 객체를 반환한다.
이 어플리케이션은 이러한 객체들을 바인딩 할 수 있고 양뱡향으로 데이터들을 변경할 수 있다. submitChanges() 라고 불리는 메서드를 호출하면, OData 백앤드 안에 엔티티들을 저장한다. 변경을 초기화하려면 deleteCreateEntry() 함수를 부르면 된다.
var oContext = oModel.createEntry(
"/Products",
{ properties:
{ ID:99,
Name:"Product",
Description:"new Product",
ReleaseDate:new Date(),
Price:"10.1",
Rating:1
}
}
); // 명시된 속성들과 값을 가진 제품들의 엔티티 생성
oForm.setBindingContext(oContext); // 엔티티 바인딩
oModel.submitChanges({success: mySuccessHandler, error: myErrorHandler}); // Odata 백앤드 안에 엔티티 저장
entity oModel.deleteCreatedEntry(oContext); // 변경 취소 시 호출
만약 엔티티들을 submit 하면 컨텍스트는 모델에 import 된 새로운 데이터를 모델로 가져온다. 새로운 엔티티를 가리킨다.
CRUD 작동
OData 모델은 OData Service에서 메뉴얼적인 작업을 허용한다. 메뉴얼적 작업이 데이터를 반환할 때는 Odata 모델 데이터의 캐시가 import 된다. 모든 작업들은 필수적 sPath 파라미터와 mParameters Map을 선택적으로 요구한다.
또한, 생성, 수정 메소드들은 데이터 객체가 생성되거나 변경될 때 OData 파라미터를 필수적으로 요구한다. 각각의 작업은 요청 중단을 사용할 수 있는 중단함수를 포함한 객체를 반환한다. 만약 요청이 중단되어 에러 핸들러가 호출된다면, 이것은 성공 또는 에러 핸들러가 모든 요청에 대해 호출되도록 보장한다. 이것은 또한 헤더 데이터, URL 파라미터, 또는 eTag에 대해 추가적으로 보낼 수 있도록 한다.
엔티티들 생성
create 함수는 OData 모델의 생성이 명시된 OData service의 POST 요청을 작동시킨다. 어플리케이션은 새로운 엔티티와 생성된 엔티티 데이터를 엔티티 셋으로 가진다.
var oData = { ProductId: 999, ProductName: "myProduct" } // 엔티티 데이터
oModel.create("/Products", oData, {success: mySuccessHandler, error: myErrorHandler}); // 엔티티들을 create()를 통해 명시된 모델로 생성한다.
엔티티 읽기
read 함수는 GET 요청으로 작동된다. 이 경로는 OData model에 의해 생성에서 명시된 OData service로 부터 가져온다. 이 가져온 데이터는 성공여부에 따라 success handler와 error handler로 나뉜다.
oModel.read("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
엔티티 수정
update 함수는 PUT/MERGE 요청으로 작동하며 성공적으로 요청이 된 후에는 model 안에서 refresh 된 데이터를 자동으로 가져온다.
oModel.update("/Products(999)", oData, {success: mySuccessHandler, error: myErrorHandler});
엔티티 삭제
delete 함수는 DELETE 요청으로 작동하며, 어플리케이션에서는 삭제해야할 엔트리의 경로를 명시해야한다.
oModel.remove("/Products(999)", {success: mySuccessHandler, error: myErrorHandler});
변경 후 refresh
모델은 변화된 엔티티들을 의존하여 자동적으로 refresh를 바인딩하는 메커니즘을 제공한다. 만약 create, update, remove 함수를 수행하면, 그 고유의 모델은 바인딩을 확인하며 이를 위해 refresh를 작동한다. 만약 그 모델이 배치모드에서 실행되면, 새로고친 요청은 같은 배치 요청 안의 변화들과 함께 묶이게 된다. setRefreshAfterChange(false) 호출에 의해 자동으로 비활성화 할 수 있다. 만약 자동 고침이 비활성화 되었을 때, 어플리케이션은 각각의 바인딩들을 새로고쳐야 한다.
자식 엔티티 생성
부모 엔티티에 새로운 자식 엔티티 생성은 한번의 싱글 API 요청으로 가능하지 않다. 이를 만들려면 부모, 자식의 생성 엔티티 API 호출을 연결하면 만들 수 있고 아래의 샘플은 판매 주문과 주문 항목을 모두 생성한다.
var oParentContext, oModel = this.getView().getModel();
oParentContext = oModel.createEntry("SalesOrderSet", { //부모 엔티티 생성
properties : {
// 새로운 판매 주문을 위한 속성
},
success : function () { // 부모(oParentContext)가 생성되었을 때
oChildContext = oModel.createEntry("ToLineItems", { //자식 엔티티 생성
context : oParentContext, // context에 부모의 컨텍스트 저장
properties : {
// 새로운 판매 주문에 의한 새로운 아이템들의 속성
},
success : function () {
// ...
}
});
oModel.submitChanges(); // OData 백앤드에 저장
}
});
oModel.submitChanges(); // OData 백앤드에 저장
동시성 제어 와 ETags
OData는 optimistic한 동시성 제어를 HTTP ETags를 사용한다. ETag는 모든 CRUD 요청을 위해서 parameter map으로 전달될 수 있다. 만약 ETag가 전달되지 않으면, 미리 로드된 경우, 캐시된 엔티티가 사용된다.
XSRF Token
CSRF 를 해결하기 위해 OData 서비스는 데이터 변경 시 XSRF 토큰을 필요로 한다. 이 경우 서버에서 가져온 토큰을 같이 서버에 보내야 하며 OData 모델은 메타데이터를 읽을때, XSRF 토큰을 가져와 header에 넣어 자동으로 토큰을 전송한다. 토큰이 유효하지 않을때엔, OData Model에서 refreshToken 함수를 호출하여 패치될 수 있다. 이 토큰은 루트 URL 서비스의 호출과 함께 가져오며 서비스 다큐먼트와 함께 응답한다. 유효한 토큰을 가져오려면 응답이 캐싱되지 않았는지 확인해야한다.
모델의 새로고침
refresh 함수는 OData 모델의 모든 데이터를 새로고침한다. 각각의 바인딩은 서버로부터 데이터를 다시 읽는다. 리스트 또는 컨텍스트 바인딩 경우, 백앤드에서 새로운 요청이 작동된다. 만약 XSRF 토큰이 유효하지 않으면, 서비스 다큐먼트에서 읽기 요청을 다시 가져와야 한다. 메뉴얼 CRUD 요청들로 임포트된 데이터는 자동으로 리로드 되지 않는다.
배치 프로세싱
ODataModel v2는 2가지 다른 방법으로 batch processing을 지원한다.
기본 : 쓰레드안에 모든 요청들은 배치 요청들로 수집되고 다뤄진다. 즉, 요청은 현재 스택이 종료된 후에 타임아웃 요청을 보낸다. 이것은 바인딩에 의해 작동된 요청들로부터 모든 메뉴얼적 CRUD 요청들을 포함한다.
지연 : 요청들은 저장되며 어플리케이션의 의해 호출되는 submitChanges() 과 함께 submit 되어질 수 있다. 이것은 또한 바인딩으로 부터 요청들이 작동될 뿐만 아니라, 모든 메뉴얼적 CRUD 요청들이 포함된다.
모델이 요청을 다루는 방법은 결정할 수 없다. 그래서 SAPUI5는 groupId를 제공한다. 각각의 컨텍스트와 바인딩 리스트 그리고 각각 메뉴얼 요청들에 그룹 아이디를 지정할 수 있다. 같은 그룹안에 속하는 모든 요청들은 하나의 요청 안에서 다뤄질 수 있다. 그룹아이디가 없는 요청은 기본 배치 그룹 안에서 다뤄진다. 변경을 위해 chageSetId 를 사용할 수 있다. 같은 changeSetId에 포함된 각 변경들은 배치 요청안에서 하나의 changeSet으로 다뤄진다. 기본적으로, 모든 변화들은 고유의 changeSet을 가진다.
setDefiredGroup() 메서드를 사용하여 이전에 정의된 그룹의 서브셋을 지연으로 설정할 수 있다. (setChangeGroups() , getChangeGroups() 에도 동일 시 적용)
그룹에 속한 모든 요청들은 요청 queue안에 저장된다.
지연된 배치 그룹은 submitChanges() 메소드를 통해 메뉴얼적으로 전송되어야한다. 만약 submitChanges()를 호출할 때 배치 그룹아이디를 명시하지 않으면, 모든 지연된 배치 그룹들이 전송된다.
- 그룹들의 서브셋을 지연으로 설정
var oModel = new ODataModel(myServiceUrl);
- 바인딩한 groupId를 전송한다.
{path:"/myEntities", parameters: {groupId: "myId"}}
- 지연한 groupId 설정
1. 그룹 가져오기
var aDeferredGroups = oModel.getDeferredGroups();
2. 리스트에 그룹 아이디 추가
aDeferredGroups = aaDeferredGroups.concat(["myId"]);
3. 지연된 모든 그룹들을 설정
oModel.setDeferredGroups(aDeferredGroups);
4. 지연된 모든 그룹 전송
oModel.submitChanges({success: mySuccessHandler, error: myErrorHandler});
양방향 바인딩
v2.oDataModel은 양방향 바인딩을 지원한다. 기본적으로 모든 변경들은 지연으로 설정된 changes 라는 배치그룹에 수집된다.
changes 로 전송하려면, submitChanges() 함수를 사용한다. changes 데이터는 복사 데이터로 만들어진다. 이것은 이전 데이터를 가져와 백앤드에 새 요청을 보내지 않고도 changes를 재설정 할 수 있다. resetChanges() 써서 모든 changes 저장공간을 리셋할 수 있다. 또한 엔티티 경로 배열이 있는 resetChanges() 함수를 호출하여 특정 엔티티만 리셋할 수 있다.
model에서 setChangeGroups() 를 사용하면, 다른 배치 그룹안에 있는 다른 엔티티들 또는 타입들을 위한 changes도 수집할 수 있다.
var oModel = new ODataModel(myServiceUrl);
oModel.setDeferredGroups(["myGroupId", "myGroupId2"]);
oModel.setChangeGroups({
"EntityTypeName": {
groupId: "myGroupId", //
changeSetId: "ID", // 선택사항 (Id 변경)
single: true, /*optional, can be true or false*/
}
});
oModel.submitChanges({groupId: "myGroupId", success: mySuccessHandler, error: myErrorHandler});
같은 배치 그룹 안에서 모든 엔티티 타입을 위한 changes 수집은 ‘*‘를 EntityType으로 사용한다. 만약 change가 지연으로 설정하지 않으면, changes는 즉시 백앤드에 보내어진다. 단일 파라미터를 true 또는 false로 설정에 의해 , 고유의 change 설정 안에 각각의 change 결과들이거나 changes가 하나의 change 설정을 수집할 것인지를 정의한다. 모델은 단지 single의 설정이 false 일 때만 changeSetId를 처리한다.
var oModel = new ODataModel(myServiceUrl);
oModel.setProperty("/myEntity(0)", oValue);
oModel.resetChanges(["/myEntity(0)"]);
바인딩별 매개변수
OData 프로토콜은 명시적으로 다른 URL 파라미터이다.
위에서 설명한 파라미터들 뿐만 아니라 바인딩 안에 파라미터들을 사용할 수 있다.
Expand parameter
확장 파라미터는 어플리케이션에서 그들의 네비게이션 속성들과 함께 연결된 엔티티들을 읽는 걸 허락한다.
oControl.bindElement("/Category(1)", {expand: "Products"});
oTable.bindRows({
path: "/Products",
parameters: {expand: "Category"}
});
예로 Category(1) 의 모든 제품들은 서버 응답안에 inline으로 내장되어 하나의 요청만 부른다. 모든 Products 위한 카테고리는 각각의 product 위한 응답에 inline으로 내장되어있다.
Select parameter
선택 파라미터는 어플리케이션이 엔티티를 요청할 때 읽는 속성들의 서브셋으로 정의하는 것을 허락한다.
oControl.bindElement("/Category(1)", {expand: "Products", select: "Name,ID,Products"});
oTable.bindRows({ path: "/Products", parameters: {select: "Name,Category"} });
예를 들어 Name, ID 그리고 ofCategory(1) 속성 뿐만 아니라 제품에 내장된 모든 속성들도 응답한다. 이 속성과 카테고리는 각각의 제품에 포함되며 카테고리 속성은 관련된 카테고리 항목에 대한 링크를 포함한다.
Custom query options
서비스 작동을 위한 input 파라미터로서 커스텀 쿼리 옵션들을 사용할 수 있다. list 바인딩이 생성될 때, 커스텀 파라미터를 명시한다.
oTable.bindRows({
path: "/Products",
parameters: {
custom: { // 커스텀 파라미터 명시
param1: "value1",
param2: "value2"
}
},
template: rowTemplate
});
만약 bindElement()를 사용하면, 아래와 같이 파라미터를 명시할 수 있다.
oTextField.bindElement("/GetProducts", {
custom: {
"price" : "500"
}
});
바인딩 의존 최적화
OData V2 모델 에서 생성자는 preliminaryContext 라 불리는 flag를 지원한다. 이 옵션이 true 이면, 모델은 더 적은 $batch 요청 종속적 바인딩에 대한 OData 호출을 다를 수 있게 된다.
소개
부모 바인딩이 의존 바인딩에 설정된 컨텍스트에 해당하는 OData 엔티티를 읽는다면 다른 바인딩(부모바인딩) 에 의존한다.
기본적으로 종속 바인딩을 위한 데이터는 바인딩 컨텍스트를 위한 데이터를 부모 바인딩를 통해 읽은 후에만 읽는다.
부모 바인딩이 컨텍스트 바인딩인 경우, 2개의 읽기 요청을 하나의 번들링으로 퍼포먼스를 증가시킬 수 있다. 바인딩과 연결된 단일 컨텍스트는 예비의 컨텍스트를 명시하여 수행할 수 있다. 이를 위해 부모 바인딩 구성에 createPreliminaryContext 파라미터 설정이 필요하다. 의존적 리스트 또는 컨텍스트 바인딩은 데이터를 읽기 전에 preliminaryContext 경로를 사용하여 데이터를 읽어 그들 자신의 요청을 위한 경로 구성을 주문 할 수 있다. 의존 바인딩의 생성을 usePreliminaryContext 파라미터를 설정해 수행한다.
설정과 사용
OData V2 model을 생성할 떄 preliminaryContext 파라미터를 설정할 수있다. 이 모델을 위해 생성된 모든 바인딩의 preliminary 컨텍스트를 전환한다.
모든 컨텍스트 바인딩은 createPreliminaryContext 파라미터를 true로 가진다.
모든 컨텍스트 바인딩과 모든 바인딩 목록은 usePreliminaryContext 파라미터를 true로 설정한다.
ODataContextBinding 생성자 또는 ODataListBinding 생성자에 해당하는 파라미터들의 기본을 무시할 수 있다.
게다가 모델에 일반적 preliminaryContext 파라미터를 사용하지 않을 수도 있다. (모든 바인딩에 영향을 미친다)
그러나 이러한 매개변수 사용하는 바인딩 인스턴스와 부모쌍에 대한 preliminaryContext를 바꾼다.
예를 들면, 부모 바인딩인 “/Product{1}” 경로를 가진 컨텍스트 바인딩을 보여준다.(sap.m.Panel 경로로 바인딩된 컨텍스트들을 생성) ‘Supplier’ 경로를 가진 의존적 관련 바인딩은 product의 모든 supplier를 보여주는 테이블로 생성된다. ( sap.ui.table.Table 제어 위한 행 을 생성)
싱글 바인딩 예제
표에 따르면, preliminaryContext들을 사용하지 않고, Binding 0 위한 하나의 요청, 그리고 나중에 Binding 1 위한 하나, 두개 연속으로 OData 요청들이 이슈가 된다.
요청 넘버
요소
1
GET Product(1)
1
GET Product(1)/Supplier
아래에서 보여주는 바인딩 매개변수를 설정하여 요청을 최적화 할 수 있다.
싱글 바인딩 예제 최적화
여기 Binding 1은 Binding 0이 생성될 때 preliminaryContext를 사용하며, 그리고 이와같이 요청 URL 은 다이렉트로 해결될 수 있다.
| 요청 넘버 | 요소 |
|:–:|:–:|
|1| GET Product(1),GET Product(1)/Supplier|
함수 임포트
ODataModel은 callFunction() 메소드에 의해 임포트나 또는 액션 함수들의 부르는 것을 지원한다.
oModel.callFunction("/GetProductsByRating",{method:"GET", urlParameters:{"rating":3}, success:fnSuccess, error: fnError})
만약 callFunction() 요청이 지연되면, submitChangesmethod()를 통해 전송되어 질 수 있다.
파라미터를 가져오는 함수의 바인딩
OData Model V2는 파라미터들을 가져오는 함수에 대해 바인딩을 지원한다. 엔티티 속성에 대해 바인딩을 지원하는 createEntry() 메서드와 비슷하다. callFunction() 메서드는 promise를 가진 핸들을 반환한다. 그 promise는 바인딩된 컨텍스트가 성공적으로 생성되거나 또는 거부될 때 해결된다.
var oHandle = oModel.callFunction("/GetProductsByRating", {urlParameters: {rating:3}});
oHandle.contextCreated().then(function(oContext) {
oView.setBindingContext(oContext);
});
만약 함수가 결과 데이터를 가져오면, 그때 그 결과 데이터는 엑세스 되고 컨텍스트 사용 속성 $result 에 대해 묶인다.
<form:SimpleForm>
<core:Title text="Parameters" />
<Label text="Rating" />
<Input value="{rating}" />
<Button text="Submit" press=".submit" />
<core:Title text="Result" />
<List items="{$result}">
<StandardListItem title="{Name}" />
</List>
</form:SimpleForm>
언어
SAPUI5는 현재 언어의 컨셉으로 사용한다. 이 언어는 자동적으로 OData V2 model에 의해 OData service로 전달된다. 이 이유로 어플리케이션은 언어 자체를 하드 코드로 해서 안된다. 예로 커스텀 쿼리 옵션에 ‘sap-language’ URL 파라미터를 명시해서는 안된다.
OData V2위한 메타 모델
sap.ui.model.odata.ODataMetaModel의 구현은 OData V2 metadata, 그리고 V4 어노테이션 둘다 통일된 엑세스로 제공한다.
메타모델은 기존 sap.ui.model.odata.ODataMetadata 기반으로 사용하며 엔티티나 또는 속성에 직접적으로 상승하는 기존 sap.ui.model.odata.ODataAnnotations로부터의 OData Version 4.0을 병합한다.
기본 구조
sap.ui.model.odata.ODataMetadata 의 기본 구조는 코드 스니핑에 따라서 보여진다. 이것은 엔티티 모델이 중첩되는 가장 중요한 요소가 어떻게 되는지 보여준다.
각각의 요소들은 약간의 네임스페이스로 부터 XML 속성 값의 확장성을 가질 수 있다. 아래 코드스니핑은 이러한 확장성들이 어떻게 저장되고 처리되는 지를 보여준다.
"dataServices": {
"schema": [{
"association": [{
"end": []
}],
"complexType": [{
"property": []
}],
"entityContainer": [{
"associationSet": [{
"end": []
}],
"entitySet": [],
"functionImport": [{
"parameter": []
}]
}],
"entityType": [{
"property": [],
"navigationProperty": []
}]
}]
}
객체, 속성 엑세스
OData 메타 모델 안에 객체들은 배열로 처리된다. 예로 ‘/dataService/schema’는 각각 스키마가 엔티티 타입, 등등의 배열과 함께 엔티티 타입 속성을 가지는 스키마들의 배열이다. 그래서 ‘/dataServices/schema/0/entityType/16’은 ‘MySchema’ 네임스페이스가 스키마 내 이름이 ‘Order’인 엔티티 타입의 경로가 될 수 있다.
그러나 이 경로들은 안정적이지 않다. 만약 낮은 인덱스를 가진 엔티티 타입이 스키마로부터 제거되었을 때, ‘Order’ 경로는 ‘/dataServices/schema/0/entityType/15/’ 로 변한다. index가 변하는 문제를 피하기 위해서 getObject 나 getProperty는 인덱스를 위한, XPath 같은 쿼리를 지원한다. 각각의 인덱스는 대괄호 안 쿼리에 의해 대체될 수 있다.
예를 들어
경로를 사용하는 스키마 주소
/dataServices/schema/[${namespace}==='MySchema']
경로를 사용하는 엔티티 주소
/dataServices/schema/[${namespace}==='MySchema']/entityType/[${name}==='Order']
를 지정하여 사용할 수 있다.
대괄호 안의 구문은 바인딩 구문의 표현식이랑 일치한다. 쿼리는 결과가 true로 될 때까지 각 객체들을 위해 실행된다. 그 다음 객체가 선택되면
이 경로를 복잡한 바인딩 구문을 사용하여 바인딩 식에 끼워넣는다.
`${path:'...'}`.
Example: `{:= ${path:'target>extensions/[${name} === \'semantics\']/value'} === 'email'}`
이러한 각 쿼리들은 자체적으로 포함되어있다. 쿼리는 관련 경로를 통해서 현재 후보의 속성에 언급할 수 있다. 그러나 XML 탬플릿 안에서 이용가능한 ${meta>} 와 같은 변수들은 언급할 수 없다.
확장자
확장 배열과 SAP 사용해 단순 속성의 객체로 변환: 이전에 속성 이름들을 추가 ex) 8번쨰 라인 참조
1 {
2 "name": "BusinessPartnerID",
3 "extensions": [{
4 "name": "label",
5 "value": "Bus. Part. ID",
6 "namespace": "http://www.sap.com/Protocols/SAPData"
7 }],
8 "sap:label": "Bus. Part. ID"
9 }
OData V4 어노테이션
엔티티 모델의 각각 요소는 어노테이션을 달 수 있다. 기존 sap.ui.model.odata.ODataAnnotations 로 부터의 어노테이션은 해당 요소에 직접적으로 병합된다.
아래 코드는 위에서 설명한 것처럼 기존 sap.ui.model.odata.ODataMetadata로부터의 구조를 보여준다.
"dataServices" : {
"schema" : [{
"namespace" : "GWSAMPLE_BASIC",
"entityType" : [{
"name" : "Product",
"property" : [{
"name" : "ProductID",
"type" : "Edm.String",
"nullable" : "false",
"maxLength" : "10"
}, {
"name" : "SupplierName",
"type" : "Edm.String",
"maxLength" : "80",
"extensions" : [{
"name" : "label",
"value" : "Company Name",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}, {
"name" : "creatable",
"value" : "false",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}, {
"name" : "updatable",
"value" : "false",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}],
"sap:label" : "Company Name",
"sap:creatable" : "false",
"sap:updatable" : "false"
"Org.OData.Core.V1.Computed" : {
"Bool" : "true"
}
}, {
"name" : "WeightMeasure",
"type" : "Edm.Decimal",
"precision" : "13",
"scale" : "3",
"Org.OData.Measures.V1.Unit" : {
"Path" : "WeightUnit"
}
}, {
"name" : "WeightUnit",
"type" : "Edm.String",
"maxLength" : "3"
}],
"com.sap.vocabularies.UI.v1.DataPoint" : {
"Value" : {
"Path" : "WeightMeasure",
"EdmType" : "Edm.Decimal"
}
},
"com.sap.vocabularies.UI.v1.Identification" : [{
"Value" : {"Path" : "ProductID"}
}, {
"Value" : {"Path" : "SupplierName"}
}, {
"Value" : {"Path" : "WeightMeasure"}
}]
}]
}]
}
OData Meta Model의 향상
각 sap:label 과 같이 기본 용어에 해당하는 SAP-specific OData 어노테이션에 쉽게 접근하는데다가, 어노테이션은 기존 sap.ui.model.odata.ODataAnnotations의 OData 버전 4.0 어노테이션 안에서 정의 되지 않았을 때에서만 믹스된다.
OData V2 내 통화 및 단위 정의
양이나 치수 는 CLDR에 정의된 것 보다 다른 통화, 또는 단위가 필요할 수 있다.
The sap.ui.model.odata.type.Currency 그리고 sap.ui.model.odata.type.Unit 데이터 타입들은 통화 코드 그리고 단위를 커스터마이징 할 때 코드목록을 사용할 수 있다.
통화 또는 유닛 커스터마이징 하는 코드 목록의 경우 어노테이션들 정의해야한다.
OData V4 시나리오와 대조적으로 OData V2는 코드 리스트 서비스를 허용하지 않는다. 모든 메터데이터 정보는 기본 metadata.xml 파일에 포함되어야 하며, 코드리스트 URL은 단지 이 파일만을 가리켜야 한다. 이것은 다음과 같이 URL 속성을 명시하여 수행된다.
<PropertyValue Property="Url" String="./$metadata"/>
com.sap.vocabularies.CodeList.v1.CurrencyCodes 또는 com.sap.vocabularies.CodeList.v1.UnitsOfMeasure 어노테이션이 참조된 코드리스트는 이것들이 필요하다.
유일한 키 속성인 내부 코드
의존적 언어 설명
특정 단위 숫자 속성
옵션 : 내부 코드 대신 보여지는 외부 코드
옵션 : 표준 코드
키 속성 문서화:
설명 속성을 가리키는 com.sap.vocabularies.Common.v1.Text
숫자 속성을 가리키는 com.sap.vocabularies.Common.v1.UnitSpecificScale
표준 코드를 가리키는 com.sap.vocabularies.CodeList.v1.StandardCode
어노테이션 안의 경로 속성은 엔티티 타입 안 속성들을 참조해야하는 것을 명심해야한다. 네비게이션 속성을 포함하는 경로는 지원되지 않는다.
대체키가 가능할 때 타입은 통화 또는 단위의 키로서 대신 사용한다. 이 경우엔, 그 서비스는 통화 또는 단위 속성안에서 대체 키 묘사를 포함해야 한다. 대체키가 주석이 달리지 않았을 땐, 키는 데이터 안에서 사용, 예상된다. 최대 하나의 키를 가져야 하며, 키와 대체키는 정확히 하나여야 한다.
com.sap.vocabularies.CodeList.v1.StandardCode 로서 문서화된 속성은 sap.ui.model.odata.type.Currency 에서 ISO 코드로 해석된다. 그리고 통화 상징을 찾는데 사용된다. 이 통화 상징은 데이터 입력에 사용된다.
service’s metadata.xml 파일 내 통화 코드와 단위 코드 리스트 문서
...
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="SAP__common"/>
<edmx:Include Namespace="Org.OData.Core.V1" Alias="SAP__core"/>
<edmx:Include Namespace="com.sap.vocabularies.CodeList.v1" Alias="SAP__CodeList"/>
...
<EntityType Name="Product">
...
<Property Name="WeightMeasure" Type="Edm.Decimal" Precision="13" Scale="3" />
<Property Name="WeightUnit" Type="Edm.String" MaxLength="3" />
<Property Name="CurrencyCode" Type="Edm.String" Nullable="false" MaxLength="5" />
<Property Name="Price" Type="Edm.Decimal" Precision="16" Scale="3" />
...
</EntityType>
...
<EntityType Name="SAP__Currency" sap:content-version="1">
<Key>
<PropertyRef Name="CurrencyCode"/>
</Key>
<Property Name="CurrencyCode" Type="Edm.String" Nullable="false" MaxLength="5" sap:label="Currency" sap:semantics="currency-code"/>
<Property Name="ISOCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="ISO Code"/>
<Property Name="Text" Type="Edm.String" Nullable="false" MaxLength="15" sap:label="Short Text"/>
<Property Name="DecimalPlaces" Type="Edm.Byte" Nullable="false" sap:label="Decimals"/>
</EntityType>
<EntityType Name="SAP__UnitOfMeasure" sap:content-version="1">
<Key>
<PropertyRef Name="UnitCode"/>
</Key>
<Property Name="UnitCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="Internal UoM" sap:semantics="unit-of-measure"/>
<Property Name="ISOCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="ISO Code"/>
<Property Name="ExternalCode" Type="Edm.String" Nullable="false" MaxLength="3" sap:label="Commercial"/>
<Property Name="Text" Type="Edm.String" Nullable="false" MaxLength="30" sap:label="UoM Text"/>
<Property Name="DecimalPlaces" Type="Edm.Int16" sap:label="Decimal Places"/>
</EntityType>
...
<EntityContainer Name="GWSAMPLE_BASIC_Entities" m:IsDefaultEntityContainer="true" sap:message-scope-supported="true" sap:supported-formats="atom json xlsx">
<EntitySet Name="ProductSet" EntityType="GWSAMPLE_BASIC.Product" sap:content-version="1"/>
...
<EntitySet Name="SAP__Currencies" EntityType="GWSAMPLE_BASIC.SAP__Currency" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
<EntitySet Name="SAP__UnitsOfMeasure" EntityType="GWSAMPLE_BASIC.SAP__UnitOfMeasure" sap:creatable="false" sap:updatable="false" sap:deletable="false" sap:pageable="false" sap:content-version="1"/>
...
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.GWSAMPLE_BASIC_Entities">
<Annotation Term="SAP__CodeList.CurrencyCodes">
<Record>
<PropertyValue Property="Url" String="./$metadata"/>
<PropertyValue Property="CollectionPath" String="SAP__Currencies"/>
</Record>
</Annotation>
<Annotation Term="SAP__CodeList.UnitsOfMeasure">
<Record>
<PropertyValue Property="Url" String="./$metadata"/>
<PropertyValue Property="CollectionPath" String="SAP__UnitsOfMeasure"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="SAP__self.Currency/CurrencyCode">
<Annotation Term="Common.Text" Path="Text" />
<Annotation Term="Common.UnitSpecificScale" Path="DecimalPlaces" />
<Annotation Term="CodeList.StandardCode" Path="ISOCode" />
</Annotations>
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.SAP__UnitOfMeasure/UnitCode">
<Annotation Term="Common.Text" Path="Text" />
<Annotation Term="Common.UnitSpecificScale" Path="DecimalPlaces" />
<Annotation Term="CodeList.StandardCode" PropertyPath="ISOCode" />
<Annotation Term="CodeList.ExternalCode" PropertyPath="ExternalCode" />
</Annotations>
<Annotations
xmlns="http://docs.oasis-open.org/odata/ns/edm"
Target="GWSAMPLE_BASIC.SAP__UnitOfMeasure">
<Annotation Term="Core.AlternateKeys">
<Collection>
<Record>
<PropertyValue Property="Key">
<Collection>
<Record>
<PropertyValue Property="Name" PropertyPath="ExternalCode" />
<PropertyValue Property="Alias" String="ExternalCode" />
<Record>
</Collection>
</PropertyValue>
<Record>
</Collection>
</Annotation>
</Annotations>
...
위 메타데이터를 통해 sap.ui.model.odata.type.Currency 와 sap.ui.model.odata.type.Unit 같은 입력 필드의 데이터 타입을 사용할 수 있다. 데이터 타입들은 금액 또는 측정을 첫번째로, 통화코드 또는 단위를 두번째로, 그리고 커스텀한 코드리스트 정보를 세번째로 하여 정보를 합쳐 사용한다.
...
<Input value="{
mode:'TwoWay',
parts:[
'WeightMeasure',
'WeightUnit',
{
mode:'OneTime',
path:'/##@@requestUnitsOfMeasure',
targetType:'any'}],
type:'sap.ui.model.odata.type.Unit'}"/>
...
<Input value="{
mode:'TwoWay',
parts:[
'Price',
'CurrencyCode',
{
mode:'OneTime',
path:'/##@@requestCurrencyCodes',
targetType:'any'}],
type:'sap.ui.model.odata.type.Currency'}"/>
...
코드리스트는 브라우저 세션 및 코드목록 URL당 한번만 자동으로 요청된다.
Touch background to close