Resume View에는 이전 튜토리얼 처럼 4 개의 탭이 있다. 하지만 사용자가 Resume 페이지로 이동하면 처음에는 첫 번째 탭만 표시된다.
현재 탭에서 특정 탭으로 바로 이동하거나 탭을 북마크로 지정하는 기능은 추후에 만들도록 한다.

이 단계에서는 쿼리를 매개 변수로 하여 tab에 대한 링크를 활성하여 북마크 기능을 구현한다.
Deep Link는 기본적으로 URL에서 App의 더 깊은 구조와 매개 변수를 직접 참조하는 링크이다.
특정 task나 action을 위해 App에 편리한 entry point가 있도록 북마크로 공유한다.
선택한 tab은 URL에 반영되어야 하지만 예를 들어 처음에 이력서 페이지로 이동할 때 tab을 생략 할 수도 있다.

Preview

09_preview.png

Coding

Routing and Navigation - Step 9.

webapp/manifest.json

{
    ...,
    "sap.ui5": {
        ...
        "routing": {
            "config": {
                "routerClass": "sap.m.routing.Router",
                "viewType": "XML",
                "viewPath": "sap.ui.demo.nav.view",
                "controlId": "app",
                "controlAggregation": "pages",
                "transition": "slide",
                "bypassed": {
                    "target": "notFound"
                }
            },
            "routes": [{
                "pattern": "",
                "name": "appHome",
                "target": "home"
            }, {
                "pattern": "employees",
                "name": "employeeList",
                "target": "employees"
            }, {
                "pattern": "employees/{employeeId}",
                "name": "employee",
                "target": "employee"
            }, {
                "pattern": "employees/{employeeId}/resume:?query:",
                "name": "employeeResume",
                "target": "employeeResume"
            }],
            "targets": {
                ...
            }
        }
    }
}

이전까지는 webapp/index.html#/employees/3/resume으로 직원의 이력서 페이지로 이동을 했다. IconTabBar 컨트롤에 의해 구현 된 첫 번재 Tab을 항상 선택했다.
여기서 선택된 특정 탭을 사용하여 페이지를 직접 열거나 북마크 기능을 추가하려면 URL 패턴에 query 매개 변수를 추가해야 한다.

webapp/index.html#/employees/3/resume?tab=Project와 같이 url로 허용이 된다. 쿼리 매개 변수는 표시할 tab을 정의한다.
descriptor 파일에서 라우트 패턴을 employees/{employeeId}/resume:?query:로 변경을 한다. ?:query: 부분은 매개 변수를 사용하여 query로 전달할 수 있고
패턴과 일치하는 이벤트에서 처리 할 수 있다.

note:
?:query:매개 변수는 :로 시작해서 :로 끝난다. 이는 옵션 사항으로 필수로 설정하기 위해선 {?query}로 사용하면 {}사이의 모든 항목은 필수 항목으로 간주된다.

webapp/view/employee/Resume.view.xml

<mvc:View
    controllerName="sap.ui.demo.nav.controller.employee.Resume"
    xmlns="sap.m"
    xmlns:mvc="sap.ui.core.mvc"
    xmlns:f="sap.ui.layout.form"
    busyIndicatorDelay="0">
    <Page
        title="{i18n>ResumeOf} {FirstName} {LastName}"
        id="employeeResumePage"
        showNavButton="true"
        navButtonPress="onNavBack"
        class="sapUiResponsiveContentPadding">
        <content>
            <IconTabBar
                id="iconTabBar"
                class="sapUiResponsiveContentPadding"
                binding="{Resume}"
                select="onTabSelect"
                selectedKey="{view>/selectedTabKey}">
                <items>
                    <IconTabFilter id="infoTab" text="{i18n>Info}" key="Info">
                        <Text text="{Information}" />
                    </IconTabFilter>
                    <IconTabFilter id="projectsTab" text="{i18n>Projects}" key="Projects">
                        <mvc:XMLView viewName="sap.ui.demo.nav.view.employee.ResumeProjects"></mvc:XMLView>
                    </IconTabFilter>
                    <IconTabFilter id="hobbiesTab" text="{i18n>Hobbies}" key="Hobbies">
                        <Text text="{Hobbies}" />
                    </IconTabFilter>
                    <IconTabFilter id="notesTab" text="{i18n>Notes}" key="Notes">
                        <Text text="{Notes}" />
                    </IconTabFilter>
                </items>
            </IconTabBar>
        </content>
    </Page>
