I'm trying to implement a JSF page in which the user should insert some data. In particular, when pressing a button a dialog box should appear asking for user input. The main problem is that the execution of the backing bean should be stopped waiting for a user response.
A toy example is the following.
JSF page:
<h:form id="label">
<p:dialog header="User input" widgetVar="dlg2"
visible="true" modal="false"
resizable="false" height="100" width="300">
<br />
<h:inputText value="#{userInputMB.userInput}"></h:inputText>
</p:dialog>
<p:commandButton action="#{userInputMB.pressButton}"></p:commandButton>
</h:form>
UserInputMB:
package jsfpackage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class UserInputMB {
private String userInput;
private boolean visualizeDialog = false;
public UserInputMB() {
}
public void pressButton() {
System.out.println("Executing the pressButton method..");
//here I need to visualize the dialog and wait for the user input
System.out.println(userInput);
}
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
}
public boolean isVisualizeDialog() {
return visualizeDialog;
}
public void setVisualizeDialog(boolean visualizeDialog) {
this.visualizeDialog = visualizeDialog;
}
}
In this example, when pressing the button, the pressButton method should visualize the dialog box and wait for the user input and then continue the execution.
I also found this similar question on stackoverflow:
Synchronous dialog invocation from managed bean
but my situation is quite different. I'm forced to implement this kind of behavior.
Thanks in advance!
The following sample contains a dialog and a button. The button prepares the input and opens the dialog. In the dialog the second button calls the action to process the input.
JSF:
<!-- dialog for input -->
<p:dialog id="inputDialog" widgetVar="inputDialog" header="Input here">
<p:inputText value="#{userInputMB.userInput}" />
<p:commandButton action="#{userInputMB.processInput}" />
</p:dialog>
<!-- Calls the action to prepare the input and updates and opens the dialog -->
<p:commandButton value="show dialog" action="#{userInputMB.prepareInput}"
oncomplete="PF('inputDialog').show()" process="#this" update=":inputDialog" />
Bean:
#ManagedBean
#SessionScoped
public class UserInputMB {
private String userInput;
public void prepareInput() {
userInput = "Please enter your input here";
}
public void processInput() {
if("inputYouWanted".equals(userInput)) {
System.out.println("hurray, correct input!");
}
}
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
}
}
Related
I'm using Xamarin.Forms and Prism to create my mobile app.
I have a screen with 2 entries. When entering the screen, I'd like to set the focus on the first entry.
Then after the user entered data in this entry and validated it, I'd like to set the focus to the second entry.
Based on first answer:
I should do something wrong. I've created a small new Prism project to test it :
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:testEntry"
x:Class="testEntry.Views.MainPage"
Title="{Binding Title}">
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label Text="Welcome to Xamarin Forms and Prism!" />
<local:MyEntry Placeholder="" x:Name="entry1" />
<Button Text="set focus on entry1" Clicked="Button_Clicked"/>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using Xamarin.Forms;
namespace testEntry.Views
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
entry1.Focus(); //Not Working
}
private void Button_Clicked(object sender, EventArgs e)
{
entry1.Focus(); //Working
}
}
}
MyEntry.cs (in Main project)
using Xamarin.Forms;
namespace testEntry
{
public class MyEntry : Entry
{
}
}
MyEntryRenderer.cs (in Android Project)
using Android.Content;
using Android.Views;
using Android.Views.Accessibility;
using Xamarin.Forms.Platform.Android;
namespace testEntry.Droid
{
public class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context)
{
}
public static void Focus(View view)
{
view.SendAccessibilityEvent(EventTypes.ViewFocused);
}
}
}
Unfortunately, still nofocus on my field :'(
Finally, and thanks to Saamer, I found another way of doing it by using EventAggregator.
public class FocusChanged : PubSubEvent<String> { }
Then in my view model :
IEventAggregator _ea;
public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator) : base(navigationService)
{
_ea = eventAggregator;
}
In the viewModel, whenever I want to set the focus to a field, I'm sending an event :
_ea.GetEvent<FocusChanged>().Publish("Source");
And in my view's code behind, I handle this event:
IEventAggregator _ea;
public MainPage(IEventAggregator eventAggregator)
{
InitializeComponent();
_ea = eventAggregator;
_ea.GetEvent<FocusChanged>().Subscribe(SetFocusOnControl); //Name of method which will handle this event
}
/// set the focus on entry based on parameter
/// each event value will set focus on a specific entry (example: source is linked to entry txtScanSrc)
private async void SetFocusOnControl(String fieldName)
{
Entry l_view;
switch(fieldName)
{
case "source": l_view = this.FindByName<Entry>("txtScanSrc"); break;
case "quantity": l_view = this.FindByName<Entry>("txtQty"); break;
case "tote": l_view = this.FindByName<Entry>("txtScanTote"); break;
case "pallet": l_view = this.FindByName<Entry>("txtScanPalout"); break;
case "destination": l_view = this.FindByName<Entry>("txtScanDest"); break;
default: l_view = this.FindByName<Entry>("txtScanSrc"); break;
}
await WaitAndExecute(500, () => { l_view.Focus(); });
}
There's a way of doing this using the Accessibility APIs of each of the platforms. Xamarin forms doesn't have all the platform features of accessibility yet so you d have to create a custom renderer and then call the focus method in a life cycle event of the page.
So calling this Focus function would cause the app to focus on that element. You generally don't want to do it because the app purposefully focuses on what it does so accessible users have a consistent experience. But if you really want to override the default behavior, in Android it's something like this
public static void Focus(View view)
{
view.SendAccessibilityEvent(EventTypes.ViewFocused);
}
And in iOS, you have to use the PostNotification apis which will be some variation of this
UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, entry element)
You can look more into Accessibility Focus to get the exact answer
I have set the Remote attribute to validate that my username is Unique but when I debug it's not firing. What am I messing up?
In my View Model here is the attribute and property:
[Required]
[Remote("VerifyUsername", "Account")]
public string Username { get; set; }
In my form my form attribute is:
<input asp-for="Username" class="form-control mb-4" placeholder="Username" />
<span asp-validation-for="Username"></span>
And in my controller I have tried:
public JsonResult VerifyUsername(string username)
{
if (!_user.UsernameUnique(username))
{
return Json($"{username} is already in use.");
}
return Json(true);
}
And the method format:
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyUsername(string username)
{
if (!_user.UsernameUnique(username))
{
return Json($" {username} is already in use.");
}
return Json(true);
}
I enter a usernam and click around and try tabbing... nothing gets the remote validation to fire. Anyone see what I am missing?
So I found it... kind of a facepalm. I was using another library that was loading another version of jquery. I was not getting an error though so that was weird. I removed that script reference for that other version of jquery and so it just had the current version and it all worked.
I'm completely new to CM and also to learn it I'm migrating an application from MVVM light to Caliburn Micro. In my original code, I had a VM which responds to some UI actions (via commands) to replace some text into a string. The position is given by the view, using the textbox selection.
So the VM has (1) a bound string property representing the textbox's text, (2) another bound string property to represent the new text to be added, and (3) needs to know selection start and length in order to replace the right portion of text with the new one.
In my original code, I had a custom DialogMessage-derived object sent in the VM command implementation with a couple of properties for selection data: when the command was issued, the message was sent, and the view received it and filled it with its textbox selection start and length; then the VM was called back and could use these data.
Which would be the best way of implementing this in CM? I'd prefer the VM to remain agnostic of the view, so I don't like too much the idea of accessing the view from it. I'd rather opt for a "message"-based mechanism like the above, but I'm not sure how I can implement it in CM: I would probably look at IResult, but most of the samples I find are related to coroutines and I'm not sure how to relate the void ReplaceText() method of the VM to the view code behind.
Could anyone point me in the right direction, and/or to some code samples about dialog-like interactions between VM 'command' methods and view? Thanks!
I'd probably look at the IResult option. You'll have access to the view so code that you would have had in the code behind can be in your Result and not in your VM.
Here is code from a ShowDialog result. I believe I grabbed it from the CM discussion group. Search the discussion group for ShowDialog for more examples. The GameLibrary sample that comes with CM also has some.
public class ShowDialog : IResult
{
private readonly Type _screenType;
private readonly string _name;
[Import]
public IWindowManager WindowManager { get; set; }
public ShowDialog(string name)
{
_name = name;
}
public ShowDialog(Type screenType)
{
_screenType = screenType;
}
public void Execute(ActionExecutionContext context)
{
var screen = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_screenType, null);
Dialog = screen;
WindowManager.ShowDialog(screen);
var deactivated = screen as IDeactivate;
if (deactivated == null)
Completed(this, new ResultCompletionEventArgs());
else
{
deactivated.Deactivated += (o, e) =>
{
if (e.WasClosed)
{
Completed(this, new ResultCompletionEventArgs());
}
};
}
}
public object Dialog { get; private set; }
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowDialog Of<T>()
{
return new ShowDialog(typeof (T));
}
}
edit: If you extend TextBox you can bind SelectedText.
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty SelectedTextProperty = DependencyProperty.Register("SelectedText", typeof(string), typeof(TextBoxEx), new PropertyMetadata("oa"));
public TextBoxEx()
{
SelectionChanged += UpdateDependencyProperty;
}
private void UpdateDependencyProperty(object sender, RoutedEventArgs e)
{
SelectedText = base.SelectedText;
}
public new string SelectedText
{
get { return GetValue(SelectedTextProperty).ToString(); }
set { SetValue(SelectedTextProperty, base.SelectedText); }
}
}
then:
<SLTest:TextBoxEx x:Name="MyTextBox2"
Grid.Row="1"
Width="200"
SelectedText="{Binding SelectedText, Mode=TwoWay}"
Text="This is some text." />
Let me introduce my issue by providing some of the code in question.
First my form object:
public class OrgChartForm {
List<OrgChartFormElement> orgChartFormElements;
public OrgChartForm() {
orgChartFormElements = new ArrayList<OrgChartFormElement>();
}
private OrgChartFormElement createOrgChartFormElementFromMprsStructureYear(MprsStructureYear structureYear){
OrgChartFormElement element = new OrgChartFormElement();
element.set.... // populate element based on attribute values from structureYear param
return element;
}
public void createOrgChartFormElements(List<MprsStructureYear> structureYears) {
orgChartFormElements = new ArrayList<OrgChartFormElement>();
for(MprsStructureYear structureYear:structureYears){
orgChartFormElements.add(createOrgChartFormElementFromMprsStructureYear(structureYear));
}
}
// expected getters and setters
}
The form contains a simple list of OrgChartFormElements
public class OrgChartFormElement {
private boolean selected;
private String elementLabel;
private Long id;
//default constructor, getters and setters
}
I am using context:component-scan and mvc:annotation-driven, so my controller looks like:
#Controller
public class OrganisationStatusController{
#Autowired(required=true)
// dependencies here
#RequestMapping(value="/finyear/{finyearId}/organisationstatus", method=RequestMethod.GET)
public String createRootOrg(#PathVariable(value="finyearId") Long finyearId, Model model) throws Exception {
List<MprsStructureYear> orgStructuure = getOrganisationService().getOrganisationStructureForFinyear(finyearId);
OrgChartForm orgChartForm = new OrgChartForm();
orgChartForm.createOrgChartFormElements(orgStructuure);
model.addAttribute("orgChartForm", orgChartForm);
return "finyear/organisationchart/view";
}
#RequestMapping(value="/finyear/{finyearId}/organisationstatus", method=RequestMethod.POST)
public String createRootOrg(#PathVariable(value="finyearId") Long finyearId,#ModelAttribute("orgChartForm") OrgChartForm orgChartForm, BindingResult result, Model model) throws Exception {
System.out.println("Found model attribute: " + model.containsAttribute("orgChartForm"));
List<OrgChartFormElement> elements = orgChartForm.getOrgChartFormElements();
System.out.println(elements);
return "redirect:/spring/finyear/" + finyearId + "/organisationstatus";
}
// expected getters and setters
}
The issue is with the POST handler. I realise that it isn't doing much now, but once I get it to work, I will be persisting the submitted values.
At the moment, the output i see from the two sysout statements are:
Found model attribute: true
[]
Here is my JSP snippet:
<sf:form modelAttribute="orgChartForm" method="post">
<c:forEach items="${orgChartForm.orgChartFormElements}" var="org" varStatus="status">
<sf:hidden id="${org.id}field" path="orgChartFormElements[${status.index}].id"/>
<sf:input id="${org.id}hidden" path="orgChartFormElements[${status.index}].selected"/>
<c:out value="${org.elementLabel}"/>(<c:out value="${org.id}"/>) - <c:out value="${status.index}"/>
</c:forEach>
<input type="submit" value="Submit" />
</sf:form>
When i make the GET request, the JSP renders, and i see my list of text input fields, with the expected values, which tells me that im using the spring-form tags properly. However, when i submit, the form backing object declared as a parameter (orgChartForm) in the POST handler method is initialised, but everything is null/default initialised. I don't know where the submitted data went! It seems that springMVC looses it, and simply constucts a new object.
I have used this pattern extensively in this application without a glitch. It just wont work here. I realise this is a special case in my application where the form field is not atomic but a list, However its really confusing me that the data binds in the GET request, but not on the POST.
Thanks in advance for any pointers!
I think the problem is that you are trying to bind an arbitrary number of form fields to an ArrayList, which is a list that has a predetermined size.
Spring has something called an AutoPopulatingList that is custom designed for this purpose. Please have a look at this link for more info on how to use it: http://blog.richardadamdean.com/?p=12
I think you will need to write PropertyEditorSupport for your class. Following is the example for your reference.
public class SampleEditor extends PropertyEditorSupport {
private final SampleService sampleService;
public SampleEditor(SampleService sampleService, Class collectionType) {
super(collectionType);
this.sampleService = sampleService;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
Object obj = getValue();
List list = (List) obj;
for (String str : text.split(",")) {
list.add(sampleService.get(Long.valueOf(str)));
}
}
#Override
public String getAsText() {
return super.getAsText();
}
}
In controller, you should bind it using #InitBinder as follows:
#InitBinder
protected void initBinder(HttpServletRequest request, WebDataBinder binder) {
binder.registerCustomEditor(List.class, "list", new SampleEditor(this.sampleService, List.class));
}
Hope this will solve your problem.
I am working on a requirement to highlight fields that have failed in red after JSF server side validation. No javascript can be used for validation. Is there a method to link server side validation with css style changes?
You could do this with a managed bean:
public class ValidBean {
private UIComponent myComponent;
public UIComponent getMyComponent() {
return myComponent;
}
public void setMyComponent(UIComponent myComponent) {
this.myComponent = myComponent;
}
public String getErrorStyle() {
FacesContext context = FacesContext
.getCurrentInstance();
String clientId = myComponent.getClientId(context);
Iterator<FacesMessage> messages = context
.getMessages(clientId);
while (messages.hasNext()) {
if (messages.next().getSeverity().compareTo(
FacesMessage.SEVERITY_ERROR) >= 0) {
return "background-color: red";
}
}
return null;
}
}
Request scope variable:
<managed-bean>
<managed-bean-name>validBean</managed-bean-name>
<managed-bean-class>stylevalid.ValidBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Sample view:
<f:view>
<h:form>
<h:inputText binding="#{validBean.myComponent}" styleClass="foo"
style="#{validBean.errorStyle}">
<f:validateLength minimum="6" />
</h:inputText>
<h:commandButton />
<h:messages />
</h:form>
</f:view>
The component is bound to the backing bean. If error messages have been queued for the component, it overrides its CSS class settings with its style attribute.
The seam framework makes this very easy. Check this out: http://www.redhat.com/docs/manuals/jboss/jboss-eap-4.2/doc/seam/Seam_Reference_Guide/JSF_form_validation_in_Seam.html