Is it possible to have XML Fragments IDs prefixed with its view's ID? - sapui5

When a XML view is declared, all of its controls IDs are prefixed by the ID of the view itself.
In order to get any control inside the controller it's necessary to use:
this.byId()
... where this points to the controller by default.
I already know that there is
sap.ui.getCore().byId()
as well which can be used to retrieve a control defined in a JS View or created without a view prefix.
I declared a XML fragment with a dialog and a Text control which will contain a text defined by my controller. I noticed that the ID I defined inside the fragment is not prefixed with the view's ID.
My question is: Is it possible to have XML Fragments IDs prefixed with its view's ID (then I could use this.byId instead of sap.ui.getCore) ?

I checked and this appears to be happening only when you are adding the fragment from a controller. If the fragment is defined in the static time in the xml view the ID's of the content derive their name from the view.
The way to get over this is to ensure your fragment ID is derived from your view.
The code would be something like this in your controller.
oPage.addContent(new sap.ui.xmlfragment(this.createId("idFragment"), "fragmentcreation.SampleFragment"));
IdFragment = ID for your fragment
fragmentcreation.SampleFragment = Name of your fragment(fragmentcreation is the folder)

Related

How to retrieve view's ID with application prefix?

To retrieve prefixed ID of a control we can use view's method CreateID. However, there's no such a method in class sap.ui.core.Core. So how do I know viewID in the line
sap.ui.getCore().byId(viewID)
The Core is common to all the applications you are loading into your HTML document. An specific view of an specific application can have a different ID depending on how many different views you already loaded. The CreateID method makes sense to create an ID prefixed by the relevant View ID, but the view IDs are not prefixed but just autogenerated.
So, to get the ID of the current view, just do this.getView() in its controller and get the ID from the returned object.
If you want to know the ID of a different view to the one you are visualizing/loading, then you need to navigate through all the views in your DOM and design your own way to identify it.
However I don't recommend you to modify a View from a controller of another view. Each View should be modified by the logic in its controller only, and no by other controllers, to maintain the principles of the MVC architecture
EDIT:
How to access your component:
If you bind the context of your app execution with the Push.js script, then use this.getOwnerComponent().
If you don't, then you need to access your Component from the core with sap.ui.getCore().getComponent('componentID')
Now the problem is how to know the componentID. Well, I see two options here, give an ID to your component, or map the autogenerated component IDs in a Core variable.
How to retrieve a specific Component:
Option 1: Define a Component ID
Whenever you initialize it, usually in the index.html file, you can do it like this:
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.component({
id: "myComponentID",
async: true,
name: "myNamespace", // The namespace you defined in the bootstrap tag and points to the folder where the Component.js file is stored
manifestFirst: true
}).then(function(oNewComponent){
new sap.m.Shell({
app: new sap.ui.core.ComponentContainer({
component: oNewComponent,
height : "100%"
})
}).placeAt("content");
})
});
</script>
With this option you can retrieve your Component as sap.ui.getCore().getComponent('myComponentID') and of course whatever model registered in it with sap.ui.getCore().getComponent('myComponentID').getModel('modelName')
Option 2: Map the autogenerated components IDs with your identifiers in a Core variable
I don't like to change the Components ID, and there are some times when you have no chance to do it. So in the past I did it like in the following snippet (probably not the best/elegant solution out there, but it works good)
Component.js
/**
* The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
* #public
* #override
*/
init: function() {
//Map Component ID in a Core variable
if(!sap.ui.getCore()._data_oLoadedComponents){
sap.ui.getCore()._data_oLoadedComponents = {};
}
sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId = this.getId(); // myCustomComponentId is the ID you want to use for this specific app
// END: Map Component ID in a Core Variable
// call the base component's init function
UIComponent.prototype.init.apply(this, arguments);
// set the device model
this.setModel(models.createDeviceModel(), "device");
}
So now you can access any of your components getting the autogenerated ID from your mapping variable like this:
sap.ui.getCore().getComponent(sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId)
and also access the model like this:
sap.ui.getCore().getComponent(sap.ui.getCore()._data_oLoadedComponents.myCustomComponentId).getModel('modelName')

