2
votes

Follow up to related question here

For whatever reason I'm unable to access my model in my xml view when it's set through sap.ui.getCore().setModel(). If I set it on the this.getView() I have no problems at all.

My view XML

<mvc:View controllerName="ca.toronto.rcsmls.webapp.controller.Login"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout">

<Page title="{i18n>loginPageTitle}">
    <content>

        <Panel id="loginPanel" busyIndicatorDelay="0"
            headerText="{i18n>loginPanelTitle}" class="sapUiResponsiveMargin loginPanel"
            width="auto">
            <l:VerticalLayout width="100%">


                <Input type="Text" placeholder="{i18n>loginUidHolder}" value="{/mlsUser/uid}" />

                <Input type="Password" placeholder="{app>/Password}"
                    value="{/mlsUser/password}" />

                <Button text="{i18n>loginButtonText}" press="doLogin"
                    class="sapUiSmallMarginEnd customBold" width="100%" />


            </l:VerticalLayout>
        </Panel>

    </content>
</Page></mvc:View>

My controller JS contains this for setModel()

 onInit : function() {
        sap.ui.getCore().setModel(new sap.ui.model.json.JSONModel("webapp/controller/app.json"), "app");
    }

Again, if I set the model to this.getView().setModel() instead of getCore() XML and controller work fine together. I also added data-sap-ui-xx-bindingSyntax="complex" to my index.html but that didn't seem to make a difference. Any help would be appreciated.

Edited to include more information

My Component.js

sap.ui.define([
   "sap/ui/core/UIComponent",
   "sap/ui/model/json/JSONModel",
], function (UIComponent, JSONModel) {
   "use strict";
   return UIComponent.extend("ca.toronto.rcsmls.webapp.Component", {
      metadata : {
          manifest: "json"
      },
      init : function () {
         // call the init function of the parent
         UIComponent.prototype.init.apply(this, arguments);
         // set data model
         var oData = {
            mlsUser : {
               uid : "",
               password : "",
            }
         };
         var oModel = new JSONModel(oData);
         this.setModel(oModel);

         // create the views based on the url/hash
         this.getRouter().initialize();
      }
   });
});

My manifest.json

{
    "_version": "1.1.0",
    "sap.app": 
    {
        "_version": "1.1.0",
        "id": "ca.toronto.rcsmls",
        "type": "application",
        "i18n": "i18n/i18n.properties",
        "title": "{{appTitle}}",
        "description": "{{appDescription}}",
        "applicationVersion": 
        {
            "version": "1.0.0"
        },

        "ach": "CA-UI5-DOC"
    },

    "sap.ui": 
    {
        "_version": "1.1.0",
        "technology": "UI5",
        "deviceTypes": 
        {
            "desktop": true,
            "tablet": true,
            "phone": true
        },

        "supportedThemes": 
        [
            "sap_bluecrystal"
        ]
    },

    "sap.ui5": 
    {
        "_version": "1.1.0",
        "rootView": "ca.toronto.rcsmls.webapp.view.App",
        "dependencies": 
        {
            "minUI5Version": "1.30",
            "libs": 
            {
                "sap.m": 
                {

                }
            }
        },

        "config": 
        {
            "authenticationService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/user/authenticate/",
            "assignedWorkService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/mls/searchAssignedWork"
        },

        "models": 
        {
            "i18n": 
            {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": 
                {
                    "bundleName": "ca.toronto.rcsmls.webapp.i18n.i18n"
                }
            }
        },

        "routing": 
        {
            "config": 
            {
                "routerClass": "sap.m.routing.Router",
                "viewType": "XML",
                "viewPath": "ca.toronto.rcsmls.webapp.view",
                "controlId": "root",
                "controlAggregation": "pages"
            },

            "routes": 
            [
                {
                    "pattern": "",
                    "name": "login",
                    "target": "login"
                },
                {
                    "pattern": "work",
                    "name": "work",
                    "target": "work"
                }
            ],

            "targets": {
                "login": {
                    "viewName": "Login"
                },
                "work": {
                    "viewName": "Work"
                }
            }
        },

        "resources": 
        {
            "css": 
            [
                {
                    "uri": "css/style.css"
                }
            ]
        }
    }
}

Model app.json

{
    "BaseURL": "https://smp-pNNNNNNtrial.hanatrial.ondemand.com",
    "ES1Root": "https://sapes1.sapdevcenter.com",
    "AppName": "qmacro.myfirst",
    "Username": "yourusername",
    "Password": "yourpassword"
}

