gpt4 book ai didi

javascript - 处理grails中对象的未保存状态

转载 作者:行者123 更新时间:2023-11-29 21:44:35 24 4
gpt4 key购买 nike

我有一个问题,可能是设计问题,而不是技术问题。

假设我有这些类(class):

class A {
def someInfo
static hasMany = [b: B]
}

class B {
A a
def info
def moreInfo
...
def aLotMoreInfo
}

现在,假设用户在页面上,他可以在其中编辑A的b并添加新的b。但是用户需要将其更改保存到A,否则所有内容将被丢弃。

我当前的方法是创建其他b,通过AJAX渲染它们并将其ID保存在 session 变量中,这样我就可以删除“未保存”的b。

这很好用,但是对于一个常见用例:
用户刷新页面。

我使用window.onunload-event通知用户他将丢失未保存的更改,并对其内的delete函数进行AJAX调用,以从 session 变量中删除b。
不幸的是,在删除b之前先调用A Controller 的索引函数。
这意味着,将显示“未保存的” b,然后不久将其删除,这将迫使我进行刷新或等待以某种方式删除b。

也许我尝试实现此目标的方法是错误的-在这种情况下,我很乐意提供任何建议。

所以问题是:如何关注可能会丢弃的新对象,而无需将其所有信息存储在隐藏字段中以在保存功能上创建它们?

更新:

我之前应该提到它,但我认为它并不那么重要。

B是一个抽象类,它由许多类扩展,例如以下示例:
class childOfB extends B {
def usefulExtraInfo
}

class anotherChildOfB extends B {
def anotherUsefulExtraInfo
}

B旁边有一个整数字段,表示A中b的集合内的位置。我知道我可以为此使用SortedSet,但是出于某些特定原因,它必须是一个单独的字段。
我之所以这样说是因为 View 将每个 View 都呈现为可排序列表的元素,可以通过拖放对其进行重新排序。

用例:用户添加一些childOfB,anotherChildOfB并根据需要对其进行重新排序。我如何在不将其存储在 View 中的情况下跟踪它们的类型,我认为这也是一种不好的做法?

问候

最佳答案

the user needs to save his changes to A, otherwise everything will be discarded



在我看来,您渴望在不需要时创建 B,您只想在用户通过保存 A确认整个操作时创建它们。

a page where he can edit the b's of an A and he can add new b's



看起来您好像只有一个页面,其中显示了所有的 B以便进行编辑,因此没有必要在各处保留隐藏字段。

然后,我要做的就是使用标准格式输入将 View 中的所有当前更改保留在 View 中,并调用一个事务性操作,该操作将保存 A并根据参数创建/修改/删除 B

根据您的应用程序的外观,您可以通过多种方式执行此操作。

我过去使用过的一个样例是有一个模板(假设为 editB),该模板接收 B,索引和 prefix并显示该给定 B的相应输入,并以 ${property}.作为前缀(即,它呈现给定的编辑模式下的 B)。

