I have 3 views say first,second & third. I need to access a vertical layout's id of second view from first view. For which I have used sap.ui.getCore().byId("__xmlview1--spend_layt"); to retrieve it. It works but when I navigate to third view & come back, id changes to __xmlview28--spend_layt & the control not works. How to fix this?
EDIT :
First.view.xml
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" controllerName="budgetspend.controller.First" xmlns:html="http://www.w3.org/1999/xhtml"
displayBlock="true">
<App id="BA_APP">
<pages>
<HBox width="100%" height="100%" id="header_container">
<items>
<Image class="logo" src="../images/logo_new.png"/>
<Image class="header" src="../images/header-bg.png"/>
<html:ul class="tab">
<html:li>
<html:a id="onBud" class="tablinks active">Tab 1
</html:li>
<html:li>
<html:a id="onSpend" class="tablinks">Tab 2</html:a>
</html:li>
</html:ul>
<mvc:XMLView viewName="budgetspend.view.second"/>
</items>
</HBox>
</pages>
</App>
</mvc:View>
In the second view, I use 2 vertical layouts. one for tab 1 & another for tab 2. Only one visible at a time. Tab click events are written in First.controller.js. I don't know how to access ID's of vertical layouts(in second view) from First.
I am just exploring how standard html will work on SAPUI5 as I am aware of using sap.m.IconTab.
What you are facing is due to the dynamic creation of elements. Every time you leave a screen and goes back into it, the items are regenerated, but they can't have the same ID, since they were used before.
One solution to this is to create a Utils folder, and create an object, let's say UIHelper.
This file would look like this:
jQuery.sap.declare('my.app.Utils.UIHelper');
my.app.Utils.UIHelper = {
controllerInstance1 : null,
controllerInstance2 : null,
controllerInstance3 : null,
//Add "getters and setters" for each of these attributes (repeat for Controller 2 & 3)
setControllerInstance1 : function(oController){
this.controllerInstance1 = oController;
},
getControllerInstance1 : function(){
return this.controllerInstance1;
}
};
Now, at the controller file for each of your views, you add the following line at the top of the file:
jQuery.sap.require('my.app.Utils.UIHelper');
And on the onInit event of those controllers, you set the controller instance on the UIHelper object:
my.app.Utils.UIHelper.setControllerInstance1(this);
And when you want to get the different views from anywhere in the code, assuming that the views are assigned to one of the controllers, you can just use the following:
my.app.Utils.UIHelper.getControllerInstance1().getView();
Update in my answer after the update on the question:
To access the layout from the second view, you could do the following:
var layout = my.app.Utils.UIHelper.getControllerInstanceView2().byId('spend_layt');
Related
I am building a view with hardcoded data. which comes from home screen to the next view which is supposed to be a split view screen.
I am using FlexibleColumnLayout and inside of it begin and end layout to call views from my view folder. I have been following this tutorial. and even then my split view is not expanding on click.
My aggregation looks like this (I am not sure if it is correct):
App -> (View1, FCLView)
FCLView -> (MasterVIew, DetailView)
FCLView:
<mvc:View displayBlock="true" height="100%" xmlns="sap.f" xmlns:mvc="sap.ui.core.mvc">
<FlexibleColumnLayout id="flexibleColumnLayout" backgroundDesign="Solid">
<beginColumnPages>
<mvc:XMLView id="beginView" viewName="Ui5.Ui.view.MasterView"/>
</beginColumnPages>
<midColumnPages>
<mvc:XMLView id="detailView" viewName="Ui5.Ui.view.DetailView"/>
</midColumnPages>
</FlexibleColumnLayout>
</mvc:View>
component.js
...
getHelper: function () {
var oFCL = this.getRootControl().byId('flexibleColumnLayout'),
oSettings = {
defaultTwoColumnLayoutType: sap.f.LayoutType.TwoColumnsMidExpanded,
initialColumnsCount: 2
};
return FlexibleColumnLayoutSemanticHelper.getInstanceFor(oFCL, oSettings);
}
MasterViewController
...
onListItemPress: function (oEvent) {
MessageToast.show("here");
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
var oFCL = this.oView.getParent().getParent();
oFCL.setLayout(fioriLibrary.LayoutType.TwoColumnsMidExpanded);
}
I am following this tutorial
The scenario is, I have my first fullscreen view (View1) in which there's a button and it will navigate to second view which is a split view. (table and on clicking a row, the detail view will expand).
I am new to SAP, any kind of help would be appreciated.
It is difficult to debug your code excerpts, because there are several other things that can go wrong. (e.g. did you configure the use the sap.f.router instead of the sap.m.router in the manifest?)
The easiest way to start with a FlexibleColumnLayout is to begin with the UI5 MasterDetail-Template
This can be downloaded from Github or easily created in the WebIDE with a few clicks (new Project from template -> select Master Detail Application)
You can read more about the Template in the Demokit
I would like to know how to get the content of TextArea, assign the value to a variable, set it to a model, and then set the variable to another TextArea in another view. I have coded some examples and it works, but not on TextArea.
Here is the example code:
// In init of the Component.js
this.setModel(new JSONModel(), "TransportModel"); // JSONModel required from "sap/ui/model/json/JSONModel"
// In *.controller.js
this.getView().getModel("TransportModel").setProperty("/", {
"Serial": this.byId("mat_serial").getValue() // "mat_serial" == id of the Input box in XML view
});
In the last step, I set the Text from a different View (also XML and Input Box) with the Value of the Model Element.
<Text text="{TransportModel>/Serial}" />
That worked pretty well.
But how to do the same with the TextArea? How can I do it based on this model? The value that I want to use from the first TextArea should also be on a TextArea in another view.
UI5 supports two-way data binding. I.e. if the user changes something in the UI (e.g. user types something in the text area), that change will be reflected automatically in other bindings that listen to the change.
<!-- In view 1 -->
<TextArea value="{TransportModel>/Serial}" />
<!-- In view 2 -->
<Text text="{TransportModel>/Serial}" />
No need to get input values by hand. Simply let the framework synchronize the value.
How to use a local json model:
Create
initItemViewModel: function () {
return new JSONModel({
Serial: ""
});
}
this._oViewModel = this.initItemViewModel();
this.setModel(this._oViewModel, "TransportModel");
Using
this.getView().getModel("TransportModel").setProperty("/Serial", serial);
<Text text="{TransportModel>/Serial}" width="auto" maxLines="1"/>
I have a component in a list in a sapui5 XML view and I want to set multiple properties of that component with one function. E.g. I want to set text, status, tooltip and icon of an ObjectStatus together, because the values of those are all different facets of the same data. The issue is that i have to calculate the values to set to those properties from the model with the same relatively time-heavy function. If I write a separate formatter for each of those properties, it has to run the same function for each property. Instead of this I would like to write one function that runs this time-heavy function once and sets a value to all those properties at the same time.
To accomplish this, I have tried creating a sapui5 fragment that could be placed in the list and filled with different information by the createContent function for each instance of that fragment. However I cannot figure out how to do this.
In the view definitions I'm trying to instantiate the fragment like this:
<core:Fragment fragmentName="QuantificationParameter" type="JS" path="{project>}"/>
And then I'm trying to set different content to each instance of the fragment:
sap.ui.jsfragment("namespace.fragments.QuantificationParameter", {
createContent: function(oParentController) {
//Get the object bound to this list item
var derived; //Calculate some intermediate information from this object
return new sap.m.ObjectStatus({
icon: derived.icon,
text: derived.text,
state: derived.state,
tooltip: derived.tooltip
});
}
});
While debugging it seems that the createContent function of the fragment is run only once and I cannot figure out any way to access the data that I'm trying to bind to the fragment. Is there any way I can render different content to each instance of the fragment?
What you are searching for is called databinding.
But first of all: we do not use JS Fragments, due to the same reason we do not use JS views. Here s a little Blog written on that topic.
https://blogs.sap.com/2018/05/01/why-do-we-use-xml-views-rather-js-views-in-sapui5/
Now the databinding part:
I asume, that Fragment will have the same controlls for each instance and you just want the values to change. To do just that you need to create a JSONModel either in your BaseController or component.js. In this Model you store i.e. your Labels text.
Inside your Fragmet you bind that property to the label. Since JSONModels bindingmode is two way by default the Label will change dynamically if you update the model. You can update the model i.e. everytime the user clicks on one of your list items.
Framgmet example:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<f:SimpleForm
editable="true">
<f:content>
<Input
value="{baseModel>/inputA}"
type="Text"
placeholder="{i18n>placeHolder}"/>
<TextArea
value="{baseModel>/textA}"/>
<TextArea
editable="false"
value="{baseModel>/textB}"/>
</f:content>
</f:SimpleForm>
</core:FragmentDefinition>
creation of the model i.e in component.js:
var oBaseModel = new JSONModel({
inputA: "",
textA: "",
textB: ""
});
this.setModel(oBaseModel, "baseModel");
example for your press lit item funtion:
(should be in the controller of the view your list is located in)
onListPress: function (oEvent) {
var oLine = oEvent.getSource().getBindingContext("yourRemoteService").getObject();
this._oBaseModel.setProperty("/inputA", oLine.ListPropertyA);
this._oBaseModel.setProperty("/textA", oLine.ListPropertyb);
this._oBaseModel.setProperty("/textB", oLine.ListPropertyC);
}
You should really give that tutorial a go:
https://sapui5.hana.ondemand.com/#/topic/e5310932a71f42daa41f3a6143efca9c
Given two XML Views:
<mvc:View
controllerName="my.namespace.controller.First"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<Button press=".onBtnPress" />
</mvc:View>
<mvc:View
controllerName="my.namespace.controller.Second"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m">
<Button press=".onBtnPress" />
</mvc:View>
As expected, the press event is handled by First.controller.js or Second.controller.js.
Instead of duplicating the event handler code or implementing handlers in each Controller to chain/hand off the work, I want to declare a shared event handler.
According to docs this should be possible, using a naming convention for the handler:
Names starting with a dot ('.') are always assumed to represent a method in the controller.
Names containing a dot at a later position are assumed to represent global functions and are resolved by calling jQuery.sap.getObject with the full name.
So I change the handler and declare a shared object, like so:
First.view.xml:
<Button press="my.namespace.Shared.onBtnPress" />
Shared.js:
jQuery.sap.declare("my.namespace.Shared");
my.namespace.Shared = (function() {
var onBtnPress = function() {
console.log("button pressed");
};
return { onBtnPress : onBtnPress };
}());
Warning logged (debug sources) during view initialisation:
sap.ui.core.mvc.XMLView#__xmlview1: event handler function "my.namespace.Shared.onBtnPress" is not a function or does not exist in the controller. -
Calling jQuery.sap.getObject("my.namespace.Shared") yields undefined
Same issue when using sap.ui.define to make the object known.
Since UI5 1.69, it has become easier to share JS modules in XML view and fragment.doc
Here is an example: https://embed.plnkr.co/5G80I5HWObCuM5cG
<mvc:View controllerName="..."
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:core="sap.ui.core"
core:require="{ onButtonPress: 'my/shared/onButtonPress' }">
<Button text="Press" press="onButtonPress" />
</mvc:View>
As we can see, each button displays a different message depending on the view, even though the handler itself isn't included in the controller definition.
The this context is still the controller instance as documented in the topic Handling Events in XML Views:
As long as no event handler parameters are specified and regardless of where the function was looked up, it will be executed with the controller as the context object (this).
Your shard object looks weird
Try something like this:
sap.ui.define([], function() {
return sap.ui.base.Object.extend("my.namespace.Shared", function() {
onBtnPress : function() {
console.log("button pressed");
}
};
});
Also remember to put the object in the right directory.
I was able to copy-paste your sap.ui.define and verified it was created correctly jQuery.sap.getObject.
Make sure that your sap.ui.define has been called prior to your view being rendered.
You can set a breakpoint in the XMLTemplateProcessor where the event callbacks are handled for XML views for further debugging/timing issues.
If you look in the _resolveEventHandler function it should take you here which will perform the jQuery.sap.getObject.
Imagine a component with a root view like so:
rootView: 'myapp.view.App'
The app view looks like this:
<mvc:View
xmlns:mvc="sap.ui.core.mvc"
xmlns:core="sap.ui.core"
xmlns="sap.m"
controllerName="myapp.controller.App"
displayBlock="true">
<App id="myapp">
<mvc:XMLView id="page1" viewName="myapp.view.Page1" />
</App>
</mvc:View>
As you can see there is a controller set up for this view. The view myapp.view.Page1 is another XML view, has a controller of its own and contains a sap.m.Page control.
This Page1 controller cannot access any controls without specifying __xmlview0-- in front of the Id. I know it's in a view, because the App is in a view. createId will create something relative to the current controller, so it will return __xmlview0--page1.
For instance, I can get the app from my myapp.controller.Page, but I have to access using the view name too. The same goes for my pages. Navigating to a new page in the app looks like this:
var app = this.byId('__xmlview0--myapp');
app.to('__xmlview0--page1');
this.createId('myapp') will give me __xmlview0--page1--myapp.
I also tried getting the component itself out this.getOwnerComponent() and use the createId and getId methods, but this leaves me with __component0 instead of __xmlview0, so that's a no-go too.
edit
Not a super clear story. Maybe this will go you some visuals:
-Component.js
- App.view.xml <App id="myapp"></App>
- Page1.view.xml <Page id="page1"></Page>
- App.controller.js
- Page1.controller.js
//byId uses createId.
//myapp has id __xmlview0--myapp
this.createId('myapp') = __xmlview0--page1--myapp
this.getView().createId('myapp') = __xmlview0--page1--myapp
this.getOwnerComponent().byId('myapp') = __component0--myapp
this.getOwnerComponent().getView() is not a function
How do I determine through my controllers to add __xmlview0? Which createId method do I use? Is my rootView wrong and is there a more efficient way? How do I get myapp out of the component?
Since you are using the component and you have rootview mentioned in the component configuration there is an API to retrieve the root view
var comp = this.getOwnerComponent().getMetadata() // this is the view i am referring to here.
comp.getRootView()
You could write a recursively navigate this.parent() until you have the sViewName equal to the comp.getRootView(). This entry gets the rootview you are looking for and then sId gets you the ID which you can use to append.
I am not sure what you are trying to achieve but this is a hack i thought you can use to derive the rootview's ID.
Alternatively you can also specify the rootView ID while defining component something like this
rootView: {
viewName : "ui.sample.demo.view.App",
id : "Root",
type : "XML"
},