4

I'm trying to setup my app descriptor file (manifest.json) to include a named model, 'inputs' in its "models" object. From my understanding, after doing so, the model should be available throughout the app when provided the correct path (see XML View).

The reason I'd like to setup this manifest.json is because it's a best practice to configure all models in here.

In the controller, I'd like to get and then set the 'inputs' Model defined in the manifest.json --but how can this be done?

manifest.json (Where I have configured the 'inputs' model)

{
    "_version": "1.1.0",
    "sap.app": {
        "_version": "1.1.0",
        "id": "pricingTool",
        "type": "application",
        "applicationVersion": {
            "version": "1.0.0"
        },
        "title": "{{appTitle}}",
        "description": "{{appDescription}}",
        "ach": "ach",
        "resources": "resources.json",
        "sourceTemplate": {
            "id": "ui5template.basicSAPUI5ApplicationProject",
            "version": "1.30.3"
        },
    },

    "sap.ui": {
        "_version": "1.1.0",
        "technology": "UI5",
        "icons": {
            "icon": "",
            "favIcon": "",
            "phone": "",
            "phone@2": "",
            "tablet": "",
            "tablet@2": ""
        },
        "deviceTypes": {
            "desktop": true,
            "tablet": true,
            "phone": true
        },
        "supportedThemes": [
            "sap_hcb",
            "sap_bluecrystal"
        ]
    },

    "sap.ui5": {
        "_version": "1.1.0",
        "rootView": {
            "viewName": "pricingTool.view.Main",
            "type": "XML"
        },
        "dependencies": {
            "minUI5Version": "1.30.0",
            "libs": {
                "sap.ui.core": {},
                "sap.m": {},
                "sap.ui.layout": {}
            }
        },
        },
        "contentDensities": {
            "compact": true,
            "cozy": true
        },
        "models": {
            "inputs": {
                        "type": "sap.ui.model.json.JSONModel",
                        "uri":  "model/inputs.json"
            },

        },
    }

Main.controller.js (where the 'inputs' model should be set from the manifest file)