然后, A的编辑 View 将为其具有的所有 editB呈现 B,并且:
  • 添加新的B将触发Ajax调用,以检索该模板以获取新的B,前缀b(A的属性名称)和对应于列表长度的索引。
  • 删除B会简单地删除与模板相对应的HTML片段,然后重新计算索引。

  • 然后,在保存 A时, Controller 将检查 params.list('b')中的内容,并相应地创建,更新和删除。

    通常,它类似于:

    模板/templates/_editB.gsp
    <g:if test="${instance.id}">
    <input type="hidden" name="${prefix}.id" value="${instance.id}" />
    </g:if>
    <g:else>
    <input type="hidden" name="${prefix}.domainClassName" value=${instance.domainClass.clazz.name}" />
    </g:else>
    <input type="hidden" name="${prefix}.index" value=${instance.index}" />
    <input type="..." name="${prefix}.info" value="${instance.info}" />

    编辑A 的 View
    <g:each var="b" in="${a.b.sort { it.index }}">
    <g:render template="/templates/editB" model="${[instance: b, prefix: 'b']}" />
    <button onClick="deleteTheBJustUpThereAndTriggerIndexRecalculation()">Remove</button>
    </g:each>
    <button onClick="addNewBByInvokingAController#renderNewB(calculateMaxIndex())">Remove</button>

    AController :
    class AController {

    private B getBInstance(String domainClassName, Map params) {
    grailsApplication
    .getDomainClass(domainClassName)
    .clazz.newInstance(params)
    }

    def renderNewB(Integer index, String domainClassName) {
    render template: '/templates/editB', model: [
    instance: getBInstance(domainClassName, [index: index]),
    prefix: 'b'
    ]
    }

    def save(Long id) {
    A a = a.get(id)
    bindData(a, params, [exclude: ['b']]) // We manually bind b
    List bsToBind = params.list('b')
    List<B> removedBs = a.b.findAll { !(it.id in bsToBind*.id) }
    List newBsToBind = bsToBind.findAll { !it.id }
    A.withTransaction { // Or move it to service
    removedBs.each { // Remove the B's not present in params
    a.removeFromB(it)
    it.delete()
    }
    bsToBind.each { bParams ->
    if (bParams.id) { // Just bind data for already existing B's
    B b = a.b.find { it.id == bParams.id }
    bindData(b, bParams, [exclude: 'id', 'domainClassName'])
    }
    else { // New B's are also added to a
    B newB = getBInstance(bParams.remove('domainClassName'), bParams)
    a.addToB(b)
    }
    }
    a.save(failOnError:true)
    }
    }
    }

    缺少用于调用 renderNewB的Javascript函数,用于删除现有 B的HTML片段以及用于处理索引的Java函数,但是我希望这个主意很清楚:)。

    更新

    我假设:
  • 依赖 session 获取关键信息不是很好: session 可能会失效(例如,用户注销),并且它们不便于扩展。
  • 为了不必在 View 中携带对象而保存对象是一个坏的,脆弱的想法-它很容易中断( session 无效,用户关闭浏览器,存在部署并且 session 不持久)并需要清理。可以做到,但我认为成本太高。

  • 我认为这需要一个更好的客户端,而不是依赖于服务器技巧。您描述的更改并没有太大的不同。
  • 将索引作为B的属性可以使思考比处理SortedSet / List容易:

    显示A时的
  • ,需要添加a.b.sort { it.index }来保留顺序。
  • 呈现B时,需要添加索引的隐藏输入。
  • 当发生拖放或删除操作时,需要一个Javascript函数来重新计算索引。
  • 绑定(bind)数据时,不会发生任何变化,因为索引只是一个属性。
  • 继承B确实需要将域类作为 View 中的隐藏输入(或使用一些Javascript来跟踪该信息,但我看不出这样做的好处)。我不明白为什么这很糟糕。您正在将继承用作“B类型”。如果您不是在继承中而是在B中拥有一个名为type的属性,则可以使用输入,对吗?
  • 呈现新的B时,需要传递“type”(domainClassName)
  • 呈现 B时的
  • ,如果没有id,则需要传递类名的隐藏输入
  • 在保存A时,将使用特定的域类创建新的B,否则不做任何更改。

  • 我已经更新了代码以反射(reflect)此更改。

    如果我真的要预先保存对象怎么办?

    如果您真的确信这是正确的方法,我仍将尝试避免该 session ,并在 B中添加一个名为 confirmed的新属性。
  • 用户添加新的B时,confirmed设置为false。
  • 当用户保存一个A时,所有尚未删除的所有B都将confirmed设置为true,删除的很好,删除:)。
  • 当显示A时,仅显示确认的B

  • 即使用户关闭浏览器或 session 无效,也不会向用户显示未确认的 B,并且在再次保存 A时最终将其删除。您还可以添加一个Quartz作业,该作业会根据某些超时定期清除未确认的 B,但这很棘手-保存未确认数据的整个想法是:-)。

    关于javascript - 处理grails中对象的未保存状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31677826/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com