Загадка выбора ярлыка gmail - есть ли лучший способ сделать это? - PullRequest
9 голосов
/ 10 июля 2011

Мы находимся в процессе реализации функции маркировки точно так же, как gmail для нашего веб-приложения - вы можете выбрать сообщения (флажки) и выбрать, какие ярлыки применить / удалить из выпадающего списка «ярлыков» (которые сами по себе являются набор флажков). Проблема в том, "как это сделать?" У меня есть решение, и перед тем, как я займусь этим, я хочу узнать, верен ли он и можно ли его упростить, используя определенные конструкции jquery / javascript, о которых я мог не знать. Я пока не являюсь профессионалом JavaScript / jQuery. :)

Пусть: M = {Набор сообщений} N = {Набор меток} M_N = отношение многие ко многим между M и N, то есть набор сообщений, которые имеют хотя бы одну метку из N

Вывод: При заданном наборе «выбранных» постов и наборе «выбранных» меток получить массив элементов для JSON со следующими значениями:

  • Post_id, Label_id, действие {добавить, удалить}

Вот подход, который я предложил (наивный или оптимальный, я не знаю):

  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", затем в выпадающем меню отображается "полный тик". Метки не в выбранном наборе просто не выбраны, но могут переключаться только на отметку или «нет», но не в частичное состояние (т. Е. Только вкл / выкл. У частичного тика, так сказать, три состояния: вкл / выкл / частичный)

Столбец 'newState' в основном соответствует выбранному. Действие вывода зависит от того, каким было предыдущее состояние (то есть currentStateToShow):

  • Частично для галочки подразумевает добавление метки ко всем постам , у которых нет этой метки
  • не отмечен, означает удаление этого ярлыка из всех сообщений
  • частичное или нулевое подразумевает удаление только ярлыков из выбранных сообщений
  • нет отметок означает добавить новый ярлык для всех сообщений
  • частичное или частичное подразумевает игнорировать, т.е. без изменений .

Затем я могу перебрать этот набор и принять решение отправить следующие данные на сервер:

| 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? Но эта демонстрация предназначена только для одного сообщения за раз, и это легко сделать.Моя проблема усугубляется из-за выбора, заданного этими частичными выборками и т. Д.,

1 Ответ

5 голосов
/ 11 июля 2011

Алгоритм

Я думаю, алгоритм имеет смысл.

Хотя существует ли необходимость в большом количестве if-elses для вычисления выходного действия? Почему бы просто не добавить помеченный ярлык ко ВСЕМ сообщениям - наверняка вы не сможете добавить один ярлык к одному и тому же сообщению дважды. Я сомневаюсь, что это ухудшит производительность ... Особенно, если вы все равно поместите данные JSON для всех измененных постов в один запрос (это зависит от того, поддерживает ли ваш бэкэнд PUTTING нескольких объектов одновременно).

Удар сложность с MVC

Относительно того, как это можно сделать менее сложным: я думаю, здесь важна организация кода.

Есть кое-что, что вы можете использовать: я предлагаю вам проверить библиотеки, которые реализуют какой-то MVC-подход в JavaScript (например, Backbone.js ). В итоге у вас будет несколько классов, и ваша логика будет соответствовать маленьким методам этих классов. Ваша логика хранения данных будет обрабатываться "модельными" классами, а логика отображения - "представлениями". Это более легко обслуживаемо и проверяемо.

(Пожалуйста, проверьте эти две замечательные презентации по теме, если вы еще этого не сделали: Создание больших приложений jQuery , Организация кода, ориентированного на функциональность .)

Проблема в том, что рефакторинг существующего кода может занять некоторое время, и с первого раза сложно сделать это правильно. Кроме того, это как бы влияет на всю вашу клиентскую архитектуру, так что, возможно, это не то, что вы хотели.

* * Пример 1 022

Если бы у меня была похожая задача, я бы взял 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()

Упс, похоже, слишком много кода. Если пример неясен, не стесняйтесь задавать вопросы.

( Обновление просто для уточнения: вы можете сделать точно то же самое в JavaScript. Вы создаете классы, вызывая extend() методы объектов, предоставленных Backbone. Это было быстрее введите это так.)

Вы, вероятно, сказали бы, что это даже более сложно, чем первоначальное решение. Я бы сказал, что эти классы обычно лежат в отдельных файлах [1], и когда вы работаете над какой-то частью (скажем, над представлением метки в DOM), вы обычно имеете дело только с одним из них (LabelView). Также ознакомьтесь с презентациями, упомянутыми выше.

[1] Об организации кода см. Ниже о проекте "позднего завтрака".

Как будет работать приведенный выше пример:

  1. Пользователь выбирает несколько сообщений:

    • Обработчик кликов при просмотре поста:
      1. переключает статус сообщения.
      2. устанавливает все состояния обновления LabelManager для всех меток.
  2. Пользователь выбирает метку:

    • Обработчик клика в представлении метки переключает статус метки.
  3. Пользователь нажимает «Применить»:

    • apply_handler(): для каждого из измененных ярлыков введите соответствующее действие для каждого выбранного сообщения.

Backbone.js

Обновление в ответ на комментарий

Ну, Backbone на самом деле не намного больше, чем пара базовых классов и объектов (см. аннотированный источник ).

Но мне все же нравится.

  • Предлагает продуманные соглашения для организации кода.

    Это очень похоже на фреймворк: вы можете просто взять его и сконцентрироваться на своей информационной структуре, представлении и бизнес-логике, а не на том, «где я могу поместить то или иное, чтобы у меня не было кошмара обслуживания» ». Тем не менее, это не фреймворк , что означает, что у вас все еще есть большая свобода делать то, что вы хотите (в том числе стрелять себе в ногу), но вы также должны сами принимать некоторые дизайнерские решения.

  • Экономит большое количество стандартного кода.

    Например, если у вас есть RESTful API, предоставляемый серверной частью, вы можете просто сопоставить его с моделями Backbone, и он выполнит всю работу по синхронизации за вас: например, если вы сохраняете новый экземпляр Model -> он отправляет запрос POST на URL Collection, если вы обновляете существующий объект -> он отправляет запрос PUT на URL этого конкретного объекта. (Полезная нагрузка запроса - это JSON атрибутов модели, которые вы установили с помощью метода set().) Таким образом, все, что вам нужно сделать, это в основном настроить URL-адреса и вызвать метод 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 в один файл. Он также запускает сервер mini Express.js на localhost:8080, который немедленно отражает изменения.

Смежные вопросы
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...