jQuery更改事件和Aurelia的技术 [英] Technique for jquery change events and aurelia

查看:61
本文介绍了jQuery更改事件和Aurelia的技术的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要找到一个可靠的解决方案,以使两个框架都能很好地发挥作用.

使用materialize-css,它们的select元素使用jquery来应用值更改.但是,那并不会触发aurelia看到这种变化.使用...的技术$(选择").change((eventObject:JQueryEventObject)=> {fireEvent(eventObject.target,"change");});我可以触发aurelia看到的事件,但是,aurelia然后导致事件在更新绑定时再次被触发,并且我陷入无限循环....

在这方面,让两个人一起玩耍的最可靠方法是什么?

解决方案

我已经使用了materialize-css + aurelia一段时间,并且可以确认实现中的select元素存在很大问题.

我只是想在这里分享我的解决方案之一,以防有人想要其他示例.在这种情况下,Ashley的清洁剂可能更清洁.我的选项而不是插槽使用可绑定对象.

除此之外,基本思想是相同的(使用保护变量和微任务).

我从处理第三方插件和双向数据绑定中学到的一个教训是,它有助于在处理源自绑定目标(DOM上的select元素)的更改与更改之间进行更清晰,明显的区分.源自绑定源(例如,包含元素的页面的ViewModel).

我倾向于使用名称为 onValueChangedByBindingSource onValueChangedByBindingTarget 之类的更改处理程序,以不同的方式处理将ViewModel与DOM同步的方式,从而减少混乱代码.

示例: https://gist.run?id=6ee17e333cd89dc17ac62355a4b31ea9

src/material-select.html

 < template>< div class ="input-field"><选择value.two-way ="value" id ="material-select">< option repeat.for =选项的选项" model.bind =选项">$ {option.displayName}</option></select></div></template> 

src/material-select.ts

  import {customElement,可绑定的bindingMode,TaskQueue,一次性的,BindingEngine,注入,DOM}来自"aurelia-framework";@customElement("material-select")@inject(DOM.Element,TaskQueue,BindingEngine)出口类别MaterialSelect {公共元素:HTMLElement;public selectElement:HTMLSelectElement;@bindable({defaultBindingMode:bindingMode.twoWay})公用值:{名称:字符串,值:数字};@bindable({defaultBindingMode:bindingMode.oneWay})公用选项:{displayName:string} [];构造函数(元素:元素,私人tq:TaskQueue,私人bindingEngine:BindingEngine){this.element =元素;}私人订阅:一次性;public isAttached:boolean = false;公共attach():void {this.selectElement =< HTMLSelectElement> this.element.querySelector("select");this.isAttached = true;$(this.selectElement).material_select();$(this.selectElement).on("change",this.handleChangeFromNativeSelect);this.subscription = this.bindingEngine.collectionObserver(this.options).subscribe(()=> {$(this.selectElement).material_select();});}public detached():void {this.isAttached = false;$(this.selectElement).off("change",this.handleChangeFromNativeSelect);$(this.selectElement).material_select("destroy");this.subscription.dispose();}私人valueChanged(newValue,oldValue):void {this.tq.queueMicroTask(()=> {this.handleChangeFromViewModel(newValue);});}private _suspendUpdate = false;私人handleChangeFromNativeSelect =()=>{如果(!this._suspendUpdate){this._suspendUpdate = true;let event = new CustomEvent("change",{气泡:正确});this.selectElement.dispatchEvent(事件)this._suspendUpdate = false;}}私人handleChangeFromViewModel =(newValue)=>{如果(!this._suspendUpdate){$(this.selectElement).material_select();}}} 

编辑

自定义属性如何?

要点: https://gist.run?id=b895966489502cc4927570c0beed3123

src/app.html

 < template>< div class ="container">< div class ="row"></div>< div class ="row">< div class ="col s12">< div class ="input-element" style ="position:relative;"><选择md-select value.two-way ="currentOption">< option repeat.for =选项的选项" model.bind =选项"> $ {option.displayName}</option></select><标签>已选择:$ {currentOption.displayName}</label></div></div></div></div></template> 

src/app.ts

 导出类App {公共值:字符串;公用选项:{displayName:string} [];Constructor(){this.options = new Array< any>();this.options.push({displayName:"Option 1"});this.options.push({displayName:"Option 2"});this.options.push({displayName:"Option 3"});this.options.push({displayName:"Option 4"});}公共attach():void {this.value = this.options [1];}} 

src/md-select.ts

  import {customAttribute,可绑定的bindingMode,TaskQueue,一次性的,BindingEngine,DOM,注入}来自"aurelia-framework";@inject(DOM.Element,TaskQueue,BindingEngine)@customAttribute("md-select")导出类MdSelect {public selectElement:HTMLSelectElement;@bindable({defaultBindingMode:bindingMode.twoWay})公共价值;构造函数(元素:元素,私有tq:TaskQueue){this.selectElement =元素;}公共attach():void {$(this.selectElement).material_select();$(this.selectElement).on("change",this.handleChangeFromNativeSelect);}public detached():void {$(this.selectElement).off("change",this.handleChangeFromNativeSelect);$(this.selectElement).material_select("destroy");}私人valueChanged(newValue,oldValue):void {this.tq.queueMicroTask(()=> {this.handleChangeFromViewModel(newValue);});}private _suspendUpdate = false;私人handleChangeFromNativeSelect =()=>{如果(!this._suspendUpdate){this._suspendUpdate = true;const event = new CustomEvent("change",{bubble:true});this.selectElement.dispatchEvent(事件)this.tq.queueMicroTask(()=> this._suspendUpdate = false);}}私人handleChangeFromViewModel =(newValue)=>{如果(!this._suspendUpdate){$(this.selectElement).material_select();}}} 

I need to find a reliable solution to making the two frameworks play nicely.

Using materialize-css, their select element uses jquery to apply the value change. However that then does not trigger aurelia in seeing the change. Using the technique of... $("select") .change((eventObject: JQueryEventObject) => { fireEvent(eventObject.target, "change"); }); I can fire an event aurelia sees, however, aurelia then cause the event to be triggered again while it's updating it's bindings and I end up in an infinite loop.... Stack Overflow :D

Whats the most reliable way of getting the two to play together in this respect?

解决方案

I have worked with materialize-css + aurelia for a while and I can confirm that the select element from materialize is quite problematic.

I just wanted to share one of my solutions here in case anyone wants some additional examples. Ashley's is probably cleaner in this case. Mine uses a bindable for the options instead of a slot.

Other than that the basic idea is the same (using a guard variable and a micro task).

One lesson I learned in dealing with 3rd party plugins and two-way data binding is that it helps to make a more clear, distinct separation between handling changes that originate from the binding target (the select element on the DOM) and changes that originate from the binding source (e.g. the ViewModel of the page containing the element).

I tend to use change handlers with names like onValueChangedByBindingSource and onValueChangedByBindingTarget to deal with the different ways of syncing the ViewModel with the DOM in a way that results in less confusing code.

Example: https://gist.run?id=6ee17e333cd89dc17ac62355a4b31ea9

src/material-select.html

<template>
    <div class="input-field">
        <select value.two-way="value" id="material-select">
            <option repeat.for="option of options" model.bind="option">
                ${option.displayName}
            </option>
        </select>
    </div>
</template>

src/material-select.ts

import {
    customElement,
    bindable,
    bindingMode,
    TaskQueue,
    Disposable,
    BindingEngine,
    inject,
    DOM
} from "aurelia-framework";

@customElement("material-select")
@inject(DOM.Element, TaskQueue, BindingEngine)
export class MaterialSelect {
    public element: HTMLElement;
    public selectElement: HTMLSelectElement;

    @bindable({ defaultBindingMode: bindingMode.twoWay })
    public value: { name: string, value: number };

    @bindable({ defaultBindingMode: bindingMode.oneWay })
    public options: { displayName: string }[];

    constructor(
        element: Element,
        private tq: TaskQueue,
        private bindingEngine: BindingEngine
    ) {
      this.element = element;
    }

    private subscription: Disposable;
    public isAttached: boolean = false;
    public attached(): void {
        this.selectElement = <HTMLSelectElement>this.element.querySelector("select");
        this.isAttached = true;

        $(this.selectElement).material_select();
        $(this.selectElement).on("change", this.handleChangeFromNativeSelect);

        this.subscription = this.bindingEngine.collectionObserver(this.options).subscribe(() => {
            $(this.selectElement).material_select();
        });
    }

    public detached(): void {
        this.isAttached = false;
        $(this.selectElement).off("change", this.handleChangeFromNativeSelect);
        $(this.selectElement).material_select("destroy");
        this.subscription.dispose();
    }

    private valueChanged(newValue, oldValue): void {
        this.tq.queueMicroTask(() => {
            this.handleChangeFromViewModel(newValue);
        });
    }


    private _suspendUpdate = false;

    private handleChangeFromNativeSelect = () => {
        if (!this._suspendUpdate) {
            this._suspendUpdate = true;
            let event = new CustomEvent("change", {
                bubbles: true
            });
            this.selectElement.dispatchEvent(event)

            this._suspendUpdate = false;
        }
    }

    private handleChangeFromViewModel = (newValue) => {
        if (!this._suspendUpdate) {
            $(this.selectElement).material_select();
        }
    }
}

EDIT

How about a custom attribute?

Gist: https://gist.run?id=b895966489502cc4927570c0beed3123

src/app.html

<template>
  <div class="container">
    <div class="row"></div>
    <div class="row">
      <div class="col s12">
        <div class="input-element" style="position: relative;">
          <select md-select value.two-way="currentOption">
            <option repeat.for="option of options" model.bind="option">${option.displayName}</option>
          </select>
          <label>Selected: ${currentOption.displayName}</label>
        </div>
      </div>
      </div>
    </div>
</template>

src/app.ts

export class App {
  public value: string;
  public options: {displayName: string}[];

  constructor() {
    this.options = new Array<any>();
    this.options.push({ displayName: "Option 1" });
    this.options.push({ displayName: "Option 2" });
    this.options.push({ displayName: "Option 3" });
    this.options.push({ displayName: "Option 4" });
  }

  public attached(): void {

    this.value = this.options[1];
  }
}

src/md-select.ts

import {
    customAttribute,
    bindable,
    bindingMode,
    TaskQueue,
    Disposable,
    BindingEngine,
    DOM,
    inject
} from "aurelia-framework";

@inject(DOM.Element, TaskQueue, BindingEngine)
@customAttribute("md-select")
export class MdSelect {
    public selectElement: HTMLSelectElement;

    @bindable({ defaultBindingMode: bindingMode.twoWay })
    public value;

    constructor(element: Element, private tq: TaskQueue) {
      this.selectElement = element;
    }

    public attached(): void {
        $(this.selectElement).material_select();
        $(this.selectElement).on("change", this.handleChangeFromNativeSelect);
    }

    public detached(): void {
        $(this.selectElement).off("change", this.handleChangeFromNativeSelect);
        $(this.selectElement).material_select("destroy");
    }

    private valueChanged(newValue, oldValue): void {
        this.tq.queueMicroTask(() => {
            this.handleChangeFromViewModel(newValue);
        });
    }


    private _suspendUpdate = false;

    private handleChangeFromNativeSelect = () => {
        if (!this._suspendUpdate) {
            this._suspendUpdate = true;
            const event = new CustomEvent("change", { bubbles: true });
            this.selectElement.dispatchEvent(event)
            this.tq.queueMicroTask(() => this._suspendUpdate = false);
        }
    }

    private handleChangeFromViewModel = (newValue) => {
        if (!this._suspendUpdate) {
            $(this.selectElement).material_select();
        }
    }
}

这篇关于jQuery更改事件和Aurelia的技术的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