I found an example where core binding works here. It is a much simpler application. I'm still trying to figure out the what the differences are between this project and mine

4

4 Answers

3
votes

I was made aware of this question by a similar question on Github

By default, UI5 components don't inherit models and binding contexts from their environment (the ComponentContainer where they are placed). This was done for the sake of isolation.

You can change that default behavior by setting the property propagateModel:true for the container:

new sap.ui.core.ComponentContainer({
  name: "ca.toronto.rcsmls.webapp.Component",
  propagateModel: true
}).placeAt("content");

Documentation for propagateModel can be found in the API reference as well as in the UI5 developer guide (the corresponding paragraph in the latter page was only added recently, it was not available when you raised your question).

But when the model is created in the scope of the component and shall be used inside the component, then there should be no need to add it to the Core. Just assign it to the component to share it between views inside that component:

Assign it either from some method in a view controller

onInit : function() {
    this.getOwnerComponent().setModel(
        new sap.ui.model.json.JSONModel(
            "webapp/controller/app.json"), "app");
}

or during init of the Component.js itself

init : function() {
    // create and set model to make it available to all views
    this.setModel(
        new sap.ui.model.json.JSONModel(
            "webapp/controller/app.json"), "app");

    // never forget to call init of the base class
    UIComponent.init.apply(this, arguments);
}

The most modern approach is to configure the model in the manifest.json and to let the framework instantiate an assign it. See Walkthrough Tutorial - Step 10: Descriptor for Applications for an example.

Using any of these approaches, there should not even be a need to read and set the model in another view, as long as that view is a descendant of the component (part of the control tree returned by createContent).

1
votes

I experienced the same behavior. If I create a simple one-file application, without any complex UI elements, the core-based binding works like a charm.

If I create a complex container, like an App with a shell, this kind of binding will not work anymore. It seems that these containers hide the global model from the view.

As a workaround I used the following code snippet:

this.getView().setModel(sap.ui.getCore().getModel(modelName), "modelName");

Or even you can bind the model directly to the control where you want to use.

None of them are the best solution if you have to use the global model in several view/control, but that's working for me.

0
votes

I the view above only the placeholder of the password field is using your app model. If this placeholder is not filled correctly then I guess the json file could not be loaded or the content is not matching the binding path you use in your view for the placeholder property of the password field. To find out more please post also the content of your json file. It should look somehow like this:

{ "Password" : "Enter your password", ... }

So according to the binding in the view there must be "Password" property at the root level of the data object.

Below you find a running example that should help you. As you can see you it work like a charm, so you can easily put a named model onto the Core and reference it in your view.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>SAPUI5 single file template | nabisoft</title>
        <script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
            id="sap-ui-bootstrap"
            data-sap-ui-theme="sap_bluecrystal"
            data-sap-ui-libs="sap.m"
            data-sap-ui-bindingSyntax="complex"
            data-sap-ui-compatVersion="edge"
            data-sap-ui-preload="async"></script>
            <!-- use "sync" or change the code below if you have issues -->

        <!-- XMLView -->
        <script id="myXmlView" type="ui5/xmlview">
            <mvc:View
                controllerName="MyController"
                xmlns="sap.m"
                xmlns:core="sap.ui.core"
                xmlns:l="sap.ui.layout"
                xmlns:mvc="sap.ui.core.mvc">
                <App>
                    <Page title="My Page Title">
                        <content>
                            <Panel id="loginPanel" busyIndicatorDelay="0"
                                headerText="My Login Panel Title" class="sapUiResponsiveMargin loginPanel"
                                width="auto">
                                <l:VerticalLayout width="100%">

                                    <Input type="Text" placeholder="{Enter User ID}" value="{/mlsUser/uid}" />

                                    <Input type="Password" placeholder="{app>/Password}" value="{/mlsUser/password}" />

                                    <Button text="{Login}" press="doLogin" class="sapUiSmallMarginEnd customBold" width="100%" />

                                </l:VerticalLayout>
                            </Panel>
                        </content>
                    </Page>
                </App>
            </mvc:View>
        </script>

        <script>
            sap.ui.getCore().attachInit(function () {
                "use strict";

                //### Controller ###
                sap.ui.controller("MyController", {
                    onInit : function () {
                        var oData, oModel;

                        // 1. app model is only used for the placeholder field in the view
                        oData = {
                            Password : "Enter your password"
                        };
                        oModel = new sap.ui.model.json.JSONModel(oData);
                        sap.ui.getCore().setModel(oModel, "app");

                        // 2. default model is used in the view as well
                        oData = {
                            mlsUser : {},
                            Login : "Login now"
                        };
                        oModel = new sap.ui.model.json.JSONModel(oData);
                        sap.ui.getCore().setModel(oModel);

                        // 3. we need this because of the relative binding
                        //    of the text property of the login button
                        this.getView().bindElement("/");

                    }
                });

                //### THE APP: place the XMLView somewhere into DOM ###
                sap.ui.xmlview({
                    viewContent : jQuery("#myXmlView").html()
                }).placeAt("content");

            });
        </script>

    </head>

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

