webapp文本编辑器_Project Student:维护Webapp(可编辑)

webapp文本编辑器

这是Project Student的一部分。 其他职位包括带有Jersey的 Web服务 客户端,带有Jersey的 Web服务服务器业务层带有Spring数据的持久性 ,分片集成测试数据Webservice集成JPA标准查询维护Webapp(只读)

上次我们创建了一个简单的Web应用程序,使我们可以快速浏览数据库。 它的功能非常有限–主要目标是将整个系统(从Web浏览器到数据库)的整个系统组合在一起。 这次我们添加了实际的CRUD支持。

这篇文章是从Jumpstart网站大量借用的,但有很大的不同。 有很多代码,但是很容易重用。

局限性

  • 用户身份验证 –尚未进行身份验证的工作。
  • 加密 -尚未对通信进行加密。
  • 分页 –尚未做出任何努力来支持分页。 Tapestry 5组件将显示分页外观,但始终包含相同的第一页数据。
  • 错误消息 –将显示错误消息,但服务器端错误目前尚无信息。
  • 跨站点脚本(XSS) –尚未做出任何努力来防止XSS攻击。
  • 国际化 –尚未做出任何努力来支持国际化。

目标

我们需要标准的CRUD页面。

首先,我们需要能够创建一门新课程。 当我们没有任何数据时,我们的课程列表应包含一个链接作为默认消息。 (第一个“创建...”是一个单独的元素。)

课程列表Chromium_008

现在,创建页面具有多个字段。 代码唯一地标识一门课程,例如CSCI 101,其名称,摘要和说明应不言自明。

课程编辑器Chromium_006

创建成功后,我们将进入评论页面。

课程编辑器Chromium_002

如果需要进行更改,然后返回更新页面。

课程编辑器Chromium_004

任何时候我们都可以返回列表页面。

课程列表Chromium_001

在删除记录之前,系统会提示我们进行确认。

Course-List-Chromium_005

最后,我们可以显示服务器端错误,例如,即使当前消息非常无用,也要显示唯一字段中的重复值。

课程编辑器Chromium_007

我们也有客户端错误检查,尽管我在这里没有显示。

Index.tml

我们从索引页面开始。 这类似于我们在上一篇文章中看到的内容。

Tapestry 5具有三种主要类型的链接。 页面链接映射到标准HTML链接。 一个ActionLink的是直接由相应的类,例如,Index.javaIndex.tml模板处理。 最后,事件链接将事件注入挂毯引擎内的正常事件流。 我所有的链接都转到一个密切相关的页面,所以我使用一个动作链接。

