Cannot pass model data from aggregation binding to block for lazy loading in xml view - sapui5

I'm building an SAPUI5 Fiori application from the project template "SAP Fiori Master-Detail Application" in SAP Web IDE. I connect to an OData Service that gives me this nested structure (bold text represents a navigation property):
File
Properties...
ToRegister (returns collection of Registers)
Properties...
ToDocumentType (returns collection of DocumentTypes)
Properties...
ToDocument (returns collection of Documents)
Properties...
Displaying data in the Detail View is very slow, so I'm trying to use sap.uxap.ObjectPageLayout's lazy loading feature. For that, I have to extract parts of my view and put them into a custom block. This is accomplished by deriving from sap.uxap.BlockBase, according to this example (code here). Unfortunately, I couldn't find any examples that use aggregations/aggregation bindings.
I use XML Views. My Detail View looks like this:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m">
<ObjectPageLayout sections="{ToRegister}">
<sections>
<ObjectPageSection title="{RegisterName}" subSections="{ToDocumentType}">
<subSections>
<ObjectPageSubSection title="{Description}" >
<blocks>
<m:List items="{ToDocument}">
<m:CustomListItem>
...code for displaying properties...
</m:CustomListItem>
</m:List>
</blocks>
</ObjectPageSubSection>
</subSections>
</ObjectPageSection>
</sections>
</ObjectPageLayout>
</mvc:View>
This code does not use custom blocks. This is the slow version, but it works and displays the data correctly. Notice that the bold navigation properties are placed in curly braces (e.g. <m:List items="{ToDocument}">).
For my custom block, I extracted the <m:List> part into a seperate view:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m">
<m:List items="{Documents}">
<m:CustomListItem>
...code for displaying properties...
</m:CustomListItem>
</m:List>
</mvc:View>
Notice here that the property in the curly braces is not {ToDocument} anymore but {Documents}. That's because of the model mapping that has to be introduced in the original Detail View for this to work (see section Model Mapping in this article). I modified the Detail View like this:
<mvc:View
xmlns="sap.uxap"
xmlns:mvc="sap.ui.core.mvc"
xmlns:m="sap.m"
xmlns:attachmentblock="pft7.blocks.FileAttachmentList">
<ObjectPageLayout sections="{ToRegister}">
...
<blocks>
<attachmentblock:Block mode="Expanded">
<ModelMapping
externalModelName="ToDocumentType"
internalModelName="Documents"
externalPath="/ToDocument" />
</attachmentblock:Block>
</blocks>
...
</ObjectPageLayout>
</mvc:View>
It's pretty much the same. I added the xml namespace attachmentblock (which points to my custom block) and used it to replace the <m:List> child of <blocks>. Notice the attribute internalModelName of the <ModelMapping> node. It can be freely chosen and just has to be the same as the model name used in the block's view.
Finally, the problem: With this modification, my Detail View does not display the document properties anymore. Instead, it just displays the text "No data". I added some dummy text and it was properly displayed in my Detail View, so the actual inclusion of the custom block itself seems to work.
I suspect that I got the <ModelMapping> part wrong, but I don't know how to set the attributes correctly. I couldn't find any examples that use aggregations and navigation properties for this, so I'm pretty clueless. The console does not log any errors.

Can you check whether it works if you use an “Object Page with LazyLoading without blocks” as seen in this sample here?
In addition to this, you might also want to check on the reasons for the slow “Display of Detail View” via OData service.
Very often this is caused by long database access times.
If your backend is an R/3-ABAP system, you might want to check your OData service with an SQL-Trace (transaction ST05)
for long database response times caused by unnecessary requests or missing indexes.

Related

UI5 set a table content density/margins independent of the rest of UI style

In UI5 there are several pre-defined density / margins modes.
Can I set for a table a compact content density (sapUiSizeCompact), while the rest of UI should continue to use sapUiSizeCozy? In the list of sap.m.Table properties I can't find something like class/style.
.addStyleClass should work for every control. I've tested class in XML and it works too.
XML:
<Table class = "sapUiSizeCompact" />
JS:
this.byId("myTable").addStyleClass("sapUiSizeCompact");

SAPUI5 - How to change the table in the same View?

