How to validate and scroll to sap.m.RadioButtonGroup - sapui5

The task is straight forward:
The user has to select an entry in a group of radio buttons, otherwise the validation should fail.
In order to be nice to the user, I'd also like to scroll to that RadioButtonGroup when its validation fails since it's further down and may not be in sight (as in my example linked below).
The implementation is not so straight forward to me because RadioButtonGroup does not have the property required.
Here's my code example - the RadioButtonGroup is in file SomeView.view.xml at line 420...
I have not really tried anything so far because I have no idea what to try.
Does anyone know if this is possible without compromising the existing validator implementation (that I got from here)?

The implementation is not so straight forward to me because RadioButtonGroup does not have the property required.
Only sap.m.Label needs the property required="true". Not the control sap.m.RadioButtonGroup itself.
The control sap.m.RadioButtonGroup has a property called selectedIndex. In your Code-Snippet you set the property selectedIndex="-1". If the user selects an sap.m.Radiobutton the selectedIndex will change to 0..3. Let's capture that with sap.ui.model.type.Integer.
// JSON dummy data
var oData = {
text : "Test",
userEmail : "i.am#groot.com",
number : 50,
date : "01.01.2021",
selectedIndex: -1
};
<Label text="Final answer?" required="true"/>
<RadioButtonGroup
id="answer"
columns="4"
selectedIndex="{ path: '/selectedIndex',
type: 'sap.ui.model.type.Integer',
constraints : {
minimum : 0,
maximum : 3
}}">
<RadioButton id="A1" text="A"/>
<RadioButton id="B1" text="B"/>
<RadioButton id="C1" text="C"/>
<RadioButton id="D1" text="D"/>
</RadioButtonGroup>
The Validator.js only checks for following properties
aValidateProperties = ["value", "selectedKey", "text"]
so we add the propertie selectedIndex
aValidateProperties = ["value", "selectedKey", "text", "selectedIndex"]
Validation works now ! Code-Example
In order to be nice to the user, I'd also like to scroll to that RadioButtonGroup when its validation fails since it's further down and may not be in sight (as in my example linked below).
See this offical Example

Related

How to synchronize control values within different views

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"/>

Setting multiple properties of a component with one function

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

How to get element's id of another view SAPUI5

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

Adding Bootstrap 3 popover breaks JQuery Validation Plugin

I have a form, which I'm validating using JQuery Validation plugin. Validation works file until I add a Bootstrap 3 popover to the text field with name "taskName" (the one being validated) (please see below) . When I add the popover to this text field, error messages are repeatedly displayed every time the validation gets triggered. Please see the code excerpts and screenshots below.
I've been trying to figure out what is happening, with no success so far. Any help will be greatly appreciated. Thanks in advance!
HTLM Excerpt
The popover content
<div id="namePopoverContent" class="hide">
<ul>
<li><small>Valid characters: [a-zA-Z0-9\-_\s].</small></li>
<li><small>Required at least 3 characters.</small></li>
</ul>
</div>
The form
<form class="form-horizontal" role="form" method="post" action="" id="aForm">
<div class="form-group has-feedback">
<label for="taskName" class="col-md-1 control-label">Name</label>
<div class="col-md-7">
<input type="text" class="form-control taskNameValidation" id="taskName" name="taskName" placeholder="..." required autocomplete="off" data-toggle="popover">
<span class="form-control-feedback glyphicon" aria-hidden="true"></span>
</div>
</div>
...
</form>
JQuery Validate plugin setup
$(function() {
//Overwriting a few defaults
$.validator.setDefaults({
errorElement: 'span',
errorClass: 'text-danger',
ignore: ':hidden:not(.chosen-select)',
errorPlacement: function (error, element) {
if (element.is('select'))
error.insertAfter(element.siblings(".chosen-container"));
else
error.insertAfter(element);
}
});
//rules and messages objects
$("#aForm").validate({
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
success: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
});
$('.taskNameValidation').each(function() {
$(this).rules('add', {
required: true,
alphanumeric: true,
messages: {
required: "Provide a space-separated name."
}
});
});
});
Bootstrap 3 popover setup
$('[data-toggle="popover"]').popover({
trigger: "focus hover",
container: "body",
html: true,
title: "Name Tips",
content: function() { return $('#namePopoverContent').html();}
});
The screenshots
First Edit
It seems I did not make my question clear, so here it goes my first edit.
I'm not using the popover to display the error messages of the validation. The error messages are inserted after each of the fields that fail validation, which is precisely what I want. Hence, this question does not seem to be a duplicate of any other question previously asked.
Regarding the popover, I just want to add an informative popover that gets displayed whenever the user either clicks the text field "taskName" or hovers the mouse over it. Its role is completely independent of the validation.
The question is, then, why adding the (independent) popover is making the validation plugin misbehave, as shown in the screenshots.
I had the very same issue a few days ago and the only solution I found was to use 'label' as my errorElement:.
Change the line errorElement: 'span' to errorElement: 'label' or simply removing the entire line will temporarily fix the issue. ('label' is the default. )
I am not completely sure what the JQ validate + BS popover conflict is, but I will continue to debug.
After some debugging I think I found the issue.
Both jQuery validate and bootstrap 3 popovers are using the aria-describedby attribute. However, the popover code is overwriting the value written by jQuery validate into that attribute.
Example: You have a form input with an id = "name", jQuery validate adds an aria-describedby = "name-error" attribute to the input and creates an error message element with id = "name-error" when that input is invalid.
using errorElement:'label' or omitting this line works because on line 825 of jquery.validate.js, label is hard-coded as a default error element selector.
There are two ways to fix this issue:
Replace all aria-describedby attributes with another attribute name like data-describedby. There are 4 references in jquery.validate.js. Tested.
or
Add the following code after line 825 in jquery.validate.js. Tested.
if ( this.settings.errorElement != 'label' ) {
selector = selector + ", #" + name.replace( /\s+/g, ", #" ) + '-error';
}
I will also inform the jQuery validate developers.
The success option should only be used when you need to show the error label element on a "valid" element, not for toggling the classes.
You should use unhighlight to "undo" whatever was done by highlight.
highlight: function(element) {
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-ok').addClass('glyphicon-remove');
},
unhighlight: function(element) {
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
$(element).parent().find('.form-control-feedback').removeClass('glyphicon-remove').addClass('glyphicon-ok');
}
(The success option could also be used in conjunction with the errorPlacement option to show/hide tooltips or popovers, just not to do the styling, which is best left to highlight and unhighlight.)
Also, I recommend letting the Validate plugin create/show/hide the error label element, rather than putting it the markup yourself. Otherwise, the plugin will create its own and ignore the one you've created.
In case you were unaware, you cannot use the alphanumeric rule without including the additional-methods.js file.