<html t:type="layout" title="Course List"
      t:sidebarTitle="Framework Version"
      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
      xmlns:p="tapestry:parameter">

    <t:zone t:id="zone">   
        <p>
            "Course" page
        </p>

        <t:actionlink t:id="create">Create...</t:actionlink><br/>

        <t:grid source="courses" row="course" include="code, name,creationdate" add="edit,delete">
            <p:codecell>
                <t:actionlink t:id="view" context="course.uuid">${course.code}</t:actionlink>
            </p:codecell>
            <p:editcell>
                <t:actionlink t:id="update" context="course.uuid">Edit</t:actionlink>
            </p:editcell>
            <p:deletecell>
                <t:actionlink t:id="delete" context="course.uuid" t:mixins="Confirm" t:message="Delete ${course.name}?">Delete</t:actionlink>
            </p:deletecell>
            <p:empty>
              <p>There are no courses to display; you can <t:actionlink t:id="create1">create</t:actionlink> one.</p>
            </p:empty>
        </t:grid>
    </t:zone>

    <p:sidebar>
        <p>
            [
            <t:pagelink page="Index">Index</t:pagelink>
            ]<br/>
            [
            <t:pagelink page="Course/Index">Courses</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;

    @Inject
    private JavaScriptSupport javaScriptSupport;

    @InjectContainer
    private ClientElement clientElement;

    @AfterRender
    public 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 {
    @Property
    @Inject
    @Symbol(SymbolConstants.TAPESTRY_VERSION)
    private String tapestryVersion;

    @InjectComponent
    private Zone zone;

    @Inject
    private CourseFinderService courseFinderService;

    @Inject
    private CourseManagerService courseManagerService;

    @Property
    private Course course;

    // our sibling page
    @InjectPage
    private 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>元素得到了增强,它们具有一些特定于挂毯的属性,以及一些其他标记,例如<t:errors />和<t:submit>。

CustomFormCustomError是标准Tapestry FormError类的本地扩展。 它们目前为空,但允许我们轻松添加本地扩展。

<html t:type="layout" title="Course Editor"
      t:sidebarTitle="Framework Version"
      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">

    <t:zone t:id="zone">   

    <t:if test="modeCreate">
        <h1>Create</h1>

        <form t:type="form" t:id="createForm" >
            <t:errors/>

            <table>
                <tr>
                    <th><t:label for="code"/>:</th>
                    <td><input t:type="TextField" t:id="code" value="course.code" t:validate="required, maxlength=12" size="12"/></td>
                    <td>(required)</td>
                </tr>
                <tr class="err">
                    <th></th>
                    <td colspan="2"><t:CustomError for="code"/></td>
                </tr>
                <tr>
                    <th><t:label for="name"/>:</th>
                    <td><input t:type="TextField" t:id="name" value="course.name" t:validate="required, maxlength=80" size="45"/></td>
                    <td>(required)</td>
                </tr>
                <tr class="err">
                    <th></th>
                    <td colspan="2"><t:CustomError for="name"/></td>
                </tr>
                <tr>
                    <th><t:label for="summary"/>:</th>
                    <td><input cols="50" rows="4" t:type="TextArea" t:id="summary" value="course.summary" t:validate="maxlength=400"/></td>
                </tr>
                <tr class="err">
                    <th></th>
                    <td colspan="2"><t:CustomError for="summary"/></td>
                </tr>
                <tr>
                    <th><t:label for="description"/>:</th>
                    <td><input cols="50" rows="12" t:type="TextArea" t:id="description" value="course.description" t:validate="maxlength=2000"/></td>
                </tr>
                <tr class="err">
                    <th></th>
                    <td colspan="2"><t:CustomError for="description"/></td>
                </tr>
            </table>

            <div class="buttons">
                <t:submit t:event="cancelCreate" t:context="course.uuid" value="Cancel"/>
                <input type="submit" value="Save"/>
            </div>
        </form>
    </t:if>

    ...
</html>
创建java
  • 相应的java类很简单。 我们必须定义一些自定义事件。
  • ActivationRequestParameter值是从URL查询字符串中提取的。
  • 课程字段包含创建新对象时要使用的值。
  • courseForm字段在模板上包含相应的<form>。
  • indexPage包含对索引页的引用。

有四个名为onEventFromCreateForm的事件处理程序,其中event
可以准备,验证,成功或失败。 每个事件处理程序都有非常特定的角色。

还有一个附加的事件处理程序onCancelCreate() 。 您可以在模板的<t:submit>标记中看到该事件的名称。

/**
 * 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;
    }

    // Parameters

    @ActivationRequestParameter
    @Property
    private Mode mode;

    @ActivationRequestParameter
    @Property
    private String courseUuid;

    // Screen fields

    @Property
    private 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 pieces

    @Inject
    private CourseFinderService courseFinderService;

    @Inject
    private CourseManagerService courseManagerService;

    @Component
    private CustomForm createForm;

    @Inject
    private ComponentResources componentResources;

    @InjectPage
    private com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;

    // The code

    public 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 "cancelCreate"

    Object onCancelCreate() {
        return indexPage;
    }

    // Component "createForm" bubbles up the PREPARE event when it is rendered
    // or submitted

    void 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 submitted

    void 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 error

    boolean onSuccessFromCreateForm() {
        componentResources.triggerEvent(SUCCESSFUL_CREATE, new Object[] { course.getUuid() }, null);

        // We don't want "success" to bubble up, so we return true to say we've
        // handled it.
        mode = Mode.REVIEW;
        courseUuid = course.getUuid();
        return true;
    }

    boolean onFailureFromCreateForm() {
        // Rather than letting "failure" bubble up which doesn't say what you
        // were trying to do, we trigger new event
        // "failedCreate". It will bubble up because we don't have a handler
        // method for it.
        componentResources.triggerEvent(FAILED_CREATE, null, null);

        // We don't want "failure" to bubble up, so we return true to say we've
        // handled it.
        return true;
    }

    ....
}
评论模板

“审阅”模板是一个简单的表。 它以表格形式包装,但仅用于页面底部的导航按钮。

<t:if test="modeReview">
        <h1>Review</h1>

        <strong>Warning: no attempt is made to block XSS</strong>

        <form t:type="form" t:id="reviewForm">
            <t:errors/>

        <t:if test="course">
            <div t:type="if" t:test="deleteMessage" class="error">
                ${deleteMessage}
            </div>

            <table>
                <tr>
                    <th>Uuid:</th>
                    <td>${course.uuid}</td>
                </tr>
                <tr>
                    <th>Code:</th>
                    <td>${course.code}</td>
                </tr>
                <tr>
                    <th>Name:</th>
                    <td>${course.name}</td>
                </tr>
                <tr>
                    <th>Summary:</th>
                    <td>${course.summary}</td>
                </tr>
                <tr>
                    <th>Description:</th>
                    <td>${course.description}</td>
                </tr>
            </table>

            <div class="buttons">
                <t:submit t:event="toIndex" t:context="course.uuid" value="List"/>
                <t:submit t:event="toUpdate" t:context="course.uuid" value="Update"/>
                <t:submit t:event="delete" t:context="course.uuid" t:mixins="Confirm" t:message="Delete ${course.name}?" value="Delete"/>
            </div>

        </t:if>
        <t:if negate="true" test="course">
            Course ${courseUuid} does not exist.<br/><br/>
        </t:if>
        </form>
    </t:if>
回顾java

审阅表单所需的Java很简单-我们只需要加载数据即可。 我希望setupRender()足够,但实际上我需要onPrepareFromReviewForm()方法。

public class Editor {

    public enum Mode {
        CREATE, REVIEW, UPDATE;
    }

    // Parameters

    @ActivationRequestParameter
    @Property
    private Mode mode;

    @ActivationRequestParameter
    @Property
    private String courseUuid;

    // Screen fields

    @Property
    private Course course;

    // Generally useful bits and pieces

    @Inject
    private CourseFinderService courseFinderService;

    @Component
    private CustomForm reviewForm;

    @Inject
    private ComponentResources componentResources;

    @InjectPage
    private com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;

    // The code

    public 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 "toUpdate"

    boolean onToUpdate(String courseUuid) {
        mode = Mode.UPDATE;
        return false;
    }

    // Handle event "toIndex"

    Object onToIndex() {
        return indexPage;
    }

    ....
}
UPDATE模板

最后,“更新”模板看起来类似于“创建”模板。

<t:if test="modeUpdate">
        <h1>Update</h1>

        <strong>Warning: no attempt is made to block XSS</strong>

        <form t:type="form" t:id="updateForm">
            <t:errors/>

            <t:if test="course">
                <!-- If optimistic locking is not needed then comment out this next line. It works because Hidden fields are part of the submit. -->
                <!-- <t:hidden value="course.version"/> -->

                <table>
                    <tr>
                        <th><t:label for="updCode"/>:</th>
                        <td><input t:type="TextField" t:id="updCode" value="course.code" t:disabled="true" size="12"/></td>
                        <td>(read-only)</td>
                    </tr>
                    <tr class="err">
                        <th></th>
                        <td colspan="2"><t:CustomError for="updName"/></td>
                    </tr>
                    <tr>
                        <th><t:label for="updName"/>:</th>
                        <td><input t:type="TextField" t:id="updName" value="course.name" t:validate="required, maxlength=80" size="45"/></td>
                        <td>(required)</td>
                    </tr>
                    <tr class="err">
                        <th></th>
                        <td colspan="2"><t:CustomError for="updSummary"/></td>
                    </tr>
                    <tr>
                        <th><t:label for="updSummary"/>:</th>
                        <td><input cols="50" rows="4" t:type="TextArea" t:id="updSummary" value="course.summary" t:validate="maxlength=400"/></td>
                    </tr>
                    <tr class="err">
                        <th></th>
                        <td colspan="2"><t:CustomError for="updSummary"/></td>
                    </tr>
                    <tr>
                        <th><t:label for="updDescription"/>:</th>
                        <td><input cols="50" rows="12" t:type="TextArea" t:id="updDescription" value="course.description" t:validate="maxlength=50"/></td>
                    </tr>
                    <tr class="err">
                        <th></th>
                        <td colspan="2"><t:CustomError for="updDescription"/></td>
                    </tr>
                </table>

                <div class="buttons">
                    <t:submit t:event="toIndex" t:context="course.uuid" value="List"/>
                    <t:submit t:event="cancelUpdate" t:context="course.uuid" value="Cancel"/>
                    <input t:type="submit" value="Save"/>
                </div>
            </t:if>
            <t:if negate="true" test="course">
                Course ${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;
    }

    // Parameters

    @ActivationRequestParameter
    @Property
    private Mode mode;

    @ActivationRequestParameter
    @Property
    private String courseUuid;

    // Screen fields

    @Property
    private Course course;

    @Property
    @Persist(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 pieces

    @Inject
    private CourseFinderService courseFinderService;

    @Inject
    private CourseManagerService courseManagerService;

    @Component
    private CustomForm updateForm;

    @Inject
    private ComponentResources componentResources;

    @InjectPage
    private com.invariantproperties.sandbox.student.maintenance.web.pages.course.Index indexPage;

    // The code

    public 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 "cancelUpdate"

    Object onCancelUpdate(String courseUuid) {
        return indexPage;
    }

    // Component "updateForm" bubbles up the PREPARE_FOR_RENDER event during
    // form render

    void onPrepareForRenderFromUpdateForm() {
        try {
            course = courseFinderService.findCourseByUuid(courseUuid);
        } catch (ObjectNotFoundException e) {
            // Handle null course in the template.
        }

        // If the form has errors then we're redisplaying after a redirect.
        // Form will restore your input values but it's 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
    // submission

    void 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 submitted

    void 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 error

    boolean onSuccessFromUpdateForm() {
        // We want to tell our containing page explicitly what course we've
        // updated, so we trigger new event
        // "successfulUpdate" with a parameter. It will bubble up because we
        // don't have a handler method for it.
        componentResources.triggerEvent(SUCCESSFUL_UPDATE, new Object[] { courseUuid }, null);

        // We don't want "success" to bubble up, so we return true to say we've
        // handled it.
        mode = Mode.REVIEW;
        return true;
    }

    boolean onFailureFromUpdateForm() {
        versionFlash = course.getVersion();

        // Rather than letting "failure" bubble up which doesn't say what you
        // were trying to do, we trigger new event
        // "failedUpdate". It will bubble up because we don't have a handler
        // method for it.
        componentResources.triggerEvent(FAILED_UPDATE, new Object[] { courseUuid }, null);
        // We don't want "failure" to bubble up, so we return true to say we've
        // handled it.
        return true;
    }
}
删除模板和Java

编辑器没有显式的“删除”模式,但确实支持删除审阅和更新页面上的当前对象。

// /
    // DELETE
    // /

    // Handle event "delete"

    Object onDelete(String courseUuid) {
        this.courseUuid = courseUuid;
        int courseVersion = 0;

        try {
            courseManagerService.deleteCourse(courseUuid, courseVersion);
        } catch (ObjectNotFoundException e) {
            // the object's 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 don't want "delete" to bubble up, so we return true to say
            // we've 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 don't want "delete" to bubble up, so we return true to say
            // we've handled it.
            return true;
        }

        // Trigger new event "successfulDelete" which will bubble up.
        componentResources.triggerEvent(SUCCESFUL_DELETE, new Object[] { courseUuid }, null);
        // We don't want "delete" to bubble up, so we return true to say we've
        // handled it.
        return indexPage;
    }

下一步

显而易见的下一步是改进错误消息,增加对分页,支持以及一对多和多对多关系的支持。 所有这些都需要修改REST负载。 我在管道中还有一些其他项目,例如ExceptionService,更不用说安全问题了。

源代码


翻译自: https://www.javacodegeeks.com/2014/02/project-student-maintenance-webapp-editable.html

webapp文本编辑器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值