Элегантный способ предотвратить циклические события в MVC?

Вкратце вопрос:

В MVC, как отличить щелчок флажка (или изменение поля выбора или списка) от человеческого значения «Контроллер, изменить модель» и щелчка флажка (или изменения поля выбора или списка) от Контроллера, означающего «Я» m обновляет вид, потому что модель изменилась »?


Пример:

У меня есть приложение JS (все одна большая страница HTML + JS; за ним стоит сервер и работает AJAX, но это не важно для примера), в котором есть понятие «вершины», соединенные «краями». Пользовательский интерфейс позволяет добавлять и удалять вершины на карте, а также включать или отключать ребра между парами вершин.

Есть два способа отключить ребро от вершины A до вершины B:

  1. щелкните край, чтобы в окне «Детали края» появилась кнопка «Отключить этот край»; или же
  2. нажмите на вершину A (или B), чтобы в окне «Vertex Details» был представлен контрольный список ближайших вершин, в котором вы можете снять отметку с вершины B (или A).

Вот как это работает в MVC (но см. конец этого сообщения для обновления, где я исправляю проблемы в моем понимании):

  • Модель: список объектов Vertex и список объектов Edge.
  • Представление: пользовательский интерфейс GMaps с маркерами и полилиниями, а также флажками и кнопками, а также DIV «Сведения о вершине» и «Сведения о ребре».
  • Контроллер:
    • JS-функции, обновляющие модель при срабатывании событий на флажках и кнопках; а также
    • JS-функции, обновляющие представление при срабатывании событий в моделях.

Вот конкретная неэлегантность:

  1. Пользователь имеет окно сведений о вершине, сфокусированное на вершине A, и окно сведений о ребре, сфокусированное на ребре от вершины A до вершины B.
  2. Пользователь нажимает «Отключить этот край» в окне «Сведения о грани».
  3. Функция контроллера 1 получает событие щелчка и вызывает disable () для объекта модели Edge.
  4. Объект модели Edge запускает событие «Я только что отключился».
  5. Функция контроллера 2 получает событие «Я только что отключился», и
    1. перерисовывает окно деталей края, чтобы сказать: «Я отключен!» а также
    2. снимает отметку с вершины B в окне сведений о вершине.
      1. Дерьмо! Это снова запускает функцию контроллера 1, которая прослушивала события пользовательского интерфейса, которые означают, что граница была отключена!

Таким образом, происходит ненужное повторное обновление модели и повторное обновление представления. В более сложном представлении с событиями, которые запускают события, которые запускают события, это может привести к десяткам посторонних обновлений!


Обновление: отличный ответ.

Я немного неправильно понял MVC. У меня нет только одного представления, как я описал выше: у меня есть несколько представлений в нескольких моделях. В частности, у меня есть список с флажками View of Edges для конкретного узла и отдельный, «подробный оконный» вид Edge.

Более того, у меня не должно быть одной функции контроллера, обновляющей все представления при изменении модели: каждое представление должно изменять сам при изменении модели.

Итак, если каждое представление регистрируется для событий «обновленного состояния» в модели, и каждое представление обновляется при получении этих событий, то ответ на мой вопрос о циклических событиях будет просто следующим:

Просмотр списка флажков отключит события флажков на тот момент, когда они обновляют флажки после изменения состояния модели.

Теперь, если пользователь отключает Edge через окно Edge Detail, контроллер обновляет Edge Model, представление списка флажков получает уведомление об обновлении, а представление списка флажков достаточно умен, чтобы отключить события флажков при изменении статуса соответствующий флажок.

Это намного приятнее, чем мое первоначальное решение, в котором один контроллер обновляет ВСЕ представления - и, следовательно, должен знать, какие представления требуют особого внимания и кормления, чтобы избежать циклов. Вместо этого с проблемой может справиться только одно представление с проблемными элементами пользовательского интерфейса.

Спасибо тем, кто ответил на мой вопрос!

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
7
0
1 405
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это сложный вопрос. Если я правильно понимаю, проблема возникает из-за того, что вы предоставили обработчик кликов в своей модели, а событие click модели перехватывается контроллером. Контроллер обновляет представление, которое, в свою очередь, переключает то же событие.

С моей точки зрения, я считаю неуместным для контроллера присоединяться к событию Edge Click, потому что оно раскрывает слишком много деталей о том, как Edge реализован и используется. Контроллер не заботится о том, как используется Edge, или о каких-либо других деталях реализации.

Фактически, канонический стиль MVC вообще не требует, чтобы Контроллер подключался к событиям модели любой, как правило, потому, что состояние модели не изменяется представлением или любыми другими контроллерами. Модель не обязана уведомлять Контроллер о том, что она была изменена.

Чтобы решить вашу проблему, вы должны определить интерфейс View, чтобы иметь один метод, например ToggleEdge:


