Collection/List property won't bind or update on form submit - spring-webflow

So I have set up a wizard interface with spring web flow that gradually populates a single form object/model. It works fine for the first few steps that have to populate single String or primitive properties and a String array (from a checkbox interface).
Then I have a List<String> property. It renders properly as multiple textboxes with correct initialized values. But when I edit the textboxes on the browser and submit, the values do not take effect on the form bean. It still has the initial values.
Below is the detailed set-up:
Web flow on-start which creates the bean:
<on-start>
<evaluate expression="new mypackage.MyFormBean()" result="flowScope.myFormBean" />
</on-start>
Here are the relevant parts of my form bean:
public class MyFormBean implements Serializable {
...
private List<SlotBean> slots;
...
public List<SlotBean> getSlots() {
return slots;
}
public void setSlots(List<SlotBean> slots) {
this.slots= slots;
}
...
}
public class SlotBean {
...
private int quantity;
...
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity= quantity;
}
...
}
I have a series of view-states in my web flow with simple string or number field bindings set-up that are initialized, displayed and saved without issues to the form.
This view-state generates any number of SlotBean objects then initializes quantity with 2. These are set to the slots property.
<view-state id="generate-criteria" model="disciplineCatalogue">
...
<transition on="next" to="slots-grid">
<evaluate expression="myService.generateSlots(myFormBean)"/>
</transition>
...
</view-state>
Here is the jsp fragment. All it does is render a bunch of textboxes. There's also a next button:
<form:form id="slotsGrid" modelAttribute="myFormBean" action="${flowExecutionUrl}">
...
<c:forEach var="slot" items="${myFormBean.slots}" varStatus="idx">
<form:input path="slots[${idx.index}].quantity" />
</c:forEach>
...
<button type="submit" id="next" name="_eventId_next">Next</button>
...
</form:form>
The above code displays correctly with the initial values (2). It generates multiple textboxes like below:
<input id="slots0.quantity" name="slots[0].quantity" type="text" value="2"/>
So when this page is on the browser, I change the values of the quantity textboxes to different values and click the "next" button. On my browser's network debugger, I see that the form values are sent to the server:
slots[0].quantity:3
slots[1].quantity:1
slots[2].quantity:2
Here is the relevant web flow entry for the next button.
<view-state id="slots-grid" model="myFormBean">
<binder>
...
<binding property="slots" />
</binder>
...
<transition on="next" to="finished">
<evaluate expression="myService.create(myFormBean)"/>
</transition>
...
</view-state>
So I put a break point on the myService.create(myFormBean) method and it shows that all the quantity fields are still set to the original "2". The new values didn't bind to myFormBean.slots.
Is there anything you can see in my set-up that looks wrong?
Thanks for any time you can put into this
Spring Framework 3.1.1
Spring-Webflow 2.3.1
Tomcat 6.0.18
Eclipse Indigo
Cross-posted in:
http://forum.springsource.org/showthread.php?127809-Collection-List-property-won-t-bind-or-update-on-form-submit

Related

In Blazor client, how to create a component for an interface?

From reusabilty point of view, I want to create a component for an interface. So I use it with different concrete objects.
For example, the interface is like this
interface ICalculation
{
double Calculate();
}
and the Test component is
<button #onclick="(() => SetResult())">Set</button>
#result
#code{
double result;
ICalculation Calculation;
void SetResult()
{
result = Calculation.Calculate();
}
}
so some where else in another component/page I have some thing like this
<Test inject CalculationA />
<Test inject CalculationB />
So I want to inject Different Calculations into different instances of this component. How can i get this?
I thought using dependency injection of net core, but that is for to inject one object for an interface.
Why important? It helps me to override requests to api, for example, admin and user have different requests but they see the same page structure.
In the Test component you would make it a normal parameter:
[Parameter]
public ICalculation Calculator { get; set; }
and then in 'some where else'
#inject CalculationA CalculationA
#inject CalculationB CalculationB
<Test Calculator="CalculationA" />
<Test Calculator="CalculationB" />
Or replace those '#inject` lines with normal instantiations (2x) because with multiple implementations you can't do DI on the interface anyway.

setting up a simple component with data binding