How to retrieve the rows which are selected in the list report page of smart templates

This is the List Report type of Smart Template application
Here I have selected 2nd and 5th row, I also have a button named Send Requests in the section part which is highlighted. If I click this button it calls a javascript controller function which is defined in the extensions of the application. In this js function how can I retrieve the selected rows that are selected?
I have enabled the checkboxes in this page by mentioning this code
"settings": { "gridTable": false, "multiSelect": true } in the manifest.json
As it was recommended by this link https://sapui5.netweaver.ondemand.com/#docs/guide/116b5d82e8c545e2a56e1b51b8b0a9bd.html
I want to know how can I retrieve the rows which got selected?
There is an API that you can use for your use case. It is described here: https://sapui5.netweaver.ondemand.com/#docs/guide/bd2994b69ef542998becbc69ab093f7e.html
Basically, you just need to call the getSelectedContexts method. Unfortunately you will not be able to really get the items themselves, only the binding contexts (which point to the data entities which are selected). Excerpt from the documentation:
After you have defined a view extension, you can access and modify the
properties of all UI elements defined within these extensions (for
example, change the visibility). However, you cannot access any UI
elements that are not defined within your view extensions.
In this type of table there is way.
var myTable=sap.ui.getCore().byId("your table id");
get all rows:
var myTableRows=myTable.getRows();
now get selected Indices
var selectedIndeices=myTable.getSelectedIndices(); //this will give you array of indeices.
now run loop on indeices array. And get particular row item;
// get binding path
var bindingpath=myTableRows[2].getBindingContext().sPath; // this will return eg:"/ProductCollection/2"
// now get Binding object of that particular row.
var myData=myTableRows[2].getModel().getObject(bindingpath); // this will return binding object at that perticular row.
// once your loop is over in the end you will have all object of selected row. then do whatever you want to do.
If you use smart template create an extension.
This is the standard event befor the table is rebinding:
onBeforeRebindTableExtension: function (oEvent) {
this._table = oEvent.getSource().getTable();
}
In your action function (or where you want) call the table and get the context :
this._table.getSelectedContexts();

Cannot programmatically add content to simple HTML DIV Element in XML view

I have a simple XML view (fragment) like this:
<html:div id="holder"></html:div>
I want to add content programmatically like this:
var holder = this.byId("holder");
var label = new sap.m.Label({
text: "Label"
});
holder.addContent(label);
Effect is nothing, no error, no added content.
Why does it not work?
This is because content is not an aggregation (an easy mistake to make, since content usually is an aggregation).
sap.ui.core.HTML's content metadata object is a property of type string. From the jsdoc:
HTML content to be displayed, defined as a string.
You will need to use a different container for your label, such as sap.ui.layout.VerticalLayout, or you could just use raw HTML to stick in your holder object, rather than that sap.m.Label type.
Here is a jsbin that takes the XML view part of this question out of the equation.
Note: See #hirse's comment below for an important distinction when using html:div in XML views
The HTML element and the UI5 Controls are not directly compatible. UI5 Controls are JavaScript objects that have a render function. The render function creates a html fragment on demand. That html fragment ist then inserted into the page.
I have never tried it, but a solution could be to use the placeAt() method of your label:
label.placeAt("holder");
If you are using an XML View, the holder id will be prefixed. Then you should use something like this:
label.placeAt(this.getView().createId("holder"));
You can get DOM element of UI5 control by using getDomRef of sap.ui.core.Element class.
Then add your content to this DOM element by using placeAt()
Here is working example.

Get property data from outer context