public interface GraphView
{
    event Action ToggleEdge;
}

Заманчиво создать два метода, EdgeClicked и CheckboxClicked, но настаивание на двух таких независимых методах нарушает принцип инкапсуляции. Он предоставляет слишком много деталей реализации вашему контроллеру или кому-либо еще, кто хочет подключиться к этим событиям. Помните, что Контроллер заботится только об изменении состояния представления, его не волнует, что как изменилось.

Когда вы реализуете интерфейс View в своем пользовательском интерфейсе, вы должны позаботиться о том, чтобы событие ToggleEdge было вызвано из местоположения один. Вы можете сделать это, подключившись к событию Edge.Clicked в вашем представлении и используя его для переключения флажка; это делает ваш флажок ответственным за поднятие клапана Toggle до контроллера:


public class UI : UserControl, GraphView
{
    public event Action ToggleEdge;

    void OnToggleEdge(Edge edge)
    {
        if (ToggleEdge != null)
            ToggleEdge(edge);
    }

    protected void Edge_Clicked(object sender, EventArgs e)
    {
        CheckBox chkbox = FindCheckBoxThatCorrespondsToEdge((Edge)sender);
        chkbox.Checked = !chkbox.Checked;    
    }

    protected void chkEdge_CheckChanged(object sender, EventArgs e)
    {
        Edge edge = FindEdgeThatCorrespondsToCheckbox((CheckBox)sender);
        OnToggleEdge(edge);
    }
}

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

Принцесса, спасибо за ответ. Если не понятно, мое приложение написано на JS; Я отредактировал свой вопрос, чтобы сделать его более очевидным. Если я вас правильно понял, View отвечает за обновление сам при изменении состояния. Разве контроллер не должен обновлять представления при изменении состояния?

Michael Gundlach 17.12.2008 23:24

Например, ваше решение не сработало бы, если бы в модели было три отдельных представления. Когда один изменился, он не должен отвечать за обновление других; Контроллер должен заметить изменение, а затем обновить другие представления. И когда это происходит, он снова случайно запускает их события изменения.

Michael Gundlach 17.12.2008 23:25
Ответ принят как подходящий

Просто чтобы повторить модель MVC. Представления обычно должны обновляться. Вот как это работает: контроллер изменяет состояние модели, модель отправляет обновления своим представлениям, представления извлекают новое состояние из модели и обновляются сами. Хотя контроллеры и представления, как правило, объединены (т.е. детализируют данные в графическом представлении), они никогда не должны взаимодействовать напрямую, только через модель. Это в общем конечно.

Таким образом, JS-функции, обновляющие ваши представления, на самом деле не являются контроллерами, что является важным отличием. Их следует рассматривать как часть вашей точки зрения. Возможно, это не поможет решить проблему, но я подумал, что на это стоит обратить внимание.

Вы также не можете удалить свою модель, я полагаю, вы имеете в виду, что вы удаляете что-то из из своей модели, поскольку никакие представления или контроллеры не могут существовать (или находиться в функциональном состоянии), если они не поддерживаются моделью.

Не будучи жокеем кода JS и не использовав gmaps, я действительно не понимаю, в чем проблема. Запускает ли изменение состояния флажка (свойство checked) событие onClick ()? Это действительно не должно ИМХО, но, возможно, они реализовали это таким образом, иначе вы могли бы просто прикрепить свой контроллер к onClick () и добавить некоторую логику к флажку (или, если это JS, где-то в функции), чтобы изменить состояние флажка . Если это невозможно, лучше всего подойдут варианты 1 и 2.

дополнение: пользователь взаимодействует с представлением

Так что же происходит, когда пользователь хочет взаимодействовать с представлением? Часто виджет включает в себя и представление, и контроллер. У флажка есть представление (вы можете увидеть, отмечен он или нет), а также контроллер (вы можете щелкнуть по нему). Когда вы устанавливаете флажок, в принципе должно происходить следующее:

  • Контроллер флажка получает событие
  • Контроллер флажка изменяет состояние для значения, которое этот флажок представляет в модели
  • слушатели обновлений модели (включая флажок)
  • флажок обновляет свой вид, чтобы отразить, что это значение изменилось

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

место на. я отредактирую свой вопрос, чтобы отразить ваш ответ. Вы правы, что я не могу удалить свою модель - запуталась.

Michael Gundlach 18.12.2008 18:25

Не могли бы вы отредактировать свой ответ, чтобы включить пользователя, взаимодействующего с представлением, и способ уведомления контроллера? Моя проблема частично заключалась в том, что один и тот же элемент управления в представлении использовался как для ввода пользователем, так и для обратной связи с пользователем.

Michael Gundlach 18.12.2008 18:26

Добавил текст, не уверен, что это то, что вы искали.

wds 23.12.2008 13:56

Другие вопросы по теме