You are on page 1of 12

The Model View Controller (MVC) Approach in SAPUI5

This page describes the concepts and the usage of MVC in SAPUI5 and especially the role of the artifacts View and Controller. For detailed information on the Model appraoch and the available Model flavors, goto Data Binding in Applications

Introduction
SAPUI5 offers Views and Controllers in the form of single files, these are

sap.ui.core.mvc.Controller sap.ui.core.mvc.XMLView sap.ui.core.mvc.JSView and sap.ui.core.mvc.JSONView

The base class for defining other or custom View types is

sap.ui.core.mvc.View

Objectives

Provide support for MVC paradigm Support development in distributed teams with different source locations Suggest file structure, naming, and usage patterns Add capability of UI declaration (in comparison to a programmatic construction)

Usage
According to the MVC paradigm, the View is responsible for defining and rendering the UI, the Model manages the application data, and the Controller reacts to View events and user interaction by modifying View and Model. This pattern defines a useful separation of concerns which helps developing or changing parts independently. Views and Controllers often form a 1:1 relationship; alternatively it is also possible to have UI-less Controllers (also named application controllers) and Views without Controllers. Since a View is a SAPUI5 Control from the technical point of view, it can have or inherit a SAPUI5 Model. Note that View and Controller represent reusable units, and distributed development is highly supported (developer of artifact is not the same person as the developer who is (re-)using it).

File Names and Locations (View and Controller)


Controllers and Views are defined and instantiated via a name which equals a SAPUI5 module name within the require/declare concept. This means that by default all files have to be located in a subfolder of the resources folder in the Web application. If this location is not appropriate, deviations can be configured as follows: Example: We assume that your Views and Controllers are located on your local machine where the SAPUI5 runtime is loaded from another machine. When you now try to instantiate a View or Controller, the SAPUI5 runtime tries to load them relatively to the resources folder of the

machine where the SAPUI5 runtime was loaded. Therefore you have to inform the runtime that your Views and Controllers are located on your local machine:

jQuery.sap.registerModulePath(sModuleNamePrefix, sURL);
or

sap.ui.localResources(sModuleNamePrefix);
In most cases it will be sufficient to use sap.ui.localResources which internally registers

sModuleNamePrefix to the URL "./" + sTransformedModuleNamePrefix where the transformed name has all dots replaced by slashes. All files starting with sModuleNamePrefix will
then be loaded relatively to the location of the HTML page that was calling sap.ui.localResources. If your files are located at " http://<localhost:8080>/<myapp>/", for example, you can use registerModulePath as follows:

jQuery.sap.registerModulePath("myapp", "http://<localhost:8080>/<myapp>/");
or

sap.ui.localResources("myapp");
All Views and Controllers having a name starting with myapp, for example myapp.MyView, will now be loaded from your local machine.

Controller Definition
You define a simple Controller, having no function yet, as following:

sap.ui.controller("sap.hcm.Address", { // controller logic goes here });


The string in quotes is the Controller name, which is described in the chapter above. The Controller file is then named Address.controller.js. Note that each Controller must have the suffix .controller.js.

Controller Functions
There are predefined lifecycle hooks you can implement. Typically you also add event handlers or other functions. Controllers can fire events that other Controllers or entities can register for.

Lifecycle Hooks
The lifecycle hooks are as following:

onInit() - Called when a View is instantiated and its controls (if available) are already
created. Can be used to modify the View before it is displayed to bind event handlers and do other one-time initialization.

onExit() - Called when the View is destroyed. Use this one to free resources and finalize
activities.

onAfterRendering() - Called when the View has been rendered (therefore its HTML is
part of the document). Post-rendering manipulations of the HTML can be done here. This hook is the same one that SAPUI5 controls get after being rendered. onBeforeRendering() - Is invoked before the Controller's View is re-rendered (and NOT before the first rendering). You would use onInit() in the case that the hook shall be invoked before the first rendering.

For Controllers without a View, no lifecycle hooks will be called. Example:

sap.ui.controller("sap.hcm.Address", { onInit: function() { this.counter = 0; } });

Event Handlers and Other Functionality


In addition to the lifecycle methods, a Controller can define any additional methods that can serve as event handlers, or as some functionality offered by the Controller. Example:

sap.ui.controller("sap.hcm.Address", { increaseCounter: function() { this.counter++; } });

View Types
While the XML and JSON notation for SAPUI5 UI controls has been introduced, the MVC pattern shall also be supported in the case that a traditional programmatic UI construction is preferred.

XMLView o UI is defined in an XML file/string JSONView o UI is defined via JSON in a file/string JSView

UI is constructed in a traditional manner

Note that the XMLView type is constructed in a way that a mix with plain HTML is possible.

While the View types mentioned above are predefined and offered to be chosen as View option in the application creation wizard - in the case that the tools are used for Web application development - others can also be plugged in.

JSView Definition
A JavaScript View is created like a Controller. The suffix for such a file is .view.js. There are two default methods that can be implemented and that would usually always be used:

getControllerName() - Specifies the Controller belonging to this View. In the case that
it is not implemented, or that "null" is returned, this View does not have a Controller. createContent() - Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. Since the Controller is given to this method, its event handlers can be attached right away.

Example:

sap.ui.jsview("sap.hcm.Address", { Address.view.js getControllerName: function() { return "sap.hcm.Address"; Address.controller.js },

// this View file is called

// the Controller lives in

createContent: function(oController) { var oButton = new sap.ui.commons.Button({text:"Hello JS View"}); oButton.attachPress(oController.handleButtonClicked); return oButton; } });
The string in quotes is the View name, which again equals the SAPUI5 module name within the require/declare concept.

XMLView Definition
This type of View is defined in an XML file, and the file name has to end with .view.xml, or as an XML string. The View name along with the folder structure above is the name of the View. This name again equals the SAPUI5 module name within the require/declare concept. Example: In the case of resources/sap/hcm/Address.view.xml, the View name would be

sap.hcm.Address. This View name is used when the application displays an instance of this View.
If you define the XMLView via an XML string, no file or require/declare is needed. The file looks like this:

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core"> <Panel> <Image src="http://www.sap.com/global/ui/images/global/saplogo.png"/>

<Button text="Press Me!"/> </Panel> <core:View>


Therefore you simply nest the XML tags (analogous to a nesting sequence of the SAPUI5 controls), and add the property values as attributes. (Some details on more complex Views are described below.)

Namespaces in XMLViews
Analoguous to generic XML mapping features, the names of the SAPUI5 control libraries are mapped to XML namespaces. You can choose any of the required namespaces to be the default namespace, therefore the respective control tags do not need a prefix. The surrounding <View> tag is always required and in this case has the core namespace defined in the first line. Of course you can give any name; to keep the tag names shorter it might be useful for example to use "c" instead of "core". Note: If controls are located in a subpackage of a control library, as it is the case for the sap.ui.commons.layout.MatrixLayout, they need to have their own XML namespace:

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:layout="sap.ui.commons.layout"> <layout:MatrixLayout> ...

Aggregation Handling in XMLViews


Child controls are simply added as child tags, as described in the example above where a <Button> tag was added into a <Panel> tag. Some controls have more than one content area, for example the Shell control which has main content, a pane bar, a headerItems aggregation, worksetItems and so on. This is the reason why in general an aggregation tag serves as direct child of a container, and inside are the children. Directly adding children as seen above is only possible if the container control has marked one child aggregation as "default". Some containers may not have "default" content like the Splitter, which has two equally important content areas. Note that the framework leads you as a user by providing an error message if you made a mistake here. In general, aggregations are filled as follows. Note: The namespace of the parent control tag and the aggregation tag must be the same.

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core"> <Panel> <content> <!-- this is the general way of adding children: use the aggregation name --> <Image src="http://www.sap.com/global/ui/images/global/saplogo.png"/> <Button text="Press Me"/> </content> </Panel> <core:View>

Usage of Event Handlers in XMLViews


Event handlers are bound as attributes, where the attribute name is the event name, like "press" in case of a Button, and the attribute value is the event handler name. This event handler is expected to be defined as function in the View's Controller. Therefore, this declaration will cause controller.doSomething() to be executed when the Button is pressed:

... <Button text="Press Me" press="doSomething"/> ...

Data Binding in XMLViews


You can bind data in XMLViews: If you want to bind texts of a control to a language-dependent resource bundle, simply define the resource bundle via a name (resourceBundleName attribute) or a URL (resourceBundleUrl attribute) and assign an alias (resourceBundleAlias attribute) for the bundle within the View definition. The binding path is the same as for every other SAPUI5 databinding: Resource bundle content:

MY_TEXT=Hello World
Example:

<core:View resourceBundleName="myBundle" resourceBundleAlias="i18n" controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <Panel> <Button text="{i18n>MY_TEXT}"/> </Panel> <core:View>
The necessary ResourceModel for binding these texts is created during View instantiation. The Model will be set as secondary Model with the given alias to the View instance. If you want to bind other properties to another Model you have to create the Model on your own within the corresponding Controller, or HTML page, and attach it to the View with another alias. The binding itself behaves like every SAPUI5 databinding and like described above.

Usage of Native HTML in XMLViews


The usage of Native HTML depends on the XHTML feature set. When mixing XHTML and SAPUI5 controls, some additional rules have to be obeyed: 1. XHTML elements can be used whenever the SAPUI5 type "Control" could be used, for example in the root of an XMLView, or in the content aggregation of a layout container 2. When embedding XHTML in an aggregation of a SAPUI5 control, the XHTML must not consist of a single text node. The topmost node of an embedded XHTML tree must be an XHTML element. Embedding pure text into an aggregation is not supported.

3. The XHTML nodes are converted 1:1 to HTML, the XMLView does not deal with any differences between XHTML and HTML (for example re-writing auto-closing tags) 4. The created HTML DOM nodes are preserved during re-rendering of an XMLView: Modifications to the DOM are not lost. Further restrictions are currently not known. Note: As an alternative to embedding XHTML, you can use the sap.ui.core.HTML control. Note that it is less convenient since encoding of the content is required. To mix SAPUI5 controls freely with native XHTML, you simply need another namespace - the XHTML one - and then you can use (X)HTML:

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <Panel> <Button text="Press Me. I am a SAPUI5 Button"/> <html:button>No, press me. I am native HTML Button.</html:button> </Panel> <core:View>

Usage of CSS Style Sheets in XMLViews


You include the style sheets like plain HTML. You can also add further CSS classes to SAPUI5 controls by using the class attribute. The effect is the same as it is when calling myButton.addStyleClass(...).

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <html:style> .mySuperRedButton { color: red; } </html:style> <Panel> <Button class="mySuperRedButton" text="Press Me"/> </Panel> <core:View>
It is recommended to carefully choose the elements that you style since the CSS always affects the whole page and is NOT restricted to the View.

JSONView Definition
This type of View is defined in a file and that file's name must end with .view.json, or as a JSON string. The View name along with the folder structure above is the name of the View. This name again equals the SAPUI5 module name within the require/declare concept. For example, the file could be resources/sap/hcm/Address.view.json, then the View name would be sap.hcm.Address. This View name is used when the application wants to display an instance of this View.

The file looks like this:

{ "Type":"sap.ui.core.mvc.JSONView", "controllerName":"sap.hcm.Address", "content": [{ "Type":"sap.ui.commons.Image", "id":"MyImage", "src":"http://www.sap.com/global/ui/images/global/sap-logo.png" }, { "Type":"sap.ui.commons.Button", "id":"MyButton", "text":"Press Me" }] }
So you simply nest the JSON Objects like you want to nest the SAPUI5 controls and add the property values as attributes. The syntax is exactly the same as you can use a JSON constructor for any control. Note: You can only use strings in your JSONView.

Aggregation Handling in JSONViews


As seen above, child controls are simply added as arrays, like the above example added an Image and a Button into the View content aggregation.

Usage of Event Handlers in JSONViews


Event handlers are bound as attributes, where the attribute name is the event name, like "press" in case of a Button, and the attribute value is the event handler name. This event handler is expected to be defined as function in the View's Controller. So this declaration will cause controller.doSomething() to be executed when the Button is pressed:

... { "Type":"sap.ui.commons.Button", "id":"MyButton", "text":"Press Me", "press":"doSomething" } ...

Data Binding in JSONViews


You can bind data in JSONViews. You bind the texts of a control to a language-dependent resource bundle by defining the resource bundle via name (resourceBundleName property) or a URL (resourceBundleUrl property) and assigning an alias (resourceBundleAlias property) for the bundle within the View definition. The binding path is the same as for every other SAPUI5 databinding:

Resource bundle content:

MY_TEXT=Hello World
Example:

"Type": "sap.ui.core.JSONView", "controllerName":"my.own.views.test", "resourceBundleName":"myBundle", "resourceBundleAlias":"i18n", "content": [{ "Type":"sap.ui.commons.Panel", "id":"myPanel", "content":[{ "Type":"sap.ui.commons.Button", "id":"Button1", "text":"{i18n>MY_TEXT}", "press": "doIt" }] }]

}
The necessary ResourceModel for binding this texts is created during View instantiation. The Model will be set as secondary Model with the given alias to the View instance. If you want to bind other properties to another Model you have to create the Model on your own within the corresponding Controller, or HTML page, and attach it to the View with another alias. The binding itself behaves like every SAPUI5 databinding and like described above.

View Instantiation
Views can be instantiated with the factory method sap.ui.view. Necessary information for the instantiation can be passed via an object. This object can have the following properties:

type: Type can be JSON, JS, XML. All possible types are declared in the enumeration sap.ui.core.mvc.ViewType viewName: The View name, corresponding to the module concept viewContent: XMLViews/JSONViews only - XML/JSON string representation of the View definition. If viewName and viewContent are given, the View definition will be loaded with the viewName Controller: Any Controller instance. The given Controller instance overrides the
Controller defined in the View definition viewData: JSViews only - can hold user specific data. This data is available during the whole lifecycle of the View and the Controller

Example:

... var myController = sap.ui.controller("my.own.controller"); var myView = sap.ui.view({ type: sap.ui.core.mvc.ViewType.XML, viewName: "my.own.view", controller: myController

}); ...
All regular properties of a View (control) can be passed to the object as usual.

Typed Views and Controllers


The above ways of defining Views and Controllers are light-weight and easy to write. However, for more complex use-cases a more formal way to define Views and Controllers might be preferred, even if this means a bit more complexity. This section shows how you can create new classes, using prototype inheritance, and declare their API.

Typed Views Typed Controllers


If you want to create a controller that is a new type on its own, you can do it like this. There is some boilerplate code on top that needs to be written and the functions need to be declared on the new prototype. But apart from that it's a normal controller:

/* boilerplate code for typed Controller */ jQuery.sap.declare({modName:"sap.hcm.AddressController", type:"controller"}); // declaring a special type of module sap.hcm.AddressController = function () { // the constructor sap.ui.core.mvc.Controller.apply(this, arguments); }; jQuery.sap.require("sap.ui.core.mvc.Controller"); // this is currently required, as the Controller is not loaded by default sap.hcm.AddressController.prototype = jQuery.sap.newObject(sap.ui.core.mvc.Controller.prototype); // chain the prototypes /* end of boilerplate code for typed Controller */ // to avoid the above we could in the future offer it behind a simple call to: // sap.ui.defineController("sap.hcm.Address"); sap.hcm.AddressController.prototype.onInit = function() { // modify control tree - this is the regular lifecycle hook }; // implement an event handler in the Controller sap.hcm.AddressController.prototype.doSomething = function() { alert("Hello World"); };
A live example can be found here. Or you can view the example in snippix.

Nesting Views
All Views can be nested independent of the View type. Each View type behaves like any other SAPUI5 control. The viewName property defines which View to embed. For XMLViews it can look like this:

<core:View controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <Panel> <core:JSView id="myJSView" viewName="sap.hcm.Bankaccount" /> </Panel> <core:View>

Support for Unique IDs


Normally, each View and its content defines static IDs. Via this IDs you can identify and modify the controls within your Controller during runtime. If you are reusing or nesting these Views, the IDs will be not unique any more. Therefore each View will add its own ID as prefix to all its child controls. This happens only if a static ID is given. If the ID is created during instantiation of the control it is unique per default. If you create further controls during runtime, the Controller can create an unique ID for you calling oController.createId("yourID"). This methods adds the necessary prefix to the ID. If you want to modify the control with the ID <yourID> you can call the method byId(<yourID>) on your view to get the right control directly. You don't have to handle all the prefix stuff by your own. ButtonView:

<core:View viewName="sap.hcm.ButtonView" controllerName="sap.hcm.myController" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <Button id="aButton" text="Click me"/> <core:View>
This View defines a Button with static ID aButton. ContainerView:

<core:View viewName="sap.hcm.ContainerView" controllerName="sap.hcm.Address" xmlns="sap.ui.commons" xmlns:core="sap.ui.core" xmlns:html="http://www.w3.org/1999/xhtml"> <core:View id="ButtonView1" viewName="sap.hcm.ButtonView"/> <core:View id="ButtonView2" viewName="sap.hcm.ButtonView"/> <core:View>
This View defines a View embedding the same View several times. View creation:

... var oView = sap.ui.view({type:sap.ui.core.mvc.ViewType.XML, id:"myContainerView",viewName:"sap.hcm.ContainerView"}); ...


The IDs will look like this: For the Container View both child Views get myContainerView-- as prefix: myContainerView-

-ButtonView1 myContainerView--ButtonView2
Getting one of these child Views:

... var oButtonView1 = oView.byId("ButtonView1"); ...


For the ButtonViews the IDs look like this: ButtonView1--aButton ButtonView2--aButton Getting the Button control:

... var oButtonView1 = oView.byId("ButtonView1"); oButtonView1.byId("aButton"); ...

MVC and SAPUI5 for GWT


Currently, there is no support for working on basis of the MVC approach for the Google Web Toolkit.

What About the MVP Appraoch? Why not?


(Model-View-Presenter is a Pattern closely related to MVC) Actually, MVP is not a pattern that is exactly described. You can read Martin Fowler's take on it ( http://www.martinfowler.com/eaaDev/uiArchs.html) or simply trust Wikipedia to find out that understandings of MVP differ a lot, maybe more than MVC and "median" MVP understandings differ. To some extent, the SAPUI5 MVC implementation can be considered as close to MVP than to MVC. So instead of asking "MVC vs MVP" one should understand what SAPUI5 offers and ask about specific parts of MVP that seem to be missing. These can be discussed and - if useful implemented. It's content, not the label, that counts.

Under Development

Performance measurements for XML parsing Useful connecting methods between Controller and other entities may need to be added in the future Controls - like Dialogs - that are not part of the UI tree Preserve/rendering issue when using HTML container in XMLViews