gmail标签选择器难题 - 有更好的方法吗?

PhD*_*PhD 9 javascript algorithm jquery gmail

我们正在为我们的webapp实现与gmail完全相同的标签功能 - 您可以选择帖子(复选框)并从'标签'下拉列表中选择要应用/删除的标签(它们本身是一组复选框) ).问题是"如何去做?" 我有一个解决方案,在我解决它之前,我希望得到一个关于它是否是正确方法的意见,以及是否可以使用我可能不知道的某些jquery/javascript结构进行简化.无论如何,我不是JavaScript/jQuery pro.:)

设:M = {帖子集} N = {标签集} M_N = M和N之间的多对多关系,即从N开始至少有一个标签的帖子集

输出:给定一组"选定"帖子,并且"选定"标签集获取具有以下值的JSON项目数组:

  • Post_id,Label_id,action {add,delete}

这是我提出的方法(天真或最佳,我不知道):

  1. 获取当前选定帖子的数量:var selectionCount = 5(例如,选择5个帖子)
  2. 为选择中的每个项捕获以下数据集:
 Label_id | numberOfLabelsInSelection| currentStateToShow |   newState
      4   |            3             |    partialTick     |  ticked (add)
      10  |            5             |      ticked        |  none (delete)
      12  |            1             |    partialTick     |  partialTick (ignore)
      14  |            0             |       none         |  ticked (add)

基本上上面的数据结构只是捕获显示条件,即总共选择了5个帖子,只有两个标签有"x"表示,那么标签列表应该在复选框中显示"部分刻度",如果所有帖子都有标签"y"然后下拉显示"完整勾选".不在所选集合上的标签只是未选中但只能切换到刻度线或"无"但不能切换到部分状态(即仅打开/关闭.deittyTick有三种状态可以这么说:开/关/部分)

'newState'列基本上是已选择的内容.输出操作基于先前的状态(即currentStateToShow):

  • partial to tick意味着为没有该标签的所有帖子添加标签
  • 勾选为无意味着从所有帖子中删除该标签
  • partial to none意味着删除所选帖子中的标签
  • 无勾选意味着为所有帖子添加新标签
  • 偏偏意味着忽略,即没有变化.

然后我可以迭代这个集合并决定将以下数据发送到服务器:

| Post_id | Label_id | Action |
|   99    |     4    |   add  |
|   23    |    10    | delete |
 ...

等等.

那么问题是什么?这是非常复杂的!Javascript实际上没有地图数据结构(是吗?),它需要过多的连续迭代并检查每一件事,然后有很多if-else来确定newState的值.

我不是在寻找"如何编码"但我能做些什么来让我的生活更轻松?有没有我可以使用的东西?逻辑是正确还是有点过于复杂?有关如何攻击问题或某些内置数据结构(或外部库)可能会使事情变得不那么粗糙的任何建议?代码示例:P?

我正在使用javascript/jquery + AJAX和restlet/java/mysql,并将为此发送一个JSON数据结构,但我很满意这个问题.它看起来并不像我最初想象的那么容易(我的意思是我认为它比我现在面对的更容易:)

我最初想过将所有数据发送到服务器并在后端执行所有这些操作.但收到确认后,我仍然需要以类似的方式更新前端,所以我可以"回到原点",因为我必须在前端重复相同的事情来决定隐藏哪些标签以及要显示的内容.因此,我认为在客户端做整件事情会更好.

根据我的"专业知识",我猜这是一个简单的100-150 + javascript/jquery代码行,可以说,也许关闭......但这就是为什么我在这里:D

PS:我看过这篇文章和演示如何实现gmail风格的标签选择器?但该演示一次仅适用于一个帖子,并且可以轻松完成.由于使用这些部分选择等选择集,我的问题更加严重,

Ant*_*off 5

算法

我认为,算法是有道理的.

虽然,是否需要大量的if-elses来计算输出动作?为什么不在所有帖子中添加勾选标签 - 当然,无论如何你都不能在同一个帖子上添加一个标签.我怀疑它会损害性能......特别是如果你将所有更改的帖子的JSON数据都适合一个请求(这取决于你的后端是否支持一次PUTting多个对象).

利用MVC打败复杂性

关于它如何变得不那么复杂:我认为,代码组织在这里是一个大问题.

您可以使用以下内容:我建议您检查在JavaScript中实现某种MVC方法的库(例如,Backbone.js).你最终会得到一些类,你的逻辑将适合这些类的小方法.您的数据存储逻辑将由"模型"类处理,并通过"视图"显示逻辑.这更易于维护和测试.

(请查看这两个关于主题的精彩演示文稿,如果您还没有:构建大型jQuery应用程序,功能集中的代码组织.)

问题是现有代码的重构可能需要一些时间,而且从第一次开始就难以实现.此外,它有点影响您的整个客户端架构,所以可能不是您想要的.

如果我有类似的任务,我会采取Backbone.js并做类似的事情(伪代码/ CoffeeScript; 这个例子既不好也不完整,目的是给出一般基于类的方法的基本概念):

apply_handler: ->
    # When user clicks Apply button
    selectedPosts = PostManager.get_selected()
    changedLabels = LabelManager.get_changed()
    for label in changedLabels
        for post in selectedPosts
            # Send your data to the server:
            # | post.id | label.id | label.get_action() |
            # Or use functionality provided by Backbone for that. It can handle
            # AJAX requests, if your server-side is RESTful.


class PostModel
    # Post data: title, body, etc.

    labels: <list of labels that this post already contains>
    checked: <true | false>
    view: <PostView instance>

