Сould you please give me working example of nested editors ? I've read this document but it didn't help me. In my code I have class Person and Organization.
Organization has field contactPerson of a type Person.
So I created following editor for Person:
public class PersonEditor extends Composite implements Editor<PersonProxy>
{
interface PersonEditorUiBinder extends UiBinder<Widget, PersonEditor>
{
}
private static PersonEditorUiBinder uiBinder = GWT.create(PersonEditorUiBinder.class);
#UiField
ValueBoxEditorDecorator<String> name;
#UiField
ValueBoxEditorDecorator<String> phoneNumber;
#UiField
ValueBoxEditorDecorator<String> email;
#UiField
CaptionPanel captionPanel;
public void setCaptionText(String captionText)
{
captionPanel.setCaptionText(captionText);
}
public PersonEditor()
{
initWidget(uiBinder.createAndBindUi(this));
}
}
its corresponding .ui.xml is
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:e='urn:import:com.google.gwt.editor.ui.client'
ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
ui:generateKeys="com.google.gwt.i18n.server.keygen.MD5KeyGenerator"
ui:generateLocales="en,ru">
<ui:style src="../common.css"/>
<g:CaptionPanel captionText="Test" ui:field="captionPanel">
<g:HTMLPanel>
<table class="{style.forform}">
<tr>
<th class="{style.forform}">
<div>
<ui:msg meaning="person's name">Name:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="name" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>Phone Number:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="phoneNumber" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox width="100%" stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>EMail:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="email" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox width="100%" stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
</table>
</g:HTMLPanel>
</g:CaptionPanel>
</ui:UiBinder>
It works nicely.
Here is editor for Organization:
public class OrganizationEditor extends Composite implements Editor<OrganizationProxy>
{
interface OrganizationEditorUiBinder extends UiBinder<Widget, OrganizationEditor>
{
}
private static OrganizationEditorUiBinder uiBinder = GWT.create(OrganizationEditorUiBinder.class);
#UiField
CaptionPanel captionPanel;
#UiField
ValueBoxEditorDecorator<String> name;
#UiField
ValueBoxEditorDecorator<String> address;
#UiField
PersonEditor personEditor;
public void setCaptionText(String captionText)
{
captionPanel.setCaptionText(captionText);
}
public OrganizationEditor()
{
initWidget(uiBinder.createAndBindUi(this));
}
}
and its corresponding .ui.xml is
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:e='urn:import:com.google.gwt.editor.ui.client'
xmlns:c='urn:import:com.zasutki.courierApp.client.customer'
xmlns:myui='urn:import:com.zasutki.courierApp.client.ui'
ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
ui:generateKeys="com.google.gwt.i18n.server.keygen.MD5KeyGenerator"
ui:generateLocales="en,ru">
<ui:style src="../common.css"/>
<g:CaptionPanel ui:field="captionPanel">
<myui:VerticalFlowPanel>
<g:HTMLPanel>
<table class="{style.forform}">
<tr>
<th class="{style.forform}">
<div>
<ui:msg meaning="Name of organization">Name:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="name" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
<tr>
<th class="{style.forform}">
<div>
<ui:msg>Address:</ui:msg>
</div>
</th>
<td class="{style.forform}">
<e:ValueBoxEditorDecorator ui:field="address" stylePrimaryName="{style.forform}">
<e:valuebox>
<g:TextBox stylePrimaryName="{style.forform}"/>
</e:valuebox>
</e:ValueBoxEditorDecorator>
</td>
</tr>
</table>
<c:PersonEditor ui:field="personEditor" captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</g:HTMLPanel>
</myui:VerticalFlowPanel>
</g:CaptionPanel>
</ui:UiBinder>
interface for proxy of Organization is
#ProxyFor(value = Organization.class, locator = ObjectifyLocator.class)
public interface OrganizationProxy extends EntityProxy
{
public String getName();
public void setName(String name);
public String getAddress();
public void setAddress(String address);
public PersonProxy getContactPerson();
public void setContactPerson(PersonProxy contactPerson);
}
and finally here is class that uses all described above
public class NewOrderView extends Composite
{
interface Binder extends UiBinder<Widget, NewOrderView>
{
}
private static Binder uiBinder = GWT.create(Binder.class);
// Empty interface declaration, similar to UiBinder
interface OrganizationDriver extends SimpleBeanEditorDriver<OrganizationProxy, OrganizationEditor>
{
}
OrganizationDriver driver = GWT.create(OrganizationDriver.class);
#UiField
Button save;
#UiField
OrganizationEditor orgEditor;
OrganizationProxy organizationProxy;
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
organizationProxy = createFactory().contextOrder().create(OrganizationProxy.class);
// Initialize the driver with the top-level editor
driver.initialize(orgEditor);
// Copy the data in the object into the UI
driver.edit(organizationProxy);
}
#UiHandler("save")
void buttonClick(ClickEvent e)
{
e.stopPropagation();
OrganizationProxy edited = driver.flush();
PersonProxy person = edited.getContactPerson();
// person is always null !!!
if (driver.hasErrors())
{
}
}
}
The question is why nested editor (PersonEditor) doesn't get flushed automatically ? Is it supposed to happen ? What is the proper solution ?
<e:ValueBoxEditorDecorator ui:field="contactPerson">
<e:valuebox>
<c:PersonEditor captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</e:valuebox>
</e:ValueBoxEditorDecorator>
This code throws the exception. After <e:valuebox> a sub-type of a ValueBox is expected (e.g. TextBox, DoubleBox,...). Your PersonEditor is not a ValueBox (and it makes no sense to make it one). So just add your PersonEditor in the OrganizationEditor's ui.xml like a normal Widget.
For example:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
....>
<ui:style src="../common.css"/>
<g:CaptionPanel ui:field="captionPanel">
<myui:VerticalFlowPanel>
<g:HTMLPanel>
<table class="{style.forform}">
// your other input fields
</table>
</g:HTMLPanel>
// Add the PersonEditor to the FlowPanel
<c:PersonEditor captionText="Contact person">
<ui:attribute name="captionText"/>
</c:PersonEditor>
</myui:VerticalFlowPanel>
</g:CaptionPanel>
</ui:UiBinder>
In the Java class, change
#UiField
ValueBoxEditorDecorator<PersonEditor> contactPerson;
to
#UiField
PersonEditor contactPerson;
Ah... I have to create proxy for contactPerson manually!
public class NewOrderView extends Composite
{
public NewOrderView()
{
initWidget(uiBinder.createAndBindUi(this));
AdminRequestFactory.OrderRequestContext orderRequestContext = createFactory().contextOrder();
organizationProxy = orderRequestContext.create(OrganizationProxy.class);
organizationProxy.setContactPerson(orderRequestContext.create(PersonProxy.class));
// Initialize the driver with the top-level editor
driver.initialize(orgEditor);
// Copy the data in the object into the UI
driver.edit(organizationProxy);
}
}
Related
I 'm a beginer with spring framework. I insert an image to postgresql database and i want to display it.
the model class is:
#Data
#Entity
#Table(name="Item")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
private byte[] imageTo_display;
private Long price;
#ManyToOne
private Cart cart;
}
I think the problem is at image tag but i don't know how to solve it .
and the html is:
<th:block th:each="item:${listItems}">
<tr>
<td>[[${item.id}]]</td>
<td>[[${item.name}]]</td>
<td>[[${item.price}]]</td>
<td> <img class="s-ava-alone-img" width="150" height="150" th:src="#{${item.imageTo_display}}"></td>
</tr>
<td>
<a class="h4 mr-3" th:href="#{'/users/edit/'+${item.id}}">Edit</a>
<a class="h4 mr-3" th:href="#{'/users/delete/' +${item.id}}">Delete</a>
</td>
</th:block>
The way you should do this is:
Create a controller that just returns the byte[] array of the image:
#GetMapping(value = "/image/{id}")
public void view(HttpServletResponse response, #PathVariable(name="id") int id) throws Exception {
response.setHeader("content-type", "image/jpg");
Item item = ...; // Get the item based on id;
OutputStream o = response.getOutputStream();
o.write(item.imageTo_display);
o.close();
}
Link to your image on the page:
<th:block th:each="item: ${listItems}">
<tr>
<td th:text="${item.id}" />
<td th:text="${item.name}" />
<td th:text="${item.price}" />
<td><img class="s-ava-alone-img" width="150" height="150" th:src="#{/image/{id}(id=${item.id})}"></td>
</tr>
<td>
<a class="h4 mr-3" th:href="#{/users/edit/{id}(id=${item.id})}">Edit</a>
<a class="h4 mr-3" th:href="#{/users/delete/{id}(id=${item.id})}">Delete</a>
</td>
</th:block>
In my scenario, i'm developing nested select on a JSP, binded to a bean:
Bean
public class WizardPanelp1Bean {
private Integer id;
private Stringofpanels stringofpanels;
private Paneltype paneltype;
private Integer number;
private String paneltypestring;
//and getters/setters... [Omitted]
Now i have the Paneltype object, another simple bean
Paneltype
private Integer id;
private double peakpower;
private double weight;
private String name;
private double dimension;
private double conversion;
private Set functions = new HashSet(0);
private Set sensors = new HashSet(0);
private Set panels = new HashSet(0);
//[Getters/setters omitted as usual]
So, i prepare the view, with a bean named wb
a simple arraylist of panels
public class PanelsBean {
private ArrayList<WizardPanelp1Bean> panels =new ArrayList<WizardPanelp1Bean>();
and finally i go to the jsp (please note this is in a )
<tbody>
<c:forEach items="${wb.panels}" varStatus="loop" var="item">
<tr>
<td>${item.id}</td>
<td>
<form:select path="panels[${loop.index}].paneltype" >
<c:forEach var="type" items="${types}">
<c:choose>
<c:when test="${item.paneltype.id==type.id}">
<form:option selected="selected" value="${type.id}" label="${type.name}" />
</c:when>
<c:otherwise>
<form:option value="${type.id}" label="${type.name}" />
</c:otherwise>
</c:choose>
</c:forEach>
</form:select>
</td>
<td><form:input style="width:180px" path="panels[${loop.index}].number" /></td>
<td>
<div>
<form:input style="visibility: hidden ; width:0px" path="panels[${loop.index}].id" disabled="disabled" />
<a href="javascript:remove(${item.id},${stringofpanels.id})" class="wb.panels.remove" >Rimuovi</a>
</div>
</td>
</tr>
</c:forEach>
</tbody>
every time i get a null reference to paneltype. I obviously used a #InitBinder on the controller:
Initbinder
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.registerCustomEditor(Paneltype.class, "paneltype", new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
int i=0;
PaneltypeDAO pDAO=new PaneltypeDAO();
setValue(pDAO.findById(Integer.parseInt(text)));
}
});
}
but the code never reach this. It's like the jsp is sure that the value is null.
Suggestions? Thanks
I think your problem persists when you try to submit this form and not getting the binded values in the model.
Try to initialize your list using LazyList. Please replace your panels declaration in PanelsBean class as below.
private List<WizardPanelp1Bean> panels = LazyList.decorate(new ArrayList<WizardPanelp1Bean>(),FactoryUtils.instantiateFactory(WizardPanelp1Bean.class));
Hope this helps you. Cheers.
I have a very simple implementation of the DefaultModelBinder, I need it to fire some custom validation.
public class MyViewModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ModelStateDictionary modelState = bindingContext.ModelState;
var model = (MyViewModel)base.BindModel(controllerContext, bindingContext);
var result = ValidationFactory.ForObject<MyViewModel>().Validate(model);
CustomValidation(result, modelState);
return model;
}
}
MyViewModel is a public sealed class.
The model binder is registered in the Global.asax this way:
ModelBinders.Binders.Add(typeof(MyViewModel), new MyViewModelBinder());
The problem is that the model is never populated! But the MVC default model binder (I remove the registration in global.asax) works fine.
This is the view HTML:
<table>
<tr>
<td><label for="Name">Name</label></td>
<td><input id="Name" name="Name" type="text" value="" /></td>
</tr>
<tr>
<td><label for="Code">Code</label></td>
<td><input id="Code" name="Code" type="text" value="" /></td>
</tr>
</table> </div>
Every field matches a property of the model.
From the information you provided I am unable to reproduce the problem. Here's what I did.
View model:
public sealed class MyViewModel
{
public string Name { get; set; }
public string Code { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// at this stage the model is populated perfectly fine
return View();
}
}
Index View:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<% using (Html.BeginForm()) { %>
<table>
<tr>
<td><label for="Name">Name</label></td>
<td><input id="Name" name="Name" type="text" value="" /></td>
</tr>
<tr>
<td><label for="Code">Code</label></td>
<td><input id="Code" name="Code" type="text" value="" /></td>
</tr>
</table>
<input type="submit" value="OK" />
<% } %>
</body>
</html>
Model binder:
public class MyViewModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = (MyViewModel)base.BindModel(controllerContext, bindingContext);
// at this stage the model is populated perfectly fine
return model;
}
}
So now the question is, how does your code differs than mine and what is it in those CustomValidation and Validate methods?
I have a requirement where, when a button is clicked, the user should be prompted with a popup/dialog box to enter some additional details such as last name, DOB, etc.I tried to play with window.confirm() but I think this does not serve my purpose. Can some help me how this can be achieved in GWT through UIBinder?
I tried some thing like this in my UI binder.xml
<g:HTMLPanel visible="false" >
<g:DialogBox ui:field="dialogPanel"
animationEnabled="true" modal="false" glassEnabled="false">
<g:caption>More Details</g:caption>
<table>
<tr>
<td colspan="2" align="center">
<g:Datepicker ui:field="DOB">DOB:</g:Datepicker>
</td>
</tr>
<tr>
<td>UserName:</td>
<td>
<g:TextBox ui:field="usernameTextBox" />
</td>
</tr>
<tr>
<td></td>
<td align="right">
<g:Button ui:field="loginButton">OK</g:Button>
</td>
</tr>
</table>
</g:DialogBox>
</g:HTMLPanel>
I am not sure which one to go with: popup or dialog box!
Thanks.
Here is the skeleton for a GWT Dialog box using uibinder:
MyDialogBox.java
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Widget;
public class MyDialogBox extends DialogBox {
private static final Binder binder = GWT.create(Binder.class);
interface Binder extends UiBinder<Widget, MyDialogBox> {
}
public MyDialogBox() {
setWidget(binder.createAndBindUi(this));
setAutoHideEnabled(true);
setText("My Title");
setGlassEnabled(true);
center();
}
}
MyDialog.ui.xml
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:style>
.panel {
background-color: ivory;
}
</ui:style>
<g:FlowPanel styleName="{style.panel}">
<g:Label>Dialog Content</g:Label>
</g:FlowPanel>
</ui:UiBinder>
show it using:
MyDialogBox m = new MyDialogBox();
m.show();
DialogBox is a child of PopupPanel and has all its features. Additionally it has (from the docs):
..caption area at the top and can be dragged by the user.
About usage in UiBinder (again from the docs):
DialogBox elements in UiBinder templates can have one widget child and one
<g:caption> child.
So it seems that you need to replace your <table> with a GWT widget, most probably <g:HTMLPanel> and put whole <table> inside it.
Also, PopupPanel and DialogBox are standalone widgets and normally do not get added to other Widgets, but are shown via .show() and hide() methods. So, in your UiBinder, you can put
<g:DialogBox> at the root level.
I created a widget using GWT uiBinder. It works fine, till the moment when I want to instance it second time. After i call constructor second time it returns only raw description from XML and statements in constructor (rootElement.add( new HTML( "panel1" ), leftId );) are just don't work. It throws no error or warning.
Please help
Java class:
public class DashboardLayout extends Composite {
final String leftId = "boxLeft";
final String rightId = "boxRight";
interface DashboardLayoutUiBinder extends UiBinder<HTMLPanel, DashboardLayout> {
}
private static DashboardLayoutUiBinder ourUiBinder = GWT.create( DashboardLayoutUiBinder.class );
#UiField
HTMLPanel htmlPanel;
public DashboardLayout() {
HTMLPanel rootElement = ourUiBinder.createAndBindUi( this );
this.initWidget( rootElement );
rootElement.add( new HTML( "panel1" ), leftId );
rootElement.add( new HTML( "panel2" ), rightId );
}
}
XML descriprion:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
>
<g:HTMLPanel ui:field="htmlPanel">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="40%" id="boxLeft" class="boxContextLeft">
</td>
<td width="60%" id="boxRight" class="boxContextRight">
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>
Don't use id="myid" in widgets, as they will be global(which will screw you up) instead of scoped per instantiation of the widget; use ui:field="myid" and then create a corresponding UiField variable in the java class. This will allow the gwt compiler to obfuscate the id's so you don't get collisions between instantiations of the same widget.
DashboardLayout.java
public class DashboardLayout extends Composite {
interface DashboardLayoutUiBinder extends
UiBinder<HTMLPanel, DashboardLayout> {
}
private static DashboardLayoutUiBinder ourUiBinder = GWT
.create(DashboardLayoutUiBinder.class);
#UiField
HTMLPanel htmlPanel;
#UiField
HTML panel1;
#UiField
HTML panel2;
public DashboardLayout() {
HTMLPanel rootElement = ourUiBinder.createAndBindUi(this);
this.initWidget(rootElement);
// do stuff with panel1
panel1.setHTML("<blink>blink</blink>");
// do stuff with panel2
panel2.setHTML("<marquee>marquee</marquee>");
}
}
DashboardLayout.ui.xml
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:HTMLPanel ui:field="htmlPanel">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="40%" class="boxContextLeft">
<g:HTML ui:field="panel1"></g:HTML>
</td>
<td width="60%" class="boxContextRight">
<g:HTML ui:field="panel2"></g:HTML>
</td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>