Лучший способ игнорировать ваши собственные изменения с помощью Aurelia Property Observer
Я надеюсь, что кто-то может предложить некоторые рекомендации.
Речь идет об использовании наблюдателей Aurelia в лучшем виде.
Пример представляет собой выдуманный пример, иллюстрирующий воспринимаемую проблему.
Допустим, у меня есть три свойства числа: часы, минуты, секунды, также известные как H, M, S, и я хочу построить объект, который контролирует H, M, S, и каждый раз, когда любое из этих трех изменений, он создает свойство T, которое является строкой это выглядит как продолжительность, например. "чч: мм: сс" с использованием свойств H, M, S с соответствующим заполнением нулями и т. д.
И наоборот, свойство T связано с пользовательским интерфейсом, и поэтому, если пользователь изменяет T (например, через поле ввода), мне нужно разложить части T на их компоненты H, M, S и отодвинуть отдельные значения обратно. в свойствах H, M, S.
Никто не должен забывать, что это поведение удивительно похоже на поведение конвертера значений Aurelia с точки зрения поведения "toView" и "fromView", за исключением того, что мы имеем дело с 3 свойствами с одной стороны и 1 свойством с другой стороны,
Что касается реализации, я могу использовать BindingEngine или ObserverLocator для создания наблюдателей на свойствах H, M, S и T и подписки на эти уведомления об изменениях. Легко peasy для этого бита.
Мой вопрос в конечном итоге это:
Когда H изменится, это вызовет пересчет T, поэтому я напишу новое значение в T.
Изменение T вызовет уведомление об изменении для T, чей обработчик затем разложит значение T и запишет новые значения обратно в H, M, S.
Обратная запись в H, M, S запускает другое уведомление об изменении для создания нового T и так далее.
Кажется самоочевидным, что, по крайней мере, ЖЕЛАТЕЛЬНО, что когда мой объект-конвертер записывает свойства H, M, S, T, он должен каким-то образом подготовиться к игнорированию уведомления об изменениях, которое, как он ожидает, будет быть предстоящим, как следствие.
Однако [легкость] сказать это и сделать это две разные вещи.
Мой вопрос - действительно ли это вообще необходимо? Если это желательно, как мне сделать это как можно проще. Препятствия для этого заключаются в следующем:
Чтобы создать уведомление об изменении, должно быть реальное изменение значения, поэтому вам нужно заранее знать, ожидаете ли вы его получить. Гораздо более сложная проблема заключается в том, что уведомления об изменениях отправляются через "синтетическую" очередь микро-задач Aurelia, поэтому вы очень не знаете, когда получите этот вызов микро-задачи.
Итак, есть ли хорошее решение этой проблемы? Кто-нибудь на самом деле беспокоится об этом, или они просто полагаются на пункт 1, дающий самоограничивающий результат? То есть может быть несколько циклов уведомлений об изменениях, но процесс в конечном итоге согласится?
1 ответ
Самым простым способом реализации N-way привязки (в данном случае 4-way) является использование обработчиков изменений в сочетании с флагами "ignore" для предотвращения бесконечной рекурсии.
Приведенная ниже концепция очень похожа на то, как некоторые из этих проблем решаются внутри системы Aurelia - она надежна, эффективна и примерно настолько же "естественна", насколько это возможно.
@autoinject()
export class MyViewModel {
@bindable({ changeHandler: "hmsChanged" })
public h: string;
@bindable({ changeHandler: "hmsChanged" })
public m: string;
@bindable({ changeHandler: "hmsChanged" })
public s: string;
@bindable()
public time: string;
private ignoreHMSChanged: boolean;
private ignoreTimeChanged: boolean;
constructor(private tq: TaskQueue) { }
public hmsChanged(): void {
if (this.ignoreHMSChanged) return;
this.ignoreTimeChanged = true;
this.time = `${this.h}:${this.m}:${this.s}`;
this.tq.queueMicroTask(() => this.ignoreTimeChanged = false);
}
public timeChanged(): void {
if (this.ignoreTimeChanged) return;
this.ignoreHMSChanged = true;
const hmsParts = this.time.split(":");
this.h = hmsParts[0];
this.m = hmsParts[1];
this.s = hmsParts[2];
this.tq.queueMicroTask(() => this.ignoreHMSChanged = false);
}
}
Если вам нужно универсальное решение, которое вы можете повторно использовать в нескольких ViewModel, где у вас есть разные виды N-way связываний, вы можете сделать это с BindingBehavior (и, возможно, даже с комбинацией 2 ValueConverters).
Но логика, которая должна быть реализована в них, была бы концептуально похожа на мой пример выше.
На самом деле, если в рамках уже было готовое решение этой проблемы (скажем, например, настройка @bindable()
декоратор), то внутренняя логика этого решения также будет аналогичной. Вы всегда будете нуждаться в этих флагах так же как задержать их сброс через микро задачу.
Единственный способ избежать использования микро-задачи - это изменить некоторые внутренние компоненты инфраструктуры таким образом, чтобы можно было передать контекст, который может сообщить диспетчеру уведомлений об изменениях: "это изменение произошло из обработчика изменений, поэтому не инициируйте это изменение. снова обработчик