Лучший способ игнорировать ваши собственные изменения с помощью Aurelia Property Observer - PullRequest
0 голосов
/ 11 мая 2018

Я надеюсь, что кто-то может предложить какое-то руководство.

Речь идет об оптимальном использовании Наблюдателей Аурелии.

Этот пример является вымышленным примером, иллюстрирующим воспринятую проблему.

Допустим, яимеют три свойства числа: часы, минуты, секунды, также известные как 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 и подписки на эти уведомления об изменениях.Легко понять, что это за бит.

Мой вопрос, в конечном счете, таков:

Когда изменяется H, это вызовет пересчет T, поэтому я напишу новое значение в T.

Изменение T вызовет уведомление об изменении для T, чей обработчик затем разложит значение T и запишет новые значения обратно в H, M, S.

Запись в H, M, S вызовет другое изменениеуведомление о создании нового T и т. д.

Кажется само собой разумеющимся, что, по крайней мере, ЖЕЛАТЕЛЬНО, когда мой объект-конвертер записывает свойства H, M, S, Tтогда он должен каким-то образом подготовиться к тому, чтобы игнорировать уведомление об изменениях, которое, как он ожидает, будет получено в результате.

Однако [легкость] сказать это и сделать это - две разные вещи.

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

Для получения уведомления об изменении должно быть реальное изменение значения, поэтому вам необходимо заранее знать, ожидаете ли вы его получить.Гораздо более сложная проблема заключается в том, что уведомления об изменениях выдаются через «синтетическую» очередь микро-задач Aurelia, поэтому вы очень не знаете, когда получите этот вызов микро-задачи.

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

1 Ответ

0 голосов
/ 11 мая 2018

Простейшим способом реализации 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()), то внутренняя логика этого решения также была бы аналогичной. Эти флаги вам всегда будут нужны, а также для отсрочки их сброса с помощью микро-задачи.

Единственный способ избежать использования микро-задачи - это изменить некоторые внутренние компоненты фреймворка так, чтобы можно было передать контекст, который может сообщить диспетчеру уведомлений об изменениях: «это изменение произошло из обработчика изменений, поэтому снова вызвать этот обработчик изменений "

...