package com.example.client.local; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import com.example.client.shared.Member; import com.example.client.shared.MemberService; import org.jboss.errai.bus.client.api.ErrorCallback; import org.jboss.errai.bus.client.api.Message; import org.jboss.errai.bus.client.api.RemoteCallback; import org.jboss.errai.ioc.client.api.Caller; import com.google.gwt.cell.client.SafeHtmlCell; import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeUri; import com.google.gwt.safehtml.shared.UriUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; /** * This is a GWT composite that gets added to the page by {@link KitchenSinkApp} * at page load time. The layout of this composite widget is declared in the * companion file KitchenSinkClient.ui.xml, which you will find in the same * source directory. * <p> * Note on software architecture: the embedded event handlers in this class * communicate directly with the server. Although this makes a small project * such as this one easier to understand at a glance, it is not a great approach * for long-term success. If you are planning to extend this example into a * large application, read up on the <a * href="http://code.google.com/webtoolkit/doc/latest/DevGuideMvpActivitiesAndPlaces.html">MVP pattern</a>, which is * recommended for larger apps. * * @author Jonathan Fuerth <jfuerth@redhat.com> * @author Christian Sadilek <csadilek@redhat.com> */ public class KitchenSinkClient extends Composite { private static final KitchenSinkTemplates TEMPLATES = GWT.create(KitchenSinkTemplates.class); private static KitchenSinkClientUiBinder uiBinder = GWT.create(KitchenSinkClientUiBinder.class); interface KitchenSinkClientUiBinder extends UiBinder<Widget, KitchenSinkClient> { } private final Caller<MemberService> memberService; /** * The list of members. Add to this list via the * {@link #addDisplayedMember(Member)} method to ensure the update is visible to * the user. */ private final List<Member> members = new ArrayList<Member>(); // The following fields are all injected during instance construction by GWT UiBinder @UiField Label generalErrorLabel; @UiField Button registerButton; @UiField Label registerConfirmMessage; @UiField TextBox nameBox; @UiField Label nameValidationErr; @UiField TextBox emailBox; @UiField Label emailValidationErr; @UiField TextBox phoneBox; @UiField Label phoneValidationErr; @UiField Label tableEmptyMessage; @UiField(provided=true) CellTable<Member> membersTable = new CellTable<Member>(); public KitchenSinkClient(Caller<MemberService> memberService) { this.memberService = memberService; initWidget(uiBinder.createAndBindUi(this)); // This sets up the structure of the Registered Members CellTable membersTable.addColumn(new Column<Member, String>(new TextCell()) { @Override public String getValue(Member m) { return m.getName(); } }, "Name"); membersTable.addColumn(new Column<Member, String>(new TextCell()) { @Override public String getValue(Member m) { return m.getEmail(); } }, "Email"); membersTable.addColumn(new Column<Member, String>(new TextCell()) { @Override public String getValue(Member m) { return m.getPhoneNumber(); } }, "Phone Number"); membersTable.addColumn(new Column<Member, SafeHtml>(new SafeHtmlCell()) { @Override public SafeHtml getValue(Member m) { String url = "rest/members/" + m.getId(); return TEMPLATES.link(UriUtils.fromString(url), url); } }, "REST URL"); } /** * Validates the new member data and sends it to the server if validation * passes. Displays validation messages if validation fails. * * @param event The click event (ignored) */ @UiHandler("registerButton") void onRegisterButtonClick(ClickEvent event) { Member newMember = new Member(); newMember.setName(nameBox.getText()); newMember.setEmail(emailBox.getText()); newMember.setPhoneNumber(phoneBox.getText()); nameValidationErr.setText(""); emailValidationErr.setText(""); phoneValidationErr.setText(""); registerConfirmMessage.setText(""); registerConfirmMessage.setStyleName("errorMessage"); Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<Member>> violations = validator.validate(newMember); for (ConstraintViolation<Member> cv : violations) { String prop = cv.getPropertyPath().toString(); if (prop.equals("name")) { nameValidationErr.setText(cv.getMessage()); } else if (prop.equals("email")) { emailValidationErr.setText(cv.getMessage()); } else if (prop.equals("phoneNumber")) { phoneValidationErr.setText(cv.getMessage()); } else { registerConfirmMessage.setText(cv.getMessage()); } } if (!violations.isEmpty()) return; memberService.call( new RemoteCallback<Void>() { @Override public void callback(Void response) { registerConfirmMessage.setText("Registration Complete!"); registerConfirmMessage.setStyleName("successMessage"); // the server will also broadcast a @New Member CDI event, which causes the table to update // so we don't have to do that here. } }, new ErrorCallback() { @Override public boolean error(Message message, Throwable throwable) { registerConfirmMessage.setText("Member registration failed: " + throwable.getMessage()); return false; } }).register(newMember); } /** * Adds the given member into the local Registered Members CellTable. Does not * communicate with the server. * * @param m * The member to add to the CellTable being displayed in the web * page. */ public void addDisplayedMember(Member m) { members.add(m); Collections.sort(members); membersTable.setRowData(members); setTableStatusMessage(""); } /** * Replaces the displayed list of members with the given list of members. * * @param members The list of members to display on the web page. Not null. */ public void setDisplayedMembers(List<Member> members) { this.members.clear(); this.members.addAll(members); membersTable.setRowData(this.members); if (members.isEmpty()) { setTableStatusMessage("No members registered yet."); } else { setTableStatusMessage(""); } } /** * Sets the general error message that appears near the top of the page. */ public void setGeneralErrorMessage(String string) { generalErrorLabel.setText(string); } /** * Sets the message that appears underneath the Registered Members table. */ public void setTableStatusMessage(String message) { tableEmptyMessage.setText(message); } /** * This is GWT's facility for building HTML snippets that are not vulnerable * to XSS attacks. * <p> * See <a href= * "http://code.google.com/webtoolkit/doc/latest/DevGuideSecuritySafeHtml.html#Creating_SafeHtml_Values" * >the GWT user guide</a> for details. */ public interface KitchenSinkTemplates extends SafeHtmlTemplates { @Template("<a target=\"_blank\" href=\"{0}\">{1}</a>") SafeHtml link(SafeUri url, String linkText); } }