I am trying to access the parent data from a nested template.
According to this https://github.com/BorisMoore/jsrender/issues/34, I am trying to do somehing similar but I am getting Error: data.parent is undefined.
In my 'master' template I have the declaration
{{for Rooms tmpl="#RoomTmpl" layout=true /}}
and in the #RoomTmpl
<script id="RoomTmpl" type="text/x-jsrender">
{{:parent.data.Room1Label}}
{{for #data}}
{{:RoomName}}
{{/for}}
</script>
I tried various combinations but always I get an error
{{:parent.parent.data.Room1Label}}
{{:#data.parent.parent.data.Room1Label}}
{{:#data.parent.data.Room1Label}}
{{:#data.parent.Room1Label}}
Does anyone knows how to do this?
-- SOLUTION --
the correct synatx is
#parent.parent.data.Room1Label
You want to step up through the views and get the data from a parent view. A view is the result of rendering a template, and so nested block tags, such as {{for...}}...{{/for}} or {{if...}}...{{/if}} will add child views. If you pass an array to a 'for' tag, {{for myArray}}, it will iterate over the data array and it leads to one child view whose data property is the array, and that view will then have collection of child views for each rendered item.
So you need to know that #view is the view, #view.parent is the parent view, #view.parent.data is the parent view's data etc. #foo is short for #view.foo, so #data is the data, #parent the parent view etc.
Now the only issue is to count the number of parents correctly. You can debug into what is going on by adding a method to your data: { ... properties ..., test: function() {debugger;} }. Now add {{:test()}} anywhere in a template for that data item, and step into the compiled temmplate: look at the view, its parents etc.
Another trick. If you set a template variable on any block tag, you can then access it from any nested template. So In your example, you can put
{{for Rooms tmpl="#RoomTmpl" ~label=Room1Label layout=true /}}
and then access it from a nested template as:
{{:~label}}
That way you no longer need to worry about stepping up through the parent views.

How do I avoid repetition when passing variables from the Controller/Action to the Layout

I am currently working on a project developed using Zend Framework, based on the structure of my web page design I have reached a point where I have to pass a small number of variables to my layout from each Controller/Action. These variables are:
<?php Zend_Layout::getMvcInstance()->assign('pageId', 'page1'); ?>
<?php Zend_Layout::getMvcInstance()->assign('headerType', '<header id="index">'); ?>
The reason for passing this information is firstly, I pass the page id as the multi column layout may change depending on the content being displayed, thus the page id within the body tag links the appropriate CSS to how the page should be displayed. Secondly I display a promotional jQuery slider only on the index page, but I need the flexibility to have it displayed on potentially multiple pages in case the wind changes and the client changes their mind.
My actual question: Is there a more appropriate method of passing this information to the Layout that I am overlooking?
I am not really questioning whether the information has to be sent, rather is there some Zend Framework feature that I have, in my haste, overlooked which would reduce the amount of repetitive redundant code which may very well be repeated in multiple Actions within the same controller?
You could turn that logic into an action helper than you can call from your controllers in a more direct way. You could also make a view helper to accomplish the same thing but view helpers usually generate data for the view rather than set properties.
// library/PageId.php
class Lib_PageId extends Zend_Controller_Action_Helper_Abstract
{
public function direct($title, $pageId, $headerType)
{
$view = $this->getActionController()->view;
$view->headTitle()->append($title);
$view->pageId = $pageId;
$view->headerType = $headerType;
}
}
In your controller actions you can now do this:
$this->_helper->PageId('Homepage', 'page1', 'index');
// now pageId and headerType are available in the view and
// Homepage has been appended to the title
You will also need to register the helper path in your Bootstrap like this:
protected function _initActionHelpers()
{
Zend_Controller_Action_HelperBroker::addPrefix('Lib');
}
Doing it like that can reduce the amount of repetitive code and remove needing to assign the values from the view. You can do it in the controller very quickly. You can also have default values in the case that the helper hasn't been called.
You shoudn't really be passing anything from the view to the layout, for a start the view should be included IN the layout, not the other way around.
So, setting your page title should be done using similar code to what you have, but inside the controller action being called:
$this->view->headTitle()->append('Homepage');
And the other two issues - you need to rethink as I stated to begin with. Maybe you're misunderstanding the layout/view principle? If you include the different views per action, then you simply change the div id when needed, and include the header for your banner only in the index.phtml file.