关于javascript:gmail标签选择器难题 – 还有更好的方法吗?

The gmail label chooser conundrum - is there a better way to do it?

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

让:
M = {帖子集}
N = {标签集}
M_N = M和N之间的多对多关系,即具有来自N的至少一个标签的一组帖子

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

  • Post_id,Label_id,action {add,delete}

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

  • 获取当前选定帖子的数量:var selectionCount = 5(例如,选择5个帖子)
  • 为选择中的每个项捕获以下数据集:
  • 1
    2
    3
    4
    5
     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"然后下拉显示"完整勾选"。不在所选集合上的标签只是未被选中但只能切换到刻度线或"无"但不能切换到部分状态(即仅打开/关闭.sitialTick有三种状态可以这么说:开/关/部分)

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

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

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

    1
    2
    3
    4
    | 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风格的标签选择器?但该演示一次仅适用于一个帖子,并且可以轻松完成。由于使用这些部分选择等进行选择,我的问题更加严重,


    算法

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

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

    利用MVC打败复杂性

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

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    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()

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

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

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

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

    以上示例如何工作:

  • 用户选择一些帖子:

  • 单击帖子视图上的处理程序

  • 切换帖子的已检查状态。
  • 使所有标签的所有LabelManager更新状态。
  • 用户选择标签:

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

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

    更新以回应评论

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

    但我还是喜欢它。

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

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

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

    例如,如果您有后端提供的RESTful API,那么您可以将其映射到Backbone模型,它将为您执行所有同步工作:例如如果你保存一个新的Model实例 - >它会向Collection url发出一个POST请求,如果你更新现有的对象 - >它会向这个特定对象的url发出一个PUT请求。 (请求有效负载是您使用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成一个文件。它还在localhost:8080上运行迷你Express.js服务器,它立即反映了变化。

  • 相关问题

  • backbone.js的目的是什么?
  • https://stackoverflow.com/questions/5112899/knockout-js-vs-backbone-js-vs
  • 好。