Grid inline editor - unable to get the edited column values in addCommitHandler method of grid - vaadin

When I'm trying to click save after the edited the column cell with new user name, I'm not able to get the new cell value, instead I'm getting the old value.
grid.getEditorFieldGroup().addCommitHandler(new FieldGroup.CommitHandler()
{
#Override
public void preCommit(FieldGroup.CommitEvent commitEvent) throws
FieldGroup.CommitException {
BeanItem item = (BeanItem)
commitEvent.getFieldBinder().getItemDataSource();
User user= (User) item.getBean();
user.getName();//getting the old value instead the new column value
}
}
I have used the BeanItemContainer like below:
BeanItemContainer<User> container = new BeanItemContainer<User>(User.class);
grid.setContainerDataSource(container);

preCommit(FieldGroup.CommitEvent commitEvent) is called before the commit. So the BeanItem's bean (User) contains the old values (the new values were not yet committed into the bean). Therefore getName() returns the old value.
If you want to access the new value you have to use postCommit(FieldGroup.CommitEvent commitEvent) which is called after the commit.
grid.getEditorFieldGroup().addCommitHandler(new FieldGroup.CommitHandler() {
#Override
public void preCommit(FieldGroup.CommitEvent commitEvent) {
BeanItem item = (BeanItem)
commitEvent.getFieldBinder().getItemDataSource();
User user = (User) item.getBean();
String name = user.getName(); // old value
System.out.println(name);
}
#Override
public void postCommit(FieldGroup.CommitEvent commitEvent) {
BeanItem item = (BeanItem)
commitEvent.getFieldBinder().getItemDataSource();
User user = (User) item.getBean();
String name = user.getName(); // new value
System.out.println(name);
}
});

Related

Complex scenario with ASP .NET MVC 3 without using Session

I have a model class called House:
public class House
{
public House()
{
Residents = new List<Resident>();
}
public virtual string Name { get; set; }
...
public virtual IList<Resident> Residents{ get; set; }
}
public class Resident
{
public virtual string Name { get; set; }
public virtual int Age{ get; set; }
public virtual House House { get; set; }
}
So, in my view Create (House), I need to add Residents . So I added a button "Add Resident", that opens a JQuery UI Modal with Create(Resident) and when the user click Confirm, the Modal closes and my Resident´s grid refresh...
My problem here is where I save that list ... I did that using Session... But I´d like to do that without Session...
What I´ve done (My House Controller):
[HttpGet]
public ActionResult AddResident(Resident resident) //Called when user confirms modal Resident
{
Residents.Add(resident);
return PartialView("_Residents", Residents);
}
public Collection<Resident> Residents
{
get
{
if (Session["Residents"] == null)
{
var _lista = new Collection<Resident>();
Session["Residents"] = _lista;
return _lista;
}
return (Collection<Resident>)Session["Residents"];
}
set { Session["Residents"] = value; }
}
So, what´s the right way to do that kind of scenario without session?
Thanks
Although it stil uses the Session behind the scene, TempData is the way to go when you want to persist data across actions calls.
An action method can store data in the controller's TempDataDictionary object before it calls the controller's RedirectToAction method to invoke the next action. The TempData property value is stored in session state.
The good part is that the session will be automatically cleared after your read (in your grid refresh method). It's really what it means "temporary data".
Here's a sample of code with TempData:
public ActionResult AddResident(Resident resident)
{
IList<Resident> residents = PeristResident(resident);
return PartialView("_Residents", residents);
}
private IList<Resident> PeristResident(Resident resident)
{
IList<Resident> residents = Residents; // this operation can empty TempData by reading it
residents.Add(resident);
Residents = residents; // so we persists collection after read
return residents;
}
private IList<Resident> Residents
{
get
{
object results;
if (TempData.TryGetValue("Residents", out results) == false)
{
var list = new List<Resident>();
return list;
}
return (IList<Resident>)results;
}
set { TempData["Residents"] = value; }
}
You're not too far off the mark with using session. As MVC doesn't have the rather bloated (and often ugly) viewstate, you need to store your residents collection somewhere. You basically have 3 options:
1. Use an editable list approach.
This is where you post back the whole list each time to ensure your model maintains its state. (look at this example: http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/).
Problems: You have to deal with the list wanting to validate every time you post it back which can be a problem if you have validation rules on your House form.
2. Create a client side javascript solution.
This will add new items to your list and in doing so, will store the values for those items in hidden inputs ensuring the full model is submitted in the end. This can be tricky as you then need to manage the index of each item in markup that javascript creates. For example changing this:
name="House.Residents[0].Name" id="House.Residents_0__Name"
To the index you need when you add an item. This is the approach i generally take, but it can be complicated. The code to take a returned empty editor template from MVC and reset its index is as follows:
function (response, index, oldindex) {
// Reset editor template index reference
response.attr("id", response.attr("id").replace("[" + oldindex + "]", "[" + index + "]"));
var inputList = response.find("input, select");
inputList.each(function (num) {
var thisEl = $(this);
// Allow for dbl input with checkbox
if (thisEl.attr("id"))
thisEl.attr("id", thisEl.attr("id").replace("_" + oldindex + "__", "_" + index + "__"));
thisEl.attr("name", thisEl.attr("name").replace("[" + oldindex + "]", "[" + index + "]"));
});
var spanList = response.find("span");
spanList.each(function (num) {
var thisEl = $(this);
if (thisEl.attr("data-valmsg-for")) {
thisEl.attr("data-valmsg-for", thisEl.attr("data-valmsg-for").replace("[" + oldindex + "]", "[" + index + "]"));
}
});
var labelList = response.find("label");
labelList.each(function (num) {
var thisEl = $(this);
if (thisEl.attr("for")) {
thisEl.attr("for", thisEl.attr("for").replace("_" + oldindex + "__", "_" + index + "__"));
}
});
return response;
}
3. Use session (or tempdata).
to temporarily store your list is by far the easiest, but probably the least graceful.
Two options:
Cookies
Hidden fields.
But I think session is a good approach for this scenario.

DynamoDBMapper ConditionalCheckFailedException when range-key attribute for GSI is not present in update request

I'm trying to reason about the cause of a ConditionalCheckFailedException I receive when using DynamoDBMapper with a specific save expression and with UPDATE_SKIP_NULL_ATTRIBUTES SaveBehavior.
My schema is as follows:
Member.java
#Data
#DynamoDBTable(tableName = "members")
public class Member implements DDBTable {
private static final String GROUP_GSI_NAME = "group-gsi";
#DynamoDBHashKey
#DynamoDBAutoGeneratedKey
private String memberId;
#DynamoDBVersionAttribute
private Long version;
#DynamoDBIndexHashKey(globalSecondaryIndexName = GROUP_GSI_NAME)
private String groupId;
#DynamoDBAutoGeneratedTimestamp(strategy = DynamoDBAutoGenerateStrategy.CREATE)
#DynamoDBIndexRangeKey(globalSecondaryIndexName = GROUP_GSI_NAME)
private Date joinDate;
#DynamoDBAttribute
private String memberName;
#Override
#DynamoDBIgnore
public String getHashKeyColumnName() {
return "memberId";
}
#Override
#DynamoDBIgnore
public String getHashKeyColumnValue() {
return memberId;
}
}
I use the following class to create/update/get the records in the members table.
DDBModelDAO.java
public class DDBModelDAO<T extends DDBTable> {
private final Class<T> ddbTableClass;
private final AmazonDynamoDB amazonDynamoDB;
private final DynamoDBMapper dynamoDBMapper;
public DDBModelDAO(Class<T> ddbTableClass, AmazonDynamoDB amazonDynamoDB, DynamoDBMapper dynamoDBMapper) {
this.ddbTableClass = ddbTableClass;
this.amazonDynamoDB = amazonDynamoDB;
this.dynamoDBMapper = dynamoDBMapper;
}
public T loadEntry(final String hashKey) {
return dynamoDBMapper.load(ddbTableClass, hashKey);
}
public T createEntry(final T item) {
dynamoDBMapper.save(item, getSaveExpressionForCreate(item));
return item;
}
public T updateEntry(final T item) {
dynamoDBMapper.save(item, getSaveExpressionForUpdate(item),
DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES.config());
return item;
}
private DynamoDBSaveExpression getSaveExpressionForCreate(final T item) {
// No record with the same hash key must be present when creating
return new DynamoDBSaveExpression().withExpectedEntry(item.getHashKeyColumnName(),
new ExpectedAttributeValue(false));
}
private DynamoDBSaveExpression getSaveExpressionForUpdate(final T item) {
// The hash key for the record being updated must be present.
return new DynamoDBSaveExpression().withExpectedEntry(item.getHashKeyColumnName(),
new ExpectedAttributeValue(new AttributeValue(item.getHashKeyColumnValue()))
.withComparisonOperator(ComparisonOperator.EQ)
);
}
}
I wrote a test class to insert and update records into the members table, which is as follows:
public static void main(String[] args) {
DDBTestClient testClient = new DDBTestClient();
AmazonDynamoDB amazonDynamoDB = testClient.buildAmazonDynamoDB();
DynamoDBMapper dynamoDBMapper = testClient.buildDynamoDBMapper(amazonDynamoDB);
DDBModelDAO<Member> memberDAO = new DDBModelDAO<>(Member.class, amazonDynamoDB, dynamoDBMapper);
DDBModelDAO<Group> groupDAO = new DDBModelDAO<>(Group.class, amazonDynamoDB, dynamoDBMapper);
try {
// Create a group
Group groupToCreate = new Group();
groupToCreate.setGroupName("group-0");
Group createdGroup = groupDAO.createEntry(groupToCreate);
System.out.println("Created group: " + createdGroup);
Thread.sleep(3000);
// Create a member for the group
Member memberToCreate = new Member();
memberToCreate.setGroupId(createdGroup.getGroupId());
memberToCreate.setMemberName("member-0");
Member createdMember = memberDAO.createEntry(memberToCreate);
System.out.println("Created member: " + createdMember);
Thread.sleep(3000);
// Update member name
createdMember.setMemberName("member-updated-0");
createdMember.setGroupId(null);
//createdMember.setJoinDate(null); // <---- Causes ConditionalCheckFailedException
memberDAO.updateEntry(createdMember);
System.out.println("Updated member");
} catch (Exception exception) {
System.out.println(exception.getMessage());
}
}
As can be seen above, if I do not pass a valid value for joinDate(which happens to be the range-key for the groups GSI), in the updateEntry call, DynamoDB returns a ConditionalCheckFailedException. This is the case, even when I use a save behavior of UPDATE_SKIP_NULL_ATTRIBUTES, as can be seen in DDBModelDAO.java.
Can someone help me understand, why I'm required to send the range-key attribute for the GSI, for a conditional write to succeed?
Not sure if this answers your question:
Interface DynamoDBAutoGenerator:
"DynamoDBAutoGenerateStrategy.CREATE, instructs to generate when
creating the item. The mapper, determines an item is new, or
overwriting, if it's current value is null. There is a limitation when
performing partial updates using either,
DynamoDBMapperConfig.SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES, or DynamoDBMapperConfig.SaveBehavior.APPEND_SET. A new value will only be generated if the mapper is also generating the key."
So the last part is important: "A new value will only be generated if the mapper is also generating the key"
That should explain why you only see the behavior that you are experiencing.
Does this make sense?

Apache Wicket Panel's TextFields dont refresh after closing the Modal Window

I have a use case where a modal dialog has three set of Addresses of checkbox (Address A, Address B, Other Address) divided in separate divs for each address type for the user to select one address.
Each address type has their own set of TextFields for showing Name, House no, Street, City, Postal code and country.
So far so good. I was able to handle the selection of Address and pass the textfield values to the model. The model is getting reflected with the selected address.
On click of the "OK" button of Modal Window, (AjaxLink), there is a sub-panel (under another main panel, having all the address readonly textfields in it) which has to display the selected address type. But it isnt getting refresh. But the textfields model is having the updated values. (I have found this during debug)
Have tried everything on this site:
- using LoadableDetachableModel, setDefaultModel(), creating new instance of the Panel etc. but nothing is of use
i have also pasted the relevant code parts here. Request anyone who can guide me here as to what is going wrong and where I need to correct to get the sub-panel text fields refreshed on click of "OK' button of the modal.
thanks in advance
--ShipmentAddressSelectedPanel constructor Code
public ShipmentAddressSelectedPanel(String id, IModel<OrderDTO> orderDTOIModel) {
super(id, orderDTOIModel);
name = orderDTOIModel.getObject().getDeliveryAddress().getName();
streetAddress = subOrderDTOIModel.getObject().getDeliveryAddress().getStreet();
floor = orderDTOIModel.getObject().getDeliveryAddress().getFloor();
postalNumber = orderDTOIModel.getObject().getDeliveryAddress().getZipCode();
city = orderDTOIModel.getObject().getDeliveryAddress().getCity();
add(new TextField<>("name", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + name;
}
}));
add(new TextField<>("streetAddress", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + streetAddress;
}
}));
add(new TextField<>("floor", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + floor;
}
}));
//floor
add(new TextField<>("postalNumber", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + postalNumber;
}
}));
//postalNumber
add(new TextField<>("city", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return "" + city;
}
}));
}
--code of onClick of AjaxLink ("Ok") button
add(new AjaxLink("saveAddress") {
#Override
public void onClick(final AjaxRequestTarget target) {
if (addressA.getValue().equals("true"))
{
onSaveAddress(target, getShipmentAddressModelFromDeliveryAddress(addressAModel.getObject()));
}
--Code of onSaveAddress where the logic refresh Panel is called from the main panel
#Override
public void onSaveAddress(AjaxRequestTarget target, ShipmentAddressModel shipmentAddressModel) {
ShipmentAddressSelectedPanel newShipmentAddressSelectedPanel = new ShipmentAddressSelectedPanel("showShipmentAddressSelected", orderDTOIModel);
newShipmentAddressSelectedPanel.setOutputMarkupId(true);
//newShipmentAddressSelectedPanel.setDefaultModelObject(orderDTOIModel);
target.add(newShipmentAddressSelectedPanel);
showAddressModal.close(target);
}
You're creating a new ShipmentAddressSelectedPanel, but it doesn't get added to the component tree.
Note that AjaxRequestTarget skips components, which are not inside the current page. You should have a log entry:
"Component .. with markupid: .. not rendered because it was already removed from page"
Note your usage of LDM is wrong, it should be:
//name = orderDTOIModel.getObject().getDeliveryAddress().getName();
add(new TextField<>("name", new LoadableDetachableModel<String>() {
#Override
protected String load() {
return orderDTOIModel.getObject().getDeliveryAddress().getName();
}
}));
Otherwise you'll pull out the name once only and never update when the model has changed.

Vaadin can't get combo box field value at 0 position

I'm trying to set field at index 0 in Vaadin combo box to default value, so I could avoid error message if user doesen't select anything. So I would like that instead of blank field I have populated field at index 0.
I have tried to set it and managed it with this:
field.setNullSelectionAllowed(true);
field.setNullSelectionItemId(container.getIdByIndex(0));
So I don't have blank value at index 0, instead my previous value of index 1 is now at index 0. And that is exactly what I want and need and in combo box looks just as I want.
But, unfortunately, when I submit my form, value is not passed. Only values after index 0 are passed. It's so frustrating, can somebody help me? Value passed to setNullSelectionItemId exists 100%.
How can I grab value from index at place 0 in combo box?
p.s. here is my code:
public Field<?> buildAndBindComboBox(final String caption, final BeanItemContainer<?> container,
final Object propertyId, final String title, final ValueChangeListener listener, final boolean nullAllowed,
final boolean required, final boolean enabled, final boolean visible) {
#SuppressWarnings("serial")
ComboBox field = new ComboBox(caption, container) {
// http://dev.vaadin.com/ticket/10544
// - typing in ComboBox causes Internal Error
private boolean inFilterMode;
#Override
public void containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent event) {
if (inFilterMode) {
super.containerItemSetChange(event);
}
}
#Override
protected List<?> getOptionsWithFilter(boolean needNullSelectOption) {
try {
inFilterMode = true;
return super.getOptionsWithFilter(needNullSelectOption);
} finally {
inFilterMode = false;
}
}
};
field.setStyleName("comboBox");
field.setInputPrompt("Select");
if(defaultValue == true){
field.setNullSelectionAllowed(false);
field.setNullSelectionItemId(container.getIdByIndex(0).toString());
//field.select(container.getIdByIndex(0));
//field.setValue(container.getIdByIndex(0));
//field.setRequired(false);
defaultValue = false;
} else {
field.setNullSelectionAllowed(nullAllowed);
field.setRequired(required);
}
field.setImmediate(true);
field.setNewItemsAllowed(false);
field.setFilteringMode(FilteringMode.CONTAINS);
if (title != null) {
field.setItemCaptionPropertyId(title);
}
//field.setNullSelectionAllowed(nullAllowed);
//field.setRequired(required);
field.setVisible(visible);
if (listener != null) {
field.addValueChangeListener(listener);
}
this.bind(field, propertyId);
field.setEnabled(enabled);
return field;
}
public void setDefaultValueFirstItem(boolean def){
defaultValue = def;
}
It is binded like this:
commitmentFeeBinder.setDefaultValueFirstItem(true);
commitmentFeeBinder.buildAndBindComboBox("No working day labels", noWorkingDays, "noWorkingDaysCF", "title", null, false, !transaCF, true, !transaCF);
If I understood your question correctly, Steffen Harbich is correct in suggesting that if you want the first item to be selected by default you should disable null selection and select the first item by default. E.g. this works:
ComboBox cb = new ComboBox("", Arrays.asList("First", "Second", "Third"));
cb.setNullSelectionAllowed(false);
cb.select("First");
Or alternatively with a BeanItemContainer:
List<MyBean> beans = Arrays.asList(new MyBean("First"), new MyBean("Second"), new MyBean("Third"));
BeanItemContainer<MyBean> bic = new BeanItemContainer<>(MyBean.class, beans);
ComboBox cb = new ComboBox("", bic);
cb.setNullSelectionAllowed(false);
cb.select(bic.getIdByIndex(0));
private void resetComboBoxToIndex(ComboBox combo, int index) {
BeanItemContainer<Bean_ComboBox> items_combo = (BeanItemContainer<Bean_ComboBox>)combo.getContainerDataSource();
if(items_combo != null && items_combo.size() > index) {
Bean_ComboBox primerItem = items_combo.getIdByIndex(index);
if(primerItem != null) {
combo.select(primerItem);
}
}
}

Loop with ChildWindows in Silverlight?

I have a control with textbox. After user input few lines of text and submit that by button then, for every line of text I need to show a childwindow with grid where user has to select some value.
Let's say that user inputs 5 lines of text with names of clients (one line one client name).
For every of them aften click Submit, he must select Salesperson from ChildWindow.
Of course now effect of my loop is opening 5 ChildWindows with the in the same time.
How can I do user get next ChildWindow only after choosing an element from Childwindow grid ?
Maybe your control could use a class that looks something like this.
public class SalesPersonSelector
{
private Queue<string> _clientNamesToProcess;
private Dictionary<string, SalesPerson> _selectedSalesPersons;
private Action<IDictionary<string, SalesPerson>> _onComplete;
private string _currentClientName;
public void ProcessNames(IEnumerable<string> clientNames, Action<IDictionary<string, SalesPerson>> onComplete)
{
this._clientNamesToProcess = new Queue<string>(clientNames);
this._selectedSalesPersons = new Dictionary<string, SalesPerson>();
this._onComplete = onComplete;
this.SelectSalespersonForNextClient();
}
private void SelectSalespersonForNextClient()
{
if (this._clientNamesToProcess.Any())
{
this._currentClientName = this._clientNamesToProcess.Dequeue();
ChildWindow childWindow = this.CreateChildWindow(this._currentClientName);
childWindow.Closed += new EventHandler(childWindow_Closed);
childWindow.Show();
}
else
{
this._onComplete(this._selectedSalesPersons);
}
}
private ChildWindow CreateChildWindow(string nextClientName)
{
// TODO: Create child window and give it access to the client name somehow.
throw new NotImplementedException();
}
private void childWindow_Closed(object sender, EventArgs e)
{
var salesPerson = this.GetSelectedSalesPersonFrom(sender as ChildWindow);
this._selectedSalesPersons.Add(this._currentClientName, salesPerson);
this.SelectSalespersonForNextClient();
}
private SalesPerson GetSelectedSalesPersonFrom(ChildWindow childWindow)
{
// TODO: Get the selected salesperson somehow.
throw new NotImplementedException();
}
}
Assuming your control has already split up the names from the TextBox into a list called "names", then you can do this:
var salesPersonSelector = new SalesPersonSelector();
salesPersonSelector.ProcessNames(names, selections =>
{
foreach (var selection in selections)
{
var clientName = selection.Key;
var salesPerson = selection.Value;
// TODO: Do something with this information.
}
});
I haven't tested this, but Visual Studio isn't giving me any red squiggly lines.

Resources