sap.ui.define([
        'jquery.sap.global',
        'sap/ui/core/mvc/Controller',
        'sap/ui/model/json/JSONModel',
        'sap/ui/model/Filter',
        'sap/ui/model/FilterOperator',
        'sap/m/MessageToast',
        'pricingTool/model/viewControls',
        'pricingTool/model/formatter',
        'pricingTool/model/Utility',
        'sap/ui/core/util/Export',
        'sap/ui/core/util/ExportTypeCSV',
    ],

    function (jQuery, Controller, JSONModel, Filter, FilterOperator, MessageToast, viewControls, formatter, Utility, Export, ExportTypeCSV) {

        "use strict";

        var mainController = Controller.extend("pricingTool.controller.Main", {

            onInit: function(oEvent) {

                //define named/default model(s)
                var inputs = new JSONModel("./model/inputs.json");

                //set model(s) to current xml view
                this.getView().setModel(inputs, "inputs");

//this is one solution I have tried, but doesn't do anything: 
// this.getView().setModel(this.getOwnerComponent().getModel("inputs"), "inputs");

//another solution I have tried:
//var inputs = this.getModel('input')  <--I was hoping this would find the inputs defined in the manifest.json, but this doesn't work either
// this.getView().setModel(inputs, "inputs");


            },

            ...

inputs.json

{
    "propA" : "testVal"
}

XML View

<Button text="{inputs>propA}"></Button>

Components.js (Not sure what to do in the Component.js)

sap.ui.define([
            'sap/ui/core/UIComponent'
    ],
    function(UIComponent) {
    "use strict";


    var Component = UIComponent.extend("pricingTool.Component", {

        metadata : {

            metadata : {
                manifest: "json"
            },
            rootView : "pricingTool.view.Main",
            dependencies : {
                libs : [
                    "sap.m",
                    "sap.ui.layout"
                ]
            },
            config : {
                sample : {
                    files : [
                        "Main.view.xml",
                        "Main.controller.js"
                    ]
                }
            }
        },

        init : function () {
            // call the init function of the parent
            UIComponent.prototype.init.apply(this, arguments);

        }
    });

    return Component;

});

The problem is that the model property ("propA") is not displaying when I test it on a button control. Can anyone tell me why the model is not displaying in the app?

Summarizing Question

How can I define a model in manifest.json, and then set that model in the controller so I can use it in my xml view?

Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
Kode_12
  • 4,506
  • 11
  • 47
  • 97

3 Answers3

3

try putting a forward slash before your property name...

<Button text="{inputs>/propA}"></Button>

...and update your manifest file so that your model definition points to your dataSource defined under sap.app as follows...

{
"_version": "1.1.0",
"sap.app": {
    "_version": "1.1.0",
    "id": "pricingTool",
    "type": "application",
    "applicationVersion": {
        "version": "1.0.0"
    },
    "title": "{{appTitle}}",
    "description": "{{appDescription}}",
    "ach": "ach",
    "resources": "resources.json",
    "sourceTemplate": {
        "id": "ui5template.basicSAPUI5ApplicationProject",
        "version": "1.30.3"
    },
    "dataSources": {
        "inputsData": {
            "type" : "JSON",
            "uri": "model/inputs.json"
        }
    }
},

"sap.ui": {
    "_version": "1.1.0",
    "technology": "UI5",
    "icons": {
        "icon": "",
        "favIcon": "",
        "phone": "",
        "phone@2": "",
        "tablet": "",
        "tablet@2": ""
    },
    "deviceTypes": {
        "desktop": true,
        "tablet": true,
        "phone": true
    },
    "supportedThemes": [
        "sap_hcb",
        "sap_bluecrystal"
    ]
},

"sap.ui5": {
    "_version": "1.1.0",
    "rootView": {
        "viewName": "pricingTool.view.Main",
        "type": "XML"
    },
    "dependencies": {
        "minUI5Version": "1.30.0",
        "libs": {
            "sap.ui.core": {},
            "sap.m": {},
            "sap.ui.layout": {}
        }
    },
    "contentDensities": {
        "compact": true,
        "cozy": true
    },
    "models": {
        "products": {
            "type": "sap.ui.model.json.JSONModel",
            "uri":  "model/products.json"
        },
        "inputs": {
                    "type": "sap.ui.model.json.JSONModel",
                    "dataSource" : "inputsData"
        }
      }
    }
}

...change your Component.js file to point to your manifest file...

sap.ui.define([
        'sap/ui/core/UIComponent'
    ],
    function(UIComponent) {
    "use strict";


    var Component = UIComponent.extend("pricingTool.Component", {

        metadata : {
            manifest: "json",
        },

        init : function () {
            // call the init function of the parent
            UIComponent.prototype.init.apply(this, arguments);

        }
    });
});

... and remove the onInit logic within your controller to set the model (this is handled by the component)

Ian MacGregor
  • 513
  • 1
  • 4
  • 11
  • ah yea you will need to adjust your manifest configuration slightly so your uri comes under sap.app.dataSources, will update the answer – Ian MacGregor Jan 25 '17 at 03:16
  • 1
    the manifest structure needs correcting slightly, sap.ui5 object was ending after dependencies so models was not being picked up...will update answer – Ian MacGregor Jan 25 '17 at 04:03
  • yes, it was just moving the placement of one curly bracket... have you tried with the current json? have cut&paste into an existing app's manifest and it was trying to load the model/inputs.json so the model is being initialised. – Ian MacGregor Jan 25 '17 at 20:42
  • yeah for someone reason it's still not showing the data. – Kode_12 Jan 25 '17 at 21:31
  • have updated my response to include some changes to your component as it is not currently configured to look at your manifest file. you will need to change the binding in your view as per the first part of my response – Ian MacGregor Jan 28 '17 at 00:36
  • if you're unsure you can have a look at the (minimal) example code here https://bitbucket.org/ijmacgregor/stack-overflow-binding-sample/src - there will be some console errors due to a missing products.json file and have removed some custom dependencies that were being injected into the controller, but the binding from the inputs model should work – Ian MacGregor Jan 28 '17 at 00:55
1

Those models you define in the manifest.json file are created in the Component context (if your app based on Component). To make it available in the XML view you have to obtain it from Component and then attach to the view. The code snippet you can use in onInit controller event looks like this:

this.getView().setModel(this.getOwnerComponent().getModel("<your_model_name>"), "<your_model_name>");

if you are using standard template then most likely you have a BaseController as an ancestor, in that case the code can look shorter:

this.setModel(this.getComponentModel("<your_model_name>"), "<your_model_name>");
slkorolev
  • 5,883
  • 1
  • 29
  • 32
  • As I'm new to the whole manifest/component configuration, my impression was that by defining the model in the manifest.json, that model will be accessible throughout the application. Correct me if I'm wrong, but this is still true, but now I just need to set each model defined in the manifest.json in the Component.js? – Kode_12 Jan 26 '17 at 19:48
  • 1
    The code should be in the view controller (in onInit event), not in the Component.js – slkorolev Jan 26 '17 at 21:28
  • Thanks for the reply, I tried both codes and included each in the main.Controller.js. The first does nothing; The second BaseController solution returns an error getComponentModel is not a function. Please see the 'Summarizing Question' I have provided to ensure we're on the same page with what I'm having a problem with :) – Kode_12 Jan 26 '17 at 21:53
1

Here is a minimal example of what you'd like to achieve: https://embed.plnkr.co/l1XF5O/

  • Models defined in manifest.json (aka. "app descriptor") will be set to the component (since v1.30).
  • If a descriptor is used, almost all properties of your component's metadata other than manifest: "json" are deprecated and should be avoided. Deprecated properties are listed here.
  • Views (and their controls inside) inside the root view instantiated by your component inherit models automatically from the component. Thus, setting models to your view explicitly is not needed anymore. Your view already knows the model that is set to the component.*
  • The binding syntax should be used correctly according to your situation:
    • Use relative binding syntax (modelName>property) only if a parent control has already a context bound (e.g. the parent control uses aggregation binding, or element binding).
    • In other cases, use absolute binding syntax. It starts with a slash (modelName>/property), so that the control doesn't look for the binding context of its parent.

*Although the model, which is set on the component, can be used seamlessly in the XMLView, retrieving the component model by calling view.getModel inside the onInit handler will return undefined. More about this: https://stackoverflow.com/a/43941380/5846045

Community
  • 1
  • 1
Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170