class PostView
    model: <PostModel instance>
    el: <corresponding li element>

    handle_checkbox_click: ->
        # Get new status from checkbox value.
        this.model.checked = $(el).find('.checkbox').val()
        # Update labels representation.
        LabelManager.update_all_initial_states()

class PostManager
    # All post instances:
    posts: <list>

    # Filter posts, returning list containing only checked ones:
    get_selected: -> this.posts.filter (post) -> post.get('checked') == true


class LabelModel
    # Label data: name, color, etc.

    initialState: <ticked | partialTick | none>
    newState: <ticked | partialTick | none>
    view: <LabelView instance>

    # Compute output action:
    get_action: ->
        new = this.newState
        if new == none then 'DELETE'
        if new == partialTick then 'NO_CHANGE'
        if new == ticked then 'ADD'

class LabelView
    model: <LabelModel instance>
    el: <corresponding li element>

    # Get new status from checkbox value.
    handle_checkbox_click: ->
        # (Your custom implementation depends on what solution are you using for 
        # 3-state checkboxes.)
        this.model.newState = $(this.el).find('.checkbox').val()

    # This method updates checked status depending on how many selected posts
    # are tagged with this label.
    update_initial_state: ->
        label = this.model
        checkbox = $(this.el).find('.checkbox')
        selectedPosts = PostManager.get_selected()
        postCount = selectedPosts.length

        # How many selected posts are tagged with this label:
        labelCount = 0
        for post in selectedPosts
            if label in post.labels
                labelCount += 1

        # Update checkbox value
        if labelCount == 0
            # No posts are tagged with this label
            checkbox.val('none')
        if labelCount == postCount
            # All posts are tagged with this label
            checkbox.val('ticked')
        else
            # Some posts are tagged with this label
            checkbox.val('partialTick')

        # Update object status from checkbox value
        this.initialState = checkbox.val()

class LabelManager
    # All labels:
    labels: <list>

    # Get labels with changed state:
    get_changed: ->
        this.labels.filter (label) ->
            label.get('initialState') != label.get('newState')

    # Self-explanatory, I guess:
    update_all_initial_states: ->
        for label in this.labels
            label.view.update_initial_state()
Run Code Online (Sandbox Code Playgroud)

糟糕,似乎代码太多了.如果示例不清楚,请随时提问.

(更新只是为了澄清:您可以在JavaScript中完全相同.您可以通过调用extend()Backbone提供的对象的方法来创建类.以这种方式键入它会更快.)

您可能会说这比初始解决方案更复杂.我认为:这些类通常位于单独的文件[1]中,当你处理某个部分(比如DOM中的标签表示)时,通常只处理其中一个(LabelView).另外,请查看上面提到的演示文稿.

[1]关于代码组织,请参阅下面的"早午餐"项目.

以上示例如何工作:

  1. 用户选择一些帖子:

    • 单击帖子视图上的处理程序
      1. 切换帖子的已检查状态.
      2. 使所有标签的所有LabelManager更新状态.
  2. 用户选择标签:

    • 单击标签视图上的处理程序可切换标签的状态.
  3. 用户点击"应用":

    • apply_handler():对于每个更改的标签,为每个选定的帖子发出适当的操作.

Backbone.js的

更新以回应评论

好吧,Backbone实际上并不比一些基类和对象多得多(参见带注释的源代码).

但我还是喜欢它.

  • 它为代码组织提供了经过深思熟虑的约定.

    这很像一个框架:你基本上可以把它集中在你的信息结构,表示和业务逻辑上,而不是"我在哪里放置这个或那个以便我不会最终得到维护噩梦".然而,它不是一个框架,这意味着你仍然有很多自由去做你想做的事情(包括用脚射击自己),但也必须自己做出一些设计决定.

  • 它节省了大量的样板代码.

    例如,如果您有后端提供的RESTful API,那么您可以将其映射到Backbone模型,它将为您执行所有同步工作:例如,如果您保存新Model实例 - >它会发出POST请求该Collection网址,如果您更新现有的对象- >它发出一个PUT请求此特定对象的URL.(请求有效负载是您使用set()方法设置的模型属性的JSON .)因此save(),fetch()当您需要保存时,您需要做的就是在模型上设置网址和调用方法,当您需要获取它时从服务器状态.它使用jQuery.ajax()幕后执行实际的AJAX请求.

一些参考

  • Backbone.js简介(非官方但酷)(破)

  • ToDos的例子

    不要将它作为"官方"Backbone.js示例,尽管它是由文档引用的.首先,它不使用稍后介绍的路由器.一般来说,我会说这是一个很好的建立在Backbone上的小应用程序的例子,但是如果你正在处理更复杂的事情(你做的事情),你可能最终会遇到一些不同的东西.

  • 在你的时候,一定要看看早午餐.它基本上提供了一个项目模板,使用CoffeeScript,Backbone.js,Underscore.js,Stitch,Eco和Stylus.

    由于严格的项目结构和使用require(),它实施了比Backbone.js单独执行更高级别的代码组织约定.(你基本上不需要考虑在什么类中放置代码,而且还需要考虑放置该类的文件以及将该文件放在文件系统中的位置.)但是,如果你不是"常规"类型的人,那么你可能会讨厌它.我喜欢.

    最棒的是它还提供了一种轻松构建所有这些东西的方法.您只需运行brunch watch,开始处理代码,每次保存更改时,它都会编译并构建整个项目(只需不到一秒)到一个build目录中,将所有生成的javascript连接(甚至可能最小化)到一个文件中.它还运行迷你Express.js服务器localhost:8080,立即反映更改.

相关问题