ASPxGridView: How to disable GridViewDataColumn when a GridViewDataCheckColumn is checked or unchecked?

I have an ASPxGridView with the following columns:
<dx:GridViewDataCheckColumn FieldName="ProtocolEnabled" Caption="Protocol Enabled">
<DataItemTemplate>
<asp:Literal ID="ltProtocolEnabled" runat="server" />
</DataItemTemplate>
</dx:GridViewDataCheckColumn>
<dx:GridViewDataColumn FieldName="ProtocolCount" Width="0" Caption="Protocol Count">
The checkbox column has a template with a literal in it so I can display Yes/No instead of an empty checkbox, but that's probably TMI. What I need to do is this:
In edit mode: When ProtocolEnabled is checked, I need to enable the ProtocolCount textbox. When ProtocolEnabled is unchecked, I need to disable ProtocolCount and set its text to 0.
I am not asking for a step-by-step, but a general pointer in the right direction. I would like to use callbacks if at all possible. I also promise I will not delete this question as you are answering it =P.
Update: Thanks to answerer, I was sent in the direction I needed to go. Here's the code I used:
<dx:GridViewDataCheckColumn FieldName="ProtocolEnabled" Caption="Protocol Enabled" CellStyle-HorizontalAlign="Left">
<DataItemTemplate>
<asp:Literal ID="ltProtocolEnabled" runat="server" />
</DataItemTemplate>
<PropertiesCheckEdit>
<ClientSideEvents CheckedChanged="function(s,e) {ProtocolEnabledChecked(s);}" />
</PropertiesCheckEdit>
</dx:GridViewDataCheckColumn>
<dx:GridViewDataColumn FieldName="ProtocolCount" Width="0" Caption="Protocol Count">
function ProtocolEnabledChecked(ck) {
var x = gvApplicationServer.GetEditor("ProtocolCount");
if (ck.GetValue()) {
x.enabled = true;
}
else {
x.SetValue(0);
x.enabled = false;
}
}
It's clientside code instead of callback.
First of all Check this for Accessing Controls Contained within Templates
To show Yes/No
On HtmlRowCreated Event access control and set it's text property after finding the control in the
Literal literal = ASPxGridView1.FindRowCellTemplateControl(e.VisibleIndex,
ASPxGridView1.Columns["Name"] as GridViewDataColumn, "ASPxButtonEdit1") as Literal ;
literal.Text = (bool)grid.GetRowValues(e.VisibleIndex, "columnName") ? "Yes" : "No";
In Edit Row Template Do as you did as above..
If you want to do some client side functionality then .. create client side event OnClientClick and use checkbox client side method. chkclientinstanceName.getValue(); or other to check with it is checked or not..
these controls are client accessible so enable/ disable by using txtClientName.SetEnabled(true/false);
for more help go to the
DevExpress.Web.ASPxEditors ClientScript namespace..
Try this step by step .. hope it will be helpful..