In this thread some people have mentioned that "sap.ui.getCore() works with small stuff, but for whatever reason not in more complex applications".

@Marc has also posted the right link to the API docs, where you can find the following:

A ManagedObject inherits models from the Core only when it is a descendant of an UIArea

Of course, you have to know what this means in order write code that does what you expect. Here is a little example that tells you what could happen in case you have a small "misunderstanding" (see below).

The code below creates two instances of sap.m.Text and binds their text properties to a JSONModel, which is available as a named model "core" directly on the Core (retrieved with sap.ui.getCore()). There are 2 buttons, one for each sap.m.Text instance. In the corresponding press handlers of the buttons I just display the text property of the corresponding sap.m.Text instance. As you can see, both sap.m.Text instances are bound to the same property in the JSONModel. However, only the second sap.m.Text is added to the DOM.

Now the interesting part, which might be related to the confusion of this thread: Only the text property of the second sap.m.Text control contains the expected text "Hello World" from the JSONModel. The text property of the first sap.m.Text control does not have the value "Hello World" from the model! This is expected behavior of SAPUI5 and it is documented! So I guess in case you face similar issues in your own "complex" app, then it might be quite probable that you have a hard to find bug related to this "expected" behavior.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>SAPUI5 single file template | nabisoft</title>
        <script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
            id="sap-ui-bootstrap"
            data-sap-ui-theme="sap_bluecrystal"
            data-sap-ui-libs="sap.m"
            data-sap-ui-bindingSyntax="complex"
            data-sap-ui-compatVersion="edge"
            data-sap-ui-preload="async"></script>
            <!-- use "sync" or change the code below if you have issues -->

        <script>
            sap.ui.getCore().attachInit(function () {

                sap.ui.define([
                    "sap/m/Text",
                    "sap/m/Button",
                    "sap/ui/model/json/JSONModel"
                ], function (Text, Button, JSONModel) {
                    "use strict";

                    var oModel = new JSONModel({
                        hello : "Hello World"
                    });
                    sap.ui.getCore().setModel(oModel, "core");

                    // not in DOM
                    var oText1 = new Text({
                        text : "{core>/hello}"
                    });
                    //oText1.placeAt("text1");      // uncomment this to enable the binding

                    // add to DOM
                    var oText2 = new Text({
                        text : "{core>/hello}"
                    });
                    oText2.placeAt("text2");

                    // action buttons to display text property of text controls
                    new Button({
                        text : "show oText1",
                        press : function(){
                            alert("oText1.getText() = " + oText1.getText());
                        }
                    }).placeAt("btn1");

                    new Button({
                        text : "show oText2",
                        press : function(){
                            alert("oText2.getText() = " + oText2.getText());
                        }
                    }).placeAt("btn2");

                });
            });
        </script>

    </head>

    <body class="sapUiBody">
        <div id="text1"></div>
        <div id="text2"></div>
        <span id="btn1"></span>
        <span id="btn2"></span>
    </body>
</html>
-1
votes

sap.ui.getCore() and this.getView() return not the same object i think it's obvious why this doesn't work.

You are trying to get a object(model) from another object (the core) although the desired model is bound to another object(the view)

it's like i have two colored boxes (one red, one blue) and im trying to get the color red from the blue colored box

Here is an similar issue but the cause in this one depends on the id handling of the views in the UI5 framework: https://scn.sap.com/thread/3551589

You can see the core and the view does not return the same object.

Also checkout the data binding section on the openui5 website: https://openui5beta.hana.ondemand.com/#docs/guide/e5310932a71f42daa41f3a6143efca9c.html

Create Model in components.js:

var oModel= new sap.ui.model.json.JSONModel;
oModel.loadData("webapp/controller/app.json");
this.setModel(oModel, "app");

Get Model: This will create a reference to the model which has been created in the Components.js

var oModel= this.getView().getModel("app");

Refer to the model with "{modelName(app)>desiredProperty}"

<Input type="Password" placeholder="{app>Password}"
                    value="{app>mlsUser/password}" />

could you post your json content?

Hope this was helpful