Data attribute in XML View elements - sapui5

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.

Related

In an Office Addin Is it possible to specify image just from ribbon xml?

I started by creating xml from the ribbon designer. I already have a resource file containing button images.
I got lines like
<button id="btnInsEq2" onAction="BtnInsEq2_Click" showLabel="false" />
which I changed to
<button id="btnInsEq2" onAction="BtnInsEq2_Click" image="InsEquation2" showImage="true" showLabel="false" />
There is some image called InsEquation2 in my resource file, which is accessible at runtime. I tried many variations on "InsEquation2" including the full path of the file. Nothing works.
I see answers where images are loaded in code. Is that really necessary or can I do it simpler in the xml?
By the way a line like
<button id="btnInsEq3" onAction="BtnInsEq3_Click" imageMso="Bold" label=" " showLabel="false" />
works fine. But I dont want B!
I would use the getImage tag like:
<button id="btnInsEq2" onAction="BtnInsEq2_Click" showLabel="false" getImage="GetImage"/>
which requires a callback. Assuming you write in C# it could look something like:
public Bitmap GetImage(Office.IRibbonControl control)
{
return new Bitmap(Properties.Resources.RelevantImage);
}
There are two ways for loading images in the Ribbon XML.
The getImage callback for each element where it is supported.
The loadImages callback for the customUI element where all images are loaded.
So, if you want to specify image names in the ribbon XML markup you need to implement the loadImages callback for the customUI element.
The <customUI> element's loadImage attribute enables you to specify a callback that can load all images. After you set up this callback procedure, Office calls the callback procedure and passes the string from the image attribute for each control that loads images. You do not need to implement the getImage callback multiple times, such as one time for each control that requires images. For example, a customization might include markup like the following.
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
loadImage="GetImage">
<!-- Later in the markup -->
<button id="myButton" image="mypic.jpg" />
To supply the button's image, Office calls the GetImage function. It passes the parameter "mypic.jpg" and expects an IPictureDisp object in return. By using this technique, you can write a single callback procedure that returns all the images your customization needs, without having to write each individual control's getImage callback. Note that the loadImage callback is not called again when you call the Ribbon's Invalidate method or InvalidateControl method. For the controls that need to change images dynamically at run time, use the getImage callback.
So now I have found out what to put in the GetImage function suggested by #EugeneAstafiev . Basically you need to iterate through the resources and find the one indicated by the imageId input parameter. Here it is in XML / VB
<customUI xmlns="http://schemas ... loadImage="GetButtonImages" >
..
<group id="InsertEquations" label="Add" image="InsEquation">
<button id="btnInsEq2" onAction="BtnInsEq2_Click" image="InsEquation2"/>
Public Function GetButtonImages(imageId As String) As Bitmap 'was IPictureDisp as specified in documenation
'enumerate resources ...
'see https://stackoverflow.com/questions/4656883/how-enumerate-resources-inside-a-resx-file-programmatically
Dim ErrorBitmap As New Bitmap(My.Resources._Error)
Dim ResMan As New ResourceManager("EquationAcc.Resources1", Assembly.GetExecutingAssembly)
Dim ResSet As ResourceSet
ResSet = ResMan.GetResourceSet(CultureInfo.CurrentCulture, True, True)
For Each Item As DictionaryEntry In ResSet
'Debug.WriteLine(item.Key) 'appears in window from Debug / windows / output
If Item.Key = imageId Then
Return Item.Value
End If
Next
Return ErrorBitmap
End Function
Also needed Imports System.Drawing, .Collections, .Globalization, .Reflection, .Resources
for declarations and
.Diagnostics, stdole for some now unused declarations
EquationAcc is the project, Resources1.resx the resource file
If there is a better way I'd love to know.

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");

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

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

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.

How to get id of invisible element in fragment?

I have a Dialog in fragment:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<Dialog title="{i18n>AddNewItem}" resizable="true" draggable="true">
<content>
<MessageStrip
id="failMsg"
visible="false"
text="{i18n>SensorTypesCreateFail}"
type="Error"
showIcon="true"/>
</Dialog>
</core:FragmentDefinition>
As in UI5 doc:
Retrieving a control instance when the fragment is not part of a view:
When no fragment ID was given: myControl = sap.ui.getCore().byId("myControl")
When a fragment ID myFrag was given: myControl = sap.ui.core.Fragment.byId("myFrag", "myControl")
If there is no visible="false", I can get this MessageStrip by sap.ui.getCore().byId("failMsg").
But I found that with visible="false", id of MessageStrip is sap-ui-invisible-failMsg, I failed to found proper API to get it.
Of course I can use sap.ui.getCore().byId("sap-ui-invisible-failMsg"), but I am not sure whether this ID will change after I deploy it to FLP, and as #schnoedel said in another question:
Beware that the prefixes like -- and --- used by the framework may change in the future. Thats why it's recommended to use the public api functions the framework supplies like byId() and createId().
So, is there any better way to get it?
Update:
Change my code from:
this[dialogName] = sap.ui.xmlfragment("namespace." + dialogName, this);
this.getView().addDependent(this[dialogName]);
To
this[dialogName] = sap.ui.xmlfragment(dialogName, "namespace." + dialogName, this);
this.getView().addDependent(this[dialogName]);
And now my id is sap-ui-invisible-dialogName--failMsg...
It depends on what you want to achieve after getting the ID. If you just want to change a property you could do it without any ID via a Model.
For that you can assign a Model field (i.e. baseModel>/visable) to the visable property and once it should be changed you change the model and via two way binding it updates the control.
code to change a model:
this.getView().getModel("nameOfUrModel").setProperty("property", "value")
for more information about this just check the walkthrough tutorial on
https://sapui5.hana.ondemand.com/
And if you for whatever reason really need the ID:
https://sapui5.hana.ondemand.com/#docs/api/symbols/sap.ui.core.Fragment.html
here you find the command:
sap.ui.core.Fragment.byId(sFragmentId, sId)
It should be able to return the Control your using
Hope that helps
Eric
You were very close to the solution. After adding dialogName for the fragment ID in its creation, you just have to call the API ...:
sap.ui.require(["sap/ui/core/Fragment"], Fragment => Fragment.byId(dialogName, "failMsg"));
... to get the control instance as mentioned here.
However, regardless whether you provided a fragment ID or not, you can easily ignore the render prefix "sap-ui-invisible-" at any time - Meaning that you could've also been able to get the control instance via sap.ui.getCore().byId("failMsg") instead of sap.ui.getCore().byId("sap-ui-invisible-failMsg") even if the control is invisible.