草庐IT

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

coder 2024-07-16 原文

我们正在为我们的Web应用程序实现完全类似于gmail的标签功能-您可以选择帖子(复选框),然后从“标签”(它们本身是一组复选框)下拉列表中选择要应用/删除的标签)。问题是“如何去做?”我有一个解决方案,在解决该问题之前,我想先了解一下这是否正确,以及是否可以使用某些我可能不知道的jquery / javascript构造将其简化。无论如何,我都不是JavaScript / jQuery专业人士。 :)

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

输出:给定一组“选定的”帖子和一组“选定的”标签,将获得具有以下值的JSON项目数组:

  • Post_id,Label_id,操作{添加,删除}

  • 这是我想出的方法(天真或最优,我不知道):
  • 获取当前所选帖子的数量:var selectionCount = 5(例如,选择了5个帖子)
  • 为选择中的每个项目捕获以下数据集:

  • Label_id | numberOfLabelsInSelection | currentStateToShow | newState
    4 | 3 | partialTick |勾选(添加)
    10 | 5 |打勾无(删除)
    12 | 1 | partialTick | partialTick(忽略)
    14 | 0 |无|勾选(添加)

    基本上,上述数据结构只是捕获显示的条件,即,总共选择了5个帖子,并且只有两个帖子带有标签“x”,如果所有帖子都有一个“x”,那么标签列表应在复选框中显示“部分刻度”。标签“y”,则下拉列表显示“完整刻度”。未选中的标签上的标签只是未选中,只能切换到刻度线或“无”,而不能切换到部分状态(即,仅打开/关闭。partialTick具有三种状态,可以说:打开/关闭/部分)

    “newState”列基本上是已选择的列。输出 Action 基于先前的状态(即currentStateToShow):
  • 局部打勾意味着将标签添加到所有没有该标签的帖子
  • 勾选为无表示从所有帖子中删除该标签
  • 不完整表示不删除表示从所选帖子中仅删除标签
  • 不打勾表示将新标签添加到所有帖子
  • 部分到部分表示忽略,即无变化。

  • 然后,我可以遍历此集合并决定将以下数据发送到服务器:

    | Post_id | Label_id | Action |
    | 99 | 4 |添加|
    | 23 | 10 |删除|
    ...

    等等。

    那是什么问题呢?好吧,这很复杂!!! Javascript实际上并没有map数据结构(是吗?),它将需要进行过多的顺序迭代,并检查每件事,然后要进行很多if-else以确定newState的值。

    我不是在寻找“如何编写代码”,但该怎么做才能使我的生活更轻松?有没有可以使用的东西?逻辑是正确的还是太费解了?关于如何解决问题的建议,或者一些内置的数据结构(或外部库),这些建议可以使事情变得不那么粗糙?代码样本:P?

    我正在使用javascript / jquery + AJAX和reSTLet / java / mysql,并将为此发送JSON数据结构,但是我对此问题感到困惑。它看起来并不像我最初想象的那么容易(我是说我认为它比现在面对的要容易:)

    我最初想到将所有数据发送到服务器,然后在后端执行所有这些操作。但是在收到确认之后,我仍然需要以类似的方式更新前端,所以可以说“回到正题”,因为我必须在前端重复相同的事情来决定隐藏哪些标签并显示。因此,我认为最好只在客户端执行全部操作。

    我猜这是根据我的“专业知识”可以轻松执行的100-150多个javascript / jquery代码行,可以这么说,也许是关闭的...但这就是为什么我在这里:D

    PS:我看过这篇文章和How can I implement a gmail-style label chooser?演示,但是该演示一次仅适用于一篇文章,可以很容易地完成。由于这些部分选择等的选择集,我的问题更加严重,

    最佳答案

    算法

    我认为,该算法很有意义。

    虽然,是否需要大量if-eles来计算输出 Action ?为什么不只是在所有帖子中添加打勾的标签-当然,您无论如何也不能两次在同一帖子中添加一个标签。我怀疑这是否会损害性能……特别是如果您将所有更改后的帖子的JSON数据始终放入一个请求中(这取决于您的后端是否支持一次放置多个对象)。

    用MVC克服复杂性

    关于如何简化它:我认为,代码组织在这里很重要。

    您可以使用其中的一些功能:建议您检查使用JavaScript实现某种MVC方法的库(例如Backbone.js)。您最终将拥有一些类,并且您的逻辑将适合这些类中的小方法。您的数据存储逻辑将由“模型”类处理,而显示逻辑将由“ View ”处理。这更易于维护和测试。

    (如果您还没有查看这两个很棒的主题演讲,请查看Building large jQuery applicationsFunctionality focused code organization。)

    问题在于现有代码的重构可能要花费一些时间,而且很难从一开始就将其正确处理。另外,它还影响您的整个客户端体系结构,因此可能不是您想要的。



    如果我有类似的任务,我将使用Backbone.js并执行类似的操作(pseudocode / 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()
    

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

    (更新只是为了澄清:您可以在JavaScript中做完全一样的事情。您可以通过调用Backbone提供的对象的extend()方法来创建类。这样做更快。)

    您可能会说这比最初的解决方案还要复杂。我会争辩:这些类通常位于单独的文件中[1],并且当您在处理某个片段(例如DOM中的label表示)时,通常只处理其中之一(LabelView)。另外,请查看上面提到的演示。

    [1]关于代码组织,请参见下面的“分支”项目。

    上面的示例如何工作:
  • 用户选择了一些帖子:
  • 帖 subview 上的单击处理程序:
  • 切换帖子的选中状态。
  • 使所有LabelManager的所有标签更新状态。
  • 用户选择一个标签:
  • 标签 View 上的单击处理程序可切换标签的状态。
  • 用户单击“应用”:
  • apply_handler():对于每个更改的标签,对每个选定的帖子执行适当的操作。

  • Backbone.js

    更新以回应评论

    好吧,Backbone实际上只不过是几个基类和对象而已(请参阅annotated source)。

    但是我还是喜欢它。
  • 它为代码组织提供了周全的约定。

    它很像一个框架:您基本上可以采用它并专注于您的信息结构,表示形式和业务逻辑,而不是“我应该在哪里放置这个或那个东西,以免最终导致维护噩梦”。但是,它不是一个框架,这意味着您仍然有很大的自由去做自己想做的事情(包括射击自己的脚),而且还必须自己做出一些设计决策。
  • 它节省了大量样板代码。

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

  • 一些引用
  • Introduction to Backbone.js(非官方但很酷)(破碎)
  • The ToDos example

    尽管它已被文档引用,但不要将其视为“官方” Backbone.js示例。首先,它不使用稍后介绍的路由器。总的来说,我想说这是一个基于Backbone构建的小型应用程序的很好的例子,但是如果您正在做更复杂的事情(您正在做),那么最终结果可能会有所不同。
  • 在使用它时,请务必 checkout brunch。它基本上提供了一个项目模板,使用CoffeeScript,Backbone.js,Underscore.js,Stitch,Eco和Stylus。

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

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

  • 相关问题
  • What is the purpose of backbone.js?
  • https://stackoverflow.com/questions/5112899/knockout-js-vs-backbone-js-vs
  • 关于javascript - gmail标签选择器难题-有更好的方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6643089/

    有关javascript - gmail标签选择器难题-有更好的方法吗?的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    5. Ruby 方法() 方法 - 2

      我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

    6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    7. ruby - Highline 询问方法不会使用同一行 - 2

      设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

    8. ruby - 在院子里用@param 标签警告 - 2

      我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?

    9. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    10. ruby - 多个属性的 update_column 方法 - 2

      我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

    随机推荐