situation:
2 diferent tables, 1 view;
want to achieve:
I've got to change the table when I change the selectedKey in a select.
I already tried to create fragments with each table and call the one I want when I change the select.
fragment1 - table 1
fragment2 - table 2
select - oEvent (onChangeSelect) - getSelectedKey - if(key === 1) - call fragment1, if(key === 2) - call fragment2, but it gives me an error.
I don't know if this is correct and I'm not finding any infos related online.
If someone can show some links or give me just an idea on how can I do this, it would be perfect.
You could use the concept of binding to achieve this without coding. assume you have something like that in your view:
<Select><!-- ... --></Select>
<core:Fragment ... /><!-- table 1 -->
<core:Fragment ... /><!-- table 2 -->
First, add a model to your app, which will contain the information about the status of the app (which of the fragments is visible currently). Easiest way is to start with an empty JSON model. If you are familiar with manifest.json config, add it there. if not, you could add this in your Components init method:
this.setModel(new JSONModel(), "config");
This way, the new empty model is available under the name "config" in all the views of the app.
Now, we will add a visibility flag, which will control the visibility of your fragments (tables), and can be changed by the select:
<Select selectedKey="{config>/selectedFragmentKey}">
<core:Item key="" text="Please choose one" />
<core:Item key="showFragment1" text="Show Fragment 1" />
<core:Item key="showFragment2" text="Show Fragment 2" />
</Select>
This new version will store the key of the selected item in the config model in the path /selectedFragment. Add visibility flag to fragments (tables):
<!-- table 1 is only visible if "Show Fragment 1" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment1'}" .../>
<!-- table 2 is only visible if "Show Fragment 2" was selected -->
<Table visible="{= ${config>/selectedFragmentKey} === 'showFragment2'}" .../>
and you're done.
Main lesson here: learn to use bindings.
If I understood the question correctly, you have a selection control (which has 2 entries), and a table in the view. Based on the selection item, you want to display ONE of the tables at any given point of time. I am also assuming here, that the tables are purely used in the current view only & not really a "re-useable" thing - which gets used in different places of the app (which then renders the use of fragments an overkill).
I am assuming, both tables are bound (although to different data sets).
In that case, consider using the "visible" property of the appropriate table to "false".
Remember - all UI components, including tables inherit from sap.ui.core.Control - which has the "visible" property.
In API reference for table : https://sapui5.hana.ondemand.com/#/api/sap.ui.table.Table%23methods/Summary
If you scroll down to "Borrowed from:" section, you will see the "setVisible" and "getVisibe" methods.
So, in the event fired during selection change, you can grab the table control & set the visibility using the aforementioned methods.
Alternatively, you can also set an expression binding directly on the table control in XML, which evaluates to "true" or "false" (perhaps using a formatter).
Let me know if you need further info on this.
Best Regards,
Gopal Nair.

Adding content to XMLView via "addContent" doesn't work

I have the following XMLView:
<mvc:View
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:data="sap.chart.data"
xmlns:viz="sap.viz.ui5.controls"
xmlns:con="sap.suite.ui.commons"
controllerName="MY_NAMESPACE.controller.ChartView"
xmlns:html="http://www.w3.org/1999/xhtml"
>
<!-- Panel here -->
</mvc:View>
Now, in my controller, I want to dynamically add a sap.m.Panel to the view.
In my onInit function, I pass the object of the current view to the method that creates the Panel and adds it to the view.
onInit: function() {
var sUrl = "/sap/opu/odata/sap/MY_ODATA_SERVICE/",
oModel = new ODataModel(sUrl), // v2
oCurrentView = this.getView();
this.getView().setModel(oModel);
this._createPanel(oCurrentView);
this._createChartContainer();
this._initializeCharts();
this._showCharts();
},
_createPanel: function(currentView) {
var sId = this._globals.panelId;
var oViewPanel = new Panel(sId, {
width: "auto"
}).addStyleClass("sapUiSmallMarginBeginEnd");
this._globals.panelState = oViewPanel;
currentView.addContent(oViewPanel);
return currentView;
},
However, the Panel is never rendered:
But when I call the getContent function of the view, the panel is listed as an entry.
Clarification:
Creating a sap.m.Panel in the XMLView isn't a problem. Placing this bit of XML into the XMLView works.
<Panel id="chartPanel"
class="sapUiSmallMarginBeginEnd"
width="auto"
></Panel>
But, I need to create and append the sap.m.Panel object to the XMLView at runtime (in the controller), not in the XMLView.
Now, the problem:
With above posted controller code, the panel objects gets created. In fact, it even gets registered as a content aggregation of the XMLView, but it simply doesn't get rendered (see picture above).
Any suggestion on why and how this behaviour occurs are greatly appreciated.
Issue
this.getView().addContent(/*...*/) doesn't work.
Why
Currently, XMLView won't allow manipulating its content via APIs as the documentation warns:
Be aware that modifications of the content aggregation of this control are not supported due to technical reasons. This includes calls to all content modifying methods like addContent etc., but also the implicit removal of controls contained by the content aggregation. For example the destruction of a Control via the destroy method. All functions can be called but may not work properly or lead to unexpected side effects.
This is, at the time of writing (v1.64), still the case.
PS: The above limit applies only to XMLView. Other view types, such as JSView*, are not affected.
* sap.ui.core.mvc.JSView and sap.ui.jsview are deprecated. Use Typed Views instead (Applicable since v1.90).
try to put the Panel inside the XML view and give it a property visible="false".
<Panel id="panelId" visible="false">
</Panel>
In your function you could do something like this:
_createPanel: function(){
var oPanel = this.getView().byId("panelId");
oPanel.setVisible(true);
// Other Methods for Panel
}
With the oPanel instance you can execute all methods listed in the API:
https://sapui5.hana.ondemand.com/#/api/sap.m.Panel
Hope this helps :-)
Best regards