</mvc:View>

URL에서 현재 선택된 탭을 업데이트 하려면 Resume view의 IconTabBar 컨트롤에 select="onTabSelect" 속성을 설정하여 select 이벤트를 등록한다.
selectedKey="{view>/selectedTabKey}"는 view의 모델에 바인드가 되고 탭에 따라 selectedKey를 넣어 줄 수 있다.

webapp/controller/employee/Resume.controller.js

sap.ui.define([
    "sap/ui/demo/nav/controller/BaseController",
    "sap/ui/model/json/JSONModel"

], function (BaseController, JSONModel) {
    "use strict";
    var _aValidTabKeys = ["Info", "Projects", "Hobbies", "Notes"];
    return BaseController.extend("sap.ui.demo.nav.controller.employee.Resume", {
        onInit: function () {
            var oRouter = this.getRouter();
            this.getView().setModel(new JSONModel(), "view");

            oRouter.getRoute("employeeResume").attachMatched(this._onRouteMatched, this);
        },
        _onRouteMatched : function (oEvent) {
            var oArgs, oView, oQuery;
            oArgs = oEvent.getParameter("arguments");
            oView = this.getView();
            oView.bindElement({
                path : "/Employees(" + oArgs.employeeId + ")",
                events : {
                    change: this._onBindingChange.bind(this),
                    dataRequested: function (oEvent) {
                        oView.setBusy(true);
                    },
                    dataReceived: function (oEvent) {
                        oView.setBusy(false);
                    }
                }
            });
            oQuery = oArgs["?query"];
            if (oQuery && _aValidTabKeys.indexOf(oQuery.tab) > -1){
                oView.getModel("view").setProperty("/selectedTabKey", oQuery.tab);
            } else {
                // the default query param should be visible at all time
                this.getRouter().navTo("employeeResume", {
                    employeeId : oArgs.employeeId,
                    query: {
                        tab : _aValidTabKeys[0]
                    }
                },true /*no history*/);
            }

        },
        _onBindingChange : function (oEvent) {
            // No data for the binding
            if (!this.getView().getBindingContext()) {
                this.getRouter().getTargets().display("notFound");
            }
        },
        onTabSelect : function (oEvent){
            var oCtx = this.getView().getBindingContext();
            this.getRouter().navTo("employeeResume", {
                employeeId : oCtx.getProperty("EmployeeID"),
                query: {
                    tab : oEvent.getParameter("selectedKey")
                }
            }, true /*without history*/);
        }

    });
});

탭을 클릭하면 onTabSelect 함수가 호출이 된다. 탭의 selectedKey를 감지하고 employeeResume 라우트로 이동하여 URL을 업데이트 한다.
이때 getBindingContext()를 통해 employeeID를 넘겨주고 query 매개변수에 object를 전달한다.(event obejct로 부터 selectedKey값을 확인하여 탭으로 넘김)
세번째 인수로 true 전달하면 현재 기록을 바꿔 수동으로 탭을 클릭해도 브라우저 history에는 추가되지 않도록 한다.

JSONModel을 종송석으로 추가하여 onInit 함수에서 JSONModel을 인스턴스화하여 view model로 사용한다.
var _aValidTabKeys = ["Info", "Projects", "Hobbies", "Notes"];를 통해 URL의 탭 매개 변수에 대한 유효성 검사를 할 수 있는 키들을 정의한다.

_onRouteMatched 함수는 라우터의 query object를 참조하는 oQuery 변수를 추가한다. query object가 전달되고 해당 탭의 매개 변수에 유효한 view model값이 있는 경우
/selectedKey 속성을 업데이트 하여 특정 탭을 표시한다.
IconTabBar 컨트롤의 selectedKey 속성은 view>selectedKey에 바인딩 되면 해당 탭이 선택된다. 만약 url에 매치되는 값이 없다면 _aValidTabKeys[0]의 값 Info로 URL이 업데이트 된다.

이제 탭을 클릭하면 URL의 해시가 즉시 변경이 되면서 ui도 업데이트가 되지만 뒤로가기로 브라우저의 히스토리에 저장되지 않음을 알 수 있다.

API Reference: sap.m.IconTabBar