I am trying to set up a component with data binding. This is basically a seperate content view that would have a property Item of type Item and supports binding. The following is the definition for the binding:
public static readonly BindableProperty ItemProperty
= BindableProperty.Create(
nameof(Item), typeof(Item), typeof(ItemComponent), null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: ItemPropertyChanged);
private readonly ItemComponentViewModel vm;
static void ItemPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = (ItemComponent)bindable;
view.Item = (Item)newValue;
}
public Item Item
{
get => (Item)GetValue(ItemProperty);
set
{
SetValue(ItemProperty, value);
if (vm != null) vm.Data = value; // break point here
}
}
The item doesn't seem to get bound. The commented line had a breakpoint and doesn't break. The complete source code is here: https://github.com/neville-nazerane/xamarin-component-sample
The above code can be found in the ItemComponent class. This component is called in the MainPage class.
Update
Just to explain what I am trying to simulate and why:
Why do we use MVVM in pages? While we'll have better type safety and performance by using the behind code directly, when the page's logic gets bigger, it becomes cleaner to handle it with a view model and to have a view that is simply bound to it.
Why do we have components? So that we can reuse a UI we intend to use with some functionality. If this functionality becomes complex it might need a view model for the same reason explained above. Hence, if pages need view models, I don't see why components won't need them at some point too.
This being considered this does feel like a particle requirement without easy to find examples.
So after looking at your example it turns out it's a bit of a complicated problem. So if my explanation is not clear, please let me know.
Basically the problem lies in these 2 code pieces:
MainPage.xaml(line 14):
<local:ItemComponent Item="{Binding Demo}" />
ItemComponent.xaml.cs (line 43):
public ItemComponent()
{
InitializeComponent();
vm = new ItemComponentViewModel();
BindingContext = vm; //this breaks the functionality
}
The first part you tell it to bind to the Demo property, and as normal it looks for this property in it's BindingContext. However in the second part you override it's BindigContext and set it to a ItemComponentViewModel this ViewModel however does not have a property Demo so the {Binding Demo} does not work on this new BindingContext you've set.
Now a possible solution for your demo application would be to change MainPage.xaml to the following code:
<?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:SampleApp"
x:Class="SampleApp.MainPage"
x:DataType="local:MainViewModel"
x:Name="MyDemoPage">
<StackLayout>
<Label Text="Manual:" />
<Label Text="{Binding Demo.Title}" />
<Label Text="Component: " />
<local:ItemComponent Item="{Binding Path=BindingContext.Demo, Source={x:Reference MyDemoPage}}" />
</StackLayout>
</ContentPage>
Basically we now place the Demo binding outside of the BindingContext of our ItemComponent control. However if you want to use it in a ListView (if I remember correctly from your original question, this solution might not work and it's possible you'll have to drop the ItemComponentViewModel and bind directly to the properties (ListView will already make sure that the BindingContext of your ItemComponent is set to the current Item, no need to pass it around through a bindable property.
Hope this helps!

Hybris HMC: adding a button to ToolBarChip / Editor window next to the save button

I'm working with Hybris 5.6 and I'm trying to add a button in the editor area right next to the save/reload/delete buttons.
How can I add a button to the ToolBarChip like in the example below?
It's quite possible to add a new action (label) to the toolbar of HMC, however it's not so recommended as it may result some issues while the migration.
First, add the following snippet to your **/hmc.xml :
<type name="AbstractOrder" mode="append">
<organizer mode="append" >
<editor>
<tab name="payment_and_delivery" position="2" mode="append">
<section name="deliveryadministration" mode="append" >
<table>
<tr>
<td width="16px">
</td>
<td>
<!-- here is the interesting part -->
<action type="item"
classname="com.foo.bar.MyNewAction"
name="action.my_new_action"
toolbaricon="my_new_action"
icon="images/icons/my_new_action_icon.gif"
autosave="true"
showtoolbarlabel="true"
hidebutton="true"
/>
</td>
</tr>
</table>
</section>
</tab>
</editor>
</organizer>
</type>
Then, define the new action to be performed when the new label is clicked :
Add a new class called MyNewAction.java that extends from ItemAction and implement the method ActionResult perform(ActionEvent event) :
public MyNewAction extends ItemAction {
#Override
public ActionResult perform(ActionEvent actionEvent) throws JaloBusinessException {
//what the new action should do here ...
}
}
Note : you could override other interesting methods to be triggers while the action is possessing like : boolean needConfirmation() or String getConfirmationMessage() ...
The result would be like this :

Spring Webflow: Can Validators Be Manually Set Per Flow?

I have multiple flow configured in my application:
<flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" >
<flow:flow-location id="reservation1" path="/WEB-INF/flows/flow1.xml" />
<flow:flow-location id="reservation2" path="/WEB-INF/flows/flow2.xml" />
</flow:flow-registry>
These two flows use separate classes for their model attribute, call them Flow1DTO.java and Flow2DTO.java. However, they use a set of common JSPs/Tiles for their actual interface.
<form:form modelAttribute="reservationForm">
<!-- etc -->
</form:form>
Is it possible to define a separate Validator class per flow?
You can define a multiple validation methods for the same Model class for each specific view-state id. Where each custom validation method maps to a specific webflow view-state id.
Your validator class name for your model needs to be defined with the name "${model}Validator" (and have #Component annotation) and each validation method name needs to be named like this "validate${state} ([ModelClassType] model, ValidationContext context)"
So lets say you have a model class called "Reservation.java" and 2 different flows definition that use this model and each flow definition having a view-state definitions of
<!-- defined in your first flow file -->
<view-state id="ReservationSameDayViewState" view="sumting" model="reservationForm">
</view-state>
....
<!-- defined in your 2nd flow file -->
<view-state id="ReservationFutureViewState" view="sumting" model="reservationForm">
</view-state>
The validator class for Reservation model would like this:
#Component
public class ReservationValidator {
public void validateReservationSameDayViewState(Reservation reservation, ValidationContext context) {
// perform custom validation for first flow
}
public void validateReservationFutureViewState(Reservation reservation, ValidationContext context) {
// perform custom validation for 2nd flow
}
}
http://docs.spring.io/autorepo/docs/webflow/2.4.x/reference/html/views.html#view-validation-programmatic-validator
Also, it is bad practice to define your flows with integer increments. Change your flow registry definition to look like this. This way you don't have to keep manually adding flows to it every time you create new flows.
<flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF/flows">
<flow-location-pattern value="/**/*-flow.xml" />
</flow:flow-registry>
note: the flow names end with "-flow.xml". This is the unofficial standard way to define a flow-registry and flow names.
I figured out a solution that allowed me to validate multiple forms with a single implementation of a form validator.
The code looked something like this:
public void validateMethodName(Flow1DTO dto, ValidationContext context) {
valMethodName(dto, context);
}
public void validateMethodName(Flow2DTO dto, ValidationContext context) {
valMethodName(dto, context);
}
private void valMethodName(CommonFlowDTO dto, ValidationContext context) {
// do stuff
}
Putting validation methods in the DTO classes themselves was not an option. Validation required calls to the database, which would have coupled the DTO objects to the business logic and made the creation of the DTOs somewhat more complex.
I discovered that the validation methods could not specify an interface, leading to the duplicate methods for each concrete DTO class.

How do I find a Silvlerlight object on an Asp.Net page using Microsoft UI Automation testing?

I have an object on my asp.net page hosting a Silverlight xap (in my particular case it is in an IFrame, but I'm curious about regular objects as well). I can find the element in UI Spy, but the name just says "Silverlight Control". Trying to find that AutomationElement in my automated test is unsuccessful (control is null every time). Is there a setting in the Silverlight code or in the html that would help? How can I distinguish it if there are multiple Silverlight controls on the same page?
<object id="silverlightClient" style="display:none;" data="data:application/x-silverlight-2," type="application/x-silverlight-2">
<param name="source" value="../../ClientBin/SilverlightApplication.xap"/>
<param name="onerror" value="onSilverlightError" />
<param name="background" value="#00000000" />
<param name="minRuntimeVersion" value="4.0.41019.0" />
<param name="autoUpgrade" value="true" />
<param name="windowless" value="false" />
</object>
TreeWalker tw = new TreeWalker(new System.Windows.Automation.PropertyCondition(AutomationElement.NameProperty, "Silverlight Control));
AutomationElement control = tw.GetFirstChild(ancestor);
UI Spy
Identification
ClassName: "MicrosoftSilverlight"
ControlType: "ControlType.Window"
Culture: "(null)"
AutomationId: "71857844"
LocalizedControlType: "window"
Name: "Silverlight Control"
ProcessId: "7636 (iexplore)"
RuntimeId: "42 2163886"
IsPassword: "False"
IsControlElement: "True"
IsContentElement: "True"
EDIT: added image, I also realized that the object is inside of an IFrame.
UISpyImage - title name removed
I've created some extension methods to make working with AutomationElement somewhat easier. I've pasted the relevant ones below, but you can read more about them here.
I'm assuming you've got a reference to the root IE window. If not, but you know it's Process Id you can find it like so:
var ieWindow = AutomationElement.RootElement.FindChildByCondition(new PropertyCondition(AutomationElement.ProcessIdProperty, ieProcessId));
Assuming there's only one Frame open in IE, and a single Silverlight control on it, you can then do:
var silverlightControl = ieWindow.FindDescendentByClassPath(
new[]{
"Frame Tab",
"TabWindowClass",
"Shell DocObject View",
"Internet Explorer_Server",
"MicrosoftSilverlight",
});
If you have more than one Silverlight control, I don't know of a way to distinguish them through UIAutomation. I would try dropping the "MicrosoftSilverlight" entry from the Class path above, so that you get a reference to the Explorer page. Then use
AutomationElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "MicrosoftSilverlight"))
to find all the SilverlightControls, then probe them each in turn locate some element within them that allows you to distinguish between them.
Here are the extension methods:
public static class AutomationExtensions
{
public static AutomationElement FindDescendentByClassPath(this AutomationElement element, IEnumerable<string> classNames)
{
var conditionPath = CreateClassNameConditionPath(classNames);
return element.FindDescendentByConditionPath(conditionPath);
}
public static AutomationElement FindDescendentByConditionPath(this AutomationElement element, IEnumerable<Condition> conditionPath)
{
if (!conditionPath.Any())
{
return element;
}
var result = conditionPath.Aggregate(
element,
(parentElement, nextCondition) => parentElement == null
? null
: parentElement.FindChildByCondition(nextCondition));
return result;
}
public static AutomationElement FindChildByCondition(this AutomationElement element, Condition condition)
{
var result = element.FindFirst(
TreeScope.Children,
condition);
return result;
}
public static IEnumerable<Condition> CreateClassNameConditionPath(IEnumerable<string> classNames)
{
return classNames.Select(name => new PropertyCondition(AutomationElement.ClassNameProperty, name, PropertyConditionFlags.IgnoreCase)).ToArray();
}
}

Resources