Data attribute in XML View elements

I'm trying to add data attributes to elements in a XML View as below:
<core:FragmentDefinition
xmlns="sap.m"
<VBox data-help-id="Some.String.Here">
...
</VBox>
</core:FragmentDefinition>
but couldn't find how to do it, unless I assign them via Controller.
Tried using CustomData namespace, but it only adds data, without adding the HTML attribute to the DOM element.
Any idea?
Thanks!
actually you can do something very close and associate data to your xmlView. This is available for xml views and more. Check this url for more details: Custom Data - Attaching Data Objects to Controls
What you would need to do is add a custom namespace to your xmlView:
xmlns:dataHelp="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"
...
<core:FragmentDefinition
xmlns="sap.m"
<VBox dataHelp:id="Some.String.Here" id="myBox"
...
</VBox>
</core:FragmentDefinition>
you are then able to set and consume this attribute in your binding and javascript/controller/event handler:
sap.ui.getCore().byId("myBox").data("id") // = Some.String.Here
You can only influence the attributes written to the DOM using the standard control properties. If the standard properties don't provide you with a way to set the right HTML attibutes, and you still want to get your own HTML attributes in the DOM, you'll need to subclass the control and write your own renderer. When you write your own renderer, you have full control over what's written to the DOM.
You can find more information on writing custom controls in Step 34 of the SAPUI5 Walkthrough.

Can i create Customizable Menu's in umbraco

im trying to create customizable menu in umbraco. i.e. user should be able to add /remove / edit any menu item in menu. (User will not be a developer)
but i dont know how to do that..i've heard about Macros but dont know much about them so cant use it.
I think this has been done before also..
Thanks in advance
Generally, your menu will reflect your node structure within umbraco. This is the easiest way to allow your clients control of the site's navigation. If there are nodes that you would rather not have in the menu, that you could use the umbracoNaviHide property on the document type.
Try out some of the starter kits that are available. They will come with macros that build the navigation based on your nodes and will give you a good idea of how they work. You can even start by using a starterkit and then just modify it as you like. That's what I would recommend as you start out with umbraco. Umbraco has about 4 or so built in starterkits and Our Umbraco has several more that other users have contributed.
To use the default navigation template provided with Umbraco:
If you log into the Umbraco backoffice and head over to the Developer section, should should see Scripting Files. Right-click Scripting Files and choose Create. Choose a filename, like Nav and and from the "Choose a template" menu, select Site Map, then click Create. You should end up with the following razor code:
#*
SITEMAP
=================================
This snippet generates a complete sitemap of all pages that are published and visible (it'll filter out any
pages with a property named "umbracoNaviHide" that's set to 'true'). It's also a great example on how to make
helper methods in Razor and how to pass values to your '.Where' filters.
How to Customize for re-use (only applies to Macros, not if you insert this snippet directly in a template):
- If you add a Macro Parameter with the alias of "MaxLevelForSitemap" which specifies how deep in the hierarchy to traverse
How it works:
- The first line (var maxLevelForSitemap) assigns default values if none is specified via Macro Parameters
- Next is a helper method 'traverse' which uses recursion to keep making new lists for each level in the sitemap
- Inside the the 'traverse' method there's an example of using a 'Dictionary' to pass the 'maxLevelForSitemap' to
the .Where filter
- Finally the 'traverse' method is called taking the very top node of the website by calling AncesterOrSelf()
NOTE: It is safe to remove this comment (anything between # * * #), the code that generates the list is only the below!
*#
#inherits umbraco.MacroEngines.DynamicNodeContext
#helper traverse(dynamic node){
var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap);
var values = new Dictionary<string,object>();
values.Add("maxLevelForSitemap", maxLevelForSitemap) ;
var items = node.Children.Where("Visible").Where("Level <= maxLevelForSitemap", values);
if (items.Count() > 0) {
<ul>
#foreach (var item in items) {
<li>
#item.Name
#traverse(item)
</li>
}
</ul>
}
}
<div class="sitemap">
#traverse(#Model.AncestorOrSelf())
</div>
This will produce a ul/li menu of the structure of your site. You plug this into your template by inserting the macro.
Take a look at the default Top Navigation template for XSLT or Razor. That should give you an idea of where to start and how navigation generally works in Umbraco. I second Douglas' answer that the Navigation usually mirrors the content structure in the Content section.
If you really want a setup where you add items to the nav independent of the Content tree structure, then use a Multinode Tree Picker on your Home Page and have that be the navigation in your Top Nav macro.