乾安网站建设公司电话,世界新闻头条最新消息,九江网站设计公司,ppt制作教程免费全集webapp文本编辑器这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端#xff0c;带有Jersey的 Web服务服务器 #xff0c; 业务层 #xff0c; 带有Spring数据的持久性 #xff0c;分片集成测试数据 #xff0c; Webservice集成 #xff0c; JPA标准… webapp文本编辑器 这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端带有Jersey的 Web服务服务器 业务层 带有Spring数据的持久性 分片集成测试数据 Webservice集成 JPA标准查询和维护Webapp只读 。 上次我们创建了一个简单的Web应用程序使我们可以快速浏览数据库。 它的功能非常有限–主要目标是将整个系统从Web浏览器到数据库的整个系统组合在一起。 这次我们添加了实际的CRUD支持。 这篇文章是从Jumpstart网站大量借用的但有很大的不同。 有很多代码但是很容易重用。 局限性 用户身份验证 –尚未进行身份验证的工作。 加密 -尚未对通信进行加密。 分页 –尚未做出任何努力来支持分页。 Tapestry 5组件将显示分页外观但始终包含相同的第一页数据。 错误消息 –将显示错误消息但服务器端错误目前尚无信息。 跨站点脚本XSS –尚未做出任何努力来防止XSS攻击。 国际化 –尚未做出任何努力来支持国际化。 目标 我们需要标准的CRUD页面。 首先我们需要能够创建一门新课程。 当我们没有任何数据时我们的课程列表应包含一个链接作为默认消息。 第一个“创建...”是一个单独的元素。 现在创建页面具有多个字段。 代码唯一地标识一门课程例如CSCI 101其名称摘要和说明应不言自明。 创建成功后我们将进入评论页面。 如果需要进行更改然后返回更新页面。 任何时候我们都可以返回列表页面。 在删除记录之前系统会提示我们进行确认。 最后我们可以显示服务器端错误例如即使当前消息非常无用也要显示唯一字段中的重复值。 我们也有客户端错误检查尽管我在这里没有显示。 Index.tml 我们从索引页面开始。 这类似于我们在上一篇文章中看到的内容。 Tapestry 5具有三种主要类型的链接。 页面链接映射到标准HTML链接。 一个ActionLink的是直接由相应的类例如Index.java为Index.tml模板处理。 最后事件链接将事件注入挂毯引擎内的正常事件流。 我所有的链接都转到一个密切相关的页面所以我使用一个动作链接。 html t:typelayout titleCourse Listt:sidebarTitleFramework Versionxmlns:thttp://tapestry.apache.org/schema/tapestry_5_3.xsdxmlns:ptapestry:parametert:zone t:idzone pCourse page/pt:actionlink t:idcreateCreate.../t:actionlinkbr/t:grid sourcecourses rowcourse includecode, name,creationdate addedit,deletep:codecellt:actionlink t:idview contextcourse.uuid${course.code}/t:actionlink/p:codecellp:editcellt:actionlink t:idupdate contextcourse.uuidEdit/t:actionlink/p:editcellp:deletecellt:actionlink t:iddelete contextcourse.uuid t:mixinsConfirm t:messageDelete ${course.name}?Delete/t:actionlink/p:deletecellp:emptypThere are no courses to display; you can t:actionlink t:idcreate1create/t:actionlink one./p/p:empty/t:grid/t:zonep:sidebarp[t:pagelink pageIndexIndex/t:pagelink]br/[t:pagelink pageCourse/IndexCourses/t:pagelink]/p/p:sidebar/html确认混入 Index.tml模板在删除操作链接上包含一个“ mixin”。 它使用javascript和Java的混合显示弹出消息要求用户确认他要删除课程。 此代码直接来自Jumpstart和Tapestry网站。 // from http://jumpstart.doublenegative.com.au/
Confirm Class.create({initialize: function(elementId, message) {this.message message;Event.observe($(elementId), click, this.doConfirm.bindAsEventListener(this));},doConfirm: function(e) {// Pop up a javascript Confirm Box (see http://www.w3schools.com/js/js_popup.asp)if (!confirm(this.message)) {e.stop();}}
})// Extend the Tapestry.Initializer with a static method that instantiates a Confirm.Tapestry.Initializer.confirm function(spec) {new Confirm(spec.elementId, spec.message);
} 相应的Java代码是 // from http://jumpstart.doublenegative.com.au/
Import(library confirm.js)
public class Confirm {Parameter(name message, value Are you sure?, defaultPrefix BindingConstants.LITERAL)private String message;Injectprivate JavaScriptSupport javaScriptSupport;InjectContainerprivate ClientElement clientElement;AfterRenderpublic void afterRender() {// Tell the Tapestry.Initializer to do the initializing of a Confirm,// which it will do when the DOM has been// fully loaded.JSONObject spec new JSONObject();spec.put(elementId, clientElement.getClientId());spec.put(message, message);javaScriptSupport.addInitializerCall(confirm, spec);}
}Index.java 支持索引模板的Java很简单因为我们只需要定义一个数据源并提供一些动作处理程序即可。 package com.invariantproperties.sandbox.student.maintenance.web.pages.course;public class Index {PropertyInjectSymbol(SymbolConstants.TAPESTRY_VERSION)private String tapestryVersion;InjectComponentprivate Zone zone;Injectprivate CourseFinderService courseFinderService;Injectprivate CourseManagerService courseManagerService;Propertyprivate Course course;// our sibling pageInjectPageprivate com.invariantproperties.sandbox.student.maintenance.web.pages.course.Editor editorPage;/*** Get the datasource containing our data.* * return*/public GridDataSource getCourses() {return new CoursePagedDataSource(courseFinderService);}/*** Handle a delete request. This could fail, e.g., if the course has already* been deleted.* * param courseUuid*/void onActionFromDelete(String courseUuid) {courseManagerService.deleteCourse(courseUuid, 0);}/*** Bring up editor page in create mode.* * param courseUuid* return*/Object onActionFromCreate() {editorPage.setup(Mode.CREATE, null);return editorPage;}/*** Bring up editor page in create mode.* * param courseUuid* return*/Object onActionFromCreate1() {return onActionFromCreate();}/*** Bring up editor page in review mode.* * param courseUuid* return*/Object onActionFromView(String courseUuid) {editorPage.setup(Mode.REVIEW, courseUuid);return editorPage;}/*** Bring up editor page in update mode.* * param courseUuid* return*/Object onActionFromUpdate(String courseUuid) {editorPage.setup(Mode.UPDATE, courseUuid);return editorPage;}
}Editor.tml CRUD页面可以是三个单独的页面用于创建查看和更新也可以是一个页面。 我遵循的是Jumpstart网站使用的模式–一个页面。 老实说-我不确定他为什么做出这个决定-也许是因为页面紧密相关并且他使用事件处理 无论如何我将分别讨论这些元素。 创建模板 “创建”模板是一种简单的形式。 您可以看到HTML input元素得到了增强它们具有一些特定于挂毯的属性以及一些其他标记例如terrors /和tsubmit。 CustomForm和CustomError是标准Tapestry Form和Error类的本地扩展。 它们目前为空但允许我们轻松添加本地扩展。 html t:typelayout titleCourse Editort:sidebarTitleFramework Versionxmlns:thttp://tapestry.apache.org/schema/tapestry_5_3.xsd xmlns:ptapestry:parametert:zone t:idzone t:if testmodeCreateh1Create/h1form t:typeform t:idcreateForm t:errors/tabletrtht:label forcode/:/thtdinput t:typeTextField t:idcode valuecourse.code t:validaterequired, maxlength12 size12//tdtd(required)/td/trtr classerrth/thtd colspan2t:CustomError forcode//td/trtrtht:label forname/:/thtdinput t:typeTextField t:idname valuecourse.name t:validaterequired, maxlength80 size45//tdtd(required)/td/trtr classerrth/thtd colspan2t:CustomError forname//td/trtrtht:label forsummary/:/thtdinput cols50 rows4 t:typeTextArea t:idsummary valuecourse.summary t:validatemaxlength400//td/trtr classerrth/thtd colspan2t:CustomError forsummary//td/trtrtht:label fordescription/:/thtdinput cols50 rows12 t:typeTextArea t:iddescription valuecourse.description t:validatemaxlength2000//td/trtr classerrth/thtd colspan2t:CustomError fordescription//td/tr/tablediv classbuttonst:submit t:eventcancelCreate t:contextcourse.uuid valueCancel/input typesubmit valueSave//div/form/t:if...
/html 创建java 相应的java类很简单。 我们必须定义一些自定义事件。 ActivationRequestParameter值是从URL查询字符串中提取的。 课程字段包含创建新对象时要使用的值。 courseForm字段在模板上包含相应的form。 indexPage包含对索引页的引用。 有四个名为onEventFromCreateForm的事件处理程序其中event 可以准备验证成功或失败。 每个事件处理程序都有非常特定的角色。 还有一个附加的事件处理程序onCancelCreate 。 您可以在模板的tsubmit标记中看到该事件的名称。 /*** This component will trigger the following events on its container (which in* this example is the page):* {link Editor.web.components.examples.component.crud.Editor#CANCEL_CREATE} ,* {link Editor.web.components.examples.component.crud.Editor#SUCCESSFUL_CREATE}* (Long courseUuid),* {link Editor.web.components.examples.component.crud.Editor#FAILED_CREATE} ,*/
// Events is applied to a component solely to document what events it may
// trigger. It is not checked at runtime.
Events({ Editor.CANCEL_CREATE, Editor.SUCCESSFUL_CREATE, Editor.FAILED_CREATE })
public class Editor {public static final String CANCEL_CREATE cancelCreate;public static final String SUCCESSFUL_CREATE successfulCreate;public static final String FAILED_CREATE failedCreate;public enum Mode {CREATE, REVIEW, UPDATE;}// ParametersActivationRequestParameterPropertyprivate Mode mode;ActivationRequestParameterPropertyprivate String courseUuid;// Screen fieldsPropertyprivate Course course;// Work fields// This carries version through the redirect that follows a server-side// validation failure.Persist(PersistenceConstants.FLASH)private Integer versionFlash;// Generally useful bits and piecesInjectprivate CourseFinderService courseFinderService;Injectprivate CourseManagerService courseManagerService;Componentprivate CustomForm createForm;Injectprivate ComponentResources componentResources;InjectPageprivate com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;// The codepublic void setup(Mode mode, String courseUuid) {this.mode mode;this.courseUuid courseUuid;}// setupRender() is called by Tapestry right before it starts rendering the// component.void setupRender() {if (mode Mode.REVIEW) {if (courseUuid null) {course null;// Handle null course in the template.} else {if (course null) {try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {// Handle null course in the template.}}}}}// /// CREATE// /// Handle event cancelCreateObject onCancelCreate() {return indexPage;}// Component createForm bubbles up the PREPARE event when it is rendered// or submittedvoid onPrepareFromCreateForm() throws Exception {// Instantiate a Course for the form data to overlay.course new Course();}// Component createForm bubbles up the VALIDATE event when it is submittedvoid onValidateFromCreateForm() {if (createForm.getHasErrors()) {// We get here only if a server-side validator detected an error.return;}try {course courseManagerService.createCourse(course.getCode(), course.getName(), course.getSummary(),course.getDescription(), 1);} catch (RestClientFailureException e) {createForm.recordError(Internal error on server.);createForm.recordError(e.getMessage());} catch (Exception e) {createForm.recordError(ExceptionUtil.getRootCauseMessage(e));}}// Component createForm bubbles up SUCCESS or FAILURE when it is// submitted, depending on whether VALIDATE// records an errorboolean onSuccessFromCreateForm() {componentResources.triggerEvent(SUCCESSFUL_CREATE, new Object[] { course.getUuid() }, null);// We dont want success to bubble up, so we return true to say weve// handled it.mode Mode.REVIEW;courseUuid course.getUuid();return true;}boolean onFailureFromCreateForm() {// Rather than letting failure bubble up which doesnt say what you// were trying to do, we trigger new event// failedCreate. It will bubble up because we dont have a handler// method for it.componentResources.triggerEvent(FAILED_CREATE, null, null);// We dont want failure to bubble up, so we return true to say weve// handled it.return true;}....
} 评论模板 “审阅”模板是一个简单的表。 它以表格形式包装但仅用于页面底部的导航按钮。 t:if testmodeReviewh1Review/h1strongWarning: no attempt is made to block XSS/strongform t:typeform t:idreviewFormt:errors/t:if testcoursediv t:typeif t:testdeleteMessage classerror${deleteMessage}/divtabletrthUuid:/thtd${course.uuid}/td/trtrthCode:/thtd${course.code}/td/trtrthName:/thtd${course.name}/td/trtrthSummary:/thtd${course.summary}/td/trtrthDescription:/thtd${course.description}/td/tr/tablediv classbuttonst:submit t:eventtoIndex t:contextcourse.uuid valueList/t:submit t:eventtoUpdate t:contextcourse.uuid valueUpdate/t:submit t:eventdelete t:contextcourse.uuid t:mixinsConfirm t:messageDelete ${course.name}? valueDelete//div/t:ift:if negatetrue testcourseCourse ${courseUuid} does not exist.br/br//t:if/form/t:if 回顾java 审阅表单所需的Java很简单-我们只需要加载数据即可。 我希望setupRender足够但实际上我需要onPrepareFromReviewForm方法。 public class Editor {public enum Mode {CREATE, REVIEW, UPDATE;}// ParametersActivationRequestParameterPropertyprivate Mode mode;ActivationRequestParameterPropertyprivate String courseUuid;// Screen fieldsPropertyprivate Course course;// Generally useful bits and piecesInjectprivate CourseFinderService courseFinderService;Componentprivate CustomForm reviewForm;Injectprivate ComponentResources componentResources;InjectPageprivate com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;// The codepublic void setup(Mode mode, String courseUuid) {this.mode mode;this.courseUuid courseUuid;}// setupRender() is called by Tapestry right before it starts rendering the// component.void setupRender() {if (mode Mode.REVIEW) {if (courseUuid null) {course null;// Handle null course in the template.} else {if (course null) {try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {// Handle null course in the template.}}}}}// /// REVIEW// /void onPrepareFromReviewForm() {try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {// Handle null course in the template.}}// /// PAGE NAVIGATION// /// Handle event toUpdateboolean onToUpdate(String courseUuid) {mode Mode.UPDATE;return false;}// Handle event toIndexObject onToIndex() {return indexPage;}....
} UPDATE模板 最后“更新”模板看起来类似于“创建”模板。 t:if testmodeUpdateh1Update/h1strongWarning: no attempt is made to block XSS/strongform t:typeform t:idupdateFormt:errors/t:if testcourse!-- If optimistic locking is not needed then comment out this next line. It works because Hidden fields are part of the submit. --!-- t:hidden valuecourse.version/ --tabletrtht:label forupdCode/:/thtdinput t:typeTextField t:idupdCode valuecourse.code t:disabledtrue size12//tdtd(read-only)/td/trtr classerrth/thtd colspan2t:CustomError forupdName//td/trtrtht:label forupdName/:/thtdinput t:typeTextField t:idupdName valuecourse.name t:validaterequired, maxlength80 size45//tdtd(required)/td/trtr classerrth/thtd colspan2t:CustomError forupdSummary//td/trtrtht:label forupdSummary/:/thtdinput cols50 rows4 t:typeTextArea t:idupdSummary valuecourse.summary t:validatemaxlength400//td/trtr classerrth/thtd colspan2t:CustomError forupdSummary//td/trtrtht:label forupdDescription/:/thtdinput cols50 rows12 t:typeTextArea t:idupdDescription valuecourse.description t:validatemaxlength50//td/trtr classerrth/thtd colspan2t:CustomError forupdDescription//td/tr/tablediv classbuttonst:submit t:eventtoIndex t:contextcourse.uuid valueList/t:submit t:eventcancelUpdate t:contextcourse.uuid valueCancel/input t:typesubmit valueSave//div/t:ift:if negatetrue testcourseCourse ${courseUuid} does not exist.br/br//t:if/form /t:if 更新java 同样“更新” Java代码看起来很像“创建” Java代码。 最大的区别是我们必须能够处理在尝试更新数据库之前已删除课程的比赛条件。 Events({ Editor.TO_UPDATE, Editor.CANCEL_UPDATE,Editor.SUCCESSFUL_UPDATE, Editor.FAILED_UPDATE })
public class Editor {public static final String TO_UPDATE toUpdate;public static final String CANCEL_UPDATE cancelUpdate;public static final String SUCCESSFUL_UPDATE successfulUpdate;public static final String FAILED_UPDATE failedUpdate;public enum Mode {CREATE, REVIEW, UPDATE;}// ParametersActivationRequestParameterPropertyprivate Mode mode;ActivationRequestParameterPropertyprivate String courseUuid;// Screen fieldsPropertyprivate Course course;PropertyPersist(PersistenceConstants.FLASH)private String deleteMessage;// Work fields// This carries version through the redirect that follows a server-side// validation failure.Persist(PersistenceConstants.FLASH)private Integer versionFlash;// Generally useful bits and piecesInjectprivate CourseFinderService courseFinderService;Injectprivate CourseManagerService courseManagerService;Componentprivate CustomForm updateForm;Injectprivate ComponentResources componentResources;InjectPageprivate com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;// The codepublic void setup(Mode mode, String courseUuid) {this.mode mode;this.courseUuid courseUuid;}// setupRender() is called by Tapestry right before it starts rendering the// component.void setupRender() {if (mode Mode.REVIEW) {if (courseUuid null) {course null;// Handle null course in the template.} else {if (course null) {try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {// Handle null course in the template.}}}}}// /// UPDATE// /// Handle event cancelUpdateObject onCancelUpdate(String courseUuid) {return indexPage;}// Component updateForm bubbles up the PREPARE_FOR_RENDER event during// form rendervoid onPrepareForRenderFromUpdateForm() {try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {// Handle null course in the template.}// If the form has errors then were redisplaying after a redirect.// Form will restore your input values but its up to us to restore// Hidden values.if (updateForm.getHasErrors()) {if (course ! null) {course.setVersion(versionFlash);}}}// Component updateForm bubbles up the PREPARE_FOR_SUBMIT event during for// submissionvoid onPrepareForSubmitFromUpdateForm() {// Get objects for the form fields to overlay.try {course courseFinderService.findCourseByUuid(courseUuid);} catch (ObjectNotFoundException e) {course new Course();updateForm.recordError(Course has been deleted by another process.);}}// Component updateForm bubbles up the VALIDATE event when it is submittedvoid onValidateFromUpdateForm() {if (updateForm.getHasErrors()) {// We get here only if a server-side validator detected an error.return;}try {courseManagerService.updateCourse(course, course.getName(), course.getSummary(), course.getDescription(), 1);} catch (RestClientFailureException e) {updateForm.recordError(Internal error on server.);updateForm.recordError(e.getMessage());} catch (Exception e) {// Display the cause. In a real system we would try harder to get a// user-friendly message.updateForm.recordError(ExceptionUtil.getRootCauseMessage(e));}}// Component updateForm bubbles up SUCCESS or FAILURE when it is// submitted, depending on whether VALIDATE// records an errorboolean onSuccessFromUpdateForm() {// We want to tell our containing page explicitly what course weve// updated, so we trigger new event// successfulUpdate with a parameter. It will bubble up because we// dont have a handler method for it.componentResources.triggerEvent(SUCCESSFUL_UPDATE, new Object[] { courseUuid }, null);// We dont want success to bubble up, so we return true to say weve// handled it.mode Mode.REVIEW;return true;}boolean onFailureFromUpdateForm() {versionFlash course.getVersion();// Rather than letting failure bubble up which doesnt say what you// were trying to do, we trigger new event// failedUpdate. It will bubble up because we dont have a handler// method for it.componentResources.triggerEvent(FAILED_UPDATE, new Object[] { courseUuid }, null);// We dont want failure to bubble up, so we return true to say weve// handled it.return true;}
} 删除模板和Java 编辑器没有显式的“删除”模式但确实支持删除审阅和更新页面上的当前对象。 // /// DELETE// /// Handle event deleteObject onDelete(String courseUuid) {this.courseUuid courseUuid;int courseVersion 0;try {courseManagerService.deleteCourse(courseUuid, courseVersion);} catch (ObjectNotFoundException e) {// the objects already deleted} catch (RestClientFailureException e) {createForm.recordError(Internal error on server.);createForm.recordError(e.getMessage());// Display the cause. In a real system we would try harder to get a// user-friendly message.deleteMessage ExceptionUtil.getRootCauseMessage(e);// Trigger new event failedDelete which will bubble up.componentResources.triggerEvent(FAILED_DELETE, new Object[] { courseUuid }, null);// We dont want delete to bubble up, so we return true to say// weve handled it.return true;} catch (Exception e) {// Display the cause. In a real system we would try harder to get a// user-friendly message.deleteMessage ExceptionUtil.getRootCauseMessage(e);// Trigger new event failedDelete which will bubble up.componentResources.triggerEvent(FAILED_DELETE, new Object[] { courseUuid }, null);// We dont want delete to bubble up, so we return true to say// weve handled it.return true;}// Trigger new event successfulDelete which will bubble up.componentResources.triggerEvent(SUCCESFUL_DELETE, new Object[] { courseUuid }, null);// We dont want delete to bubble up, so we return true to say weve// handled it.return indexPage;}下一步 显而易见的下一步是改进错误消息增加对分页支持以及一对多和多对多关系的支持。 所有这些都需要修改REST负载。 我在管道中还有一些其他项目例如ExceptionService更不用说安全问题了。 源代码 可从http://code.google.com/p/invariant-properties-blog/source/browse/student获取源代码。 参考 项目学生 Invariant Properties博客上来自JCG合作伙伴 Bear Giles的Maintenance Webapp可编辑 。 翻译自: https://www.javacodegeeks.com/2014/02/project-student-maintenance-webapp-editable.htmlwebapp文本编辑器