Aurelia如何将所有属性转发到自定义组件中的子元素

Aurelia如何将所有属性转发到自定义组件中的子元素,aurelia,Aurelia,我在不同的项目中使用React和Aurelia 我的Aurelia应用程序变得非常复杂,我非常想处理重复的逻辑。假设我想要一个节流按钮,它将等待异步请求再次被单击,使用React它非常简单: 我希望我的与本机具有完全相同的行为,除非它会自动阻止多个请求发生,直到前一个请求完成或失败: class MyComponent extends Component { handleClick = async e => { const response = await this.longR

我在不同的项目中使用React和Aurelia

我的Aurelia应用程序变得非常复杂,我非常想处理重复的逻辑。假设我想要一个节流按钮,它将等待异步请求再次被单击,使用React它非常简单:

我希望我的
与本机
具有完全相同的行为,除非它会自动阻止多个请求发生,直到前一个请求完成或失败:

class MyComponent extends Component {
  handleClick = async e => {
    const response = await this.longRequest(e)
    console.log(`Received: `, response)
  }

  longRequest = async e => {
    return new Promise((res, rej) => {
      setTimeout(() => { res(e) }, 1000)
    })
  }

  render() {
    return <div>
      <button className="btn" onClick={this.handleClick}>Click me</button>
      <ThrottledButton className="btn" onClick={this.handleClick}>Click me</ThrottledButton>
    </div>
  }
}
类MyComponent扩展组件{
handleClick=async e=>{
const response=等待此.longRequest(e)
log(`Received:`,response)
}
longRequest=async e=>{
返回新承诺((res,rej)=>{
setTimeout(()=>{res(e)},1000)
})
}
render(){
返回
点击我
点击我
}
}
下面是
的一个实现:

class ThrottledButton扩展组件{
建造师(道具){
超级(道具)
this.state={operating:false}
}
render(){
const{onClick,…restProps}=this.props
const decoratedOnClick=async e=>{
如果(本状态操作)返回
this.setState({operating:true})
试一试{
等待承诺。解决(点击(e))
this.setState({operating:false})
}捕捉(错误){
this.setState({operating:false})
失误
}
}
返回{this.props.children}
}
}
现在我真的想在Aurelia中实现相同(或类似)的功能,下面是用例组件:

<template>
  <require from="components/throttled-button"></require>

  <button
    class="btn"
    click.delegate="handleClick($event)">
    Click me
  </button>

  <throttled-button
    class="btn"
    on-click.bind="handleClick">
    Click me
  </throttled-button>
</template>

export class MyComponent {
  handleClick = async e => {
    const response = await this.longRequest(e)
    console.log(`Received: `, response)
  }

  async longRequest(e) {
    return new Promise((res, rej) => {
      setTimeout(() => { res(e) }, 1000)
    })
  }
}

点击我
点击我
导出类MyComponent{
handleClick=async e=>{
const response=等待此.longRequest(e)
log(`Received:`,response)
}
异步长请求(e){
返回新承诺((res,rej)=>{
setTimeout(()=>{res(e)},1000)
})
}
}
这是我目前的节流按钮:

<template>
  <button click.delegate="decoratedClick($event)" class.bind="class">
    <slot></slot>
  </button>
</template>

export class ThrottledButton {
  @bindable onClick = () => {}
  @bindable class

  constructor() {
    this.operating = false
  }

  decoratedClick = async e => {
    console.log(this.operating)

    if (this.operating) return
    this.operating = true

    try {
      await Promise.resolve(this.onClick(e))
      this.operating = false
    } catch (err) {
      this.operating = false
      throw err
    }
  }
}

导出类节流按钮{
@可绑定onClick=()=>{}
@可绑定类
构造函数(){
这是错误的
}
装饰点击=异步e=>{
console.log(this.operating)
如果(本操作)返回
这是真的
试一试{
等待承诺。解决(这个。点击(e))
这是错误的
}捕捉(错误){
这是错误的
失误
}
}
}
如您所见,
与本机的
有很大不同,尤其是:

  • 而不是类似React的
    传播 运算符,我必须手动传递绑定的每个属性 至
    至本机按钮。对于大多数组件,它是一个 巨大的努力可能会导致许多错误

  • 我必须绑定(或调用)on-click属性,这使得它的工作方式与本机的非常不同。为了解决这个问题,我 尝试使用DOM dispatchEvent API

  • 以下是我尝试过的:

    <template>
      <button ref="btn">
        <slot></slot>
      </button>
    </template>
    
    @inject(Element)
    export class ThrottledButton {
      constructor(el) {
        this.el = el
        this.operating = false
      }
    
      attached = () => {
        this.btn.addEventListener('click', this.forwardEvent)
      }
    
      detached = () => {
        this.btn.removeEventListener('click', this.forwardEvent)
      }
    
      forwardEvent = e => {
        this.operating = true
        this.el.dispatchEvent(e) // no way to get the promise
      }
    }
    
    
    @注入(元素)
    导出类节流按钮{
    建造师(el){
    this.el=el
    这是错误的
    }
    附件=()=>{
    this.btn.addEventListener('click',this.forwardEvent)
    }
    分离=()=>{
    this.btn.removeEventListener('单击',this.forwardEvent)
    }
    forwardEvent=e=>{
    这是真的
    this.el.dispatchEvent(e)//无法获得承诺
    }
    }
    
    但是,无法获得对承诺的引用,这使得节流变得不可能


    有没有希望在奥雷利亚解决这些问题?任何想法都将不胜感激。

    您可以使用自定义属性来解决此问题。这是typescript中的代码

    import { autoinject, customAttribute } from 'aurelia-framework';
    
    @customAttribute('single-click')
    @autoinject()
    export class SingleClickCustomAttribute {
      clicked: (e: Event) => void;
      private value: () => Promise<any>;
      private executing: boolean = false;
    
      constructor(
        private element: Element
      ) {
        this.clicked = e => {
          if (this.executing) {
            return;
          }
    
          this.executing = true;
          setTimeout(async () => {
            try {
              await this.value();
            }
            catch (ex) {
            }
            this.executing = false;
          }, 100);
        };
      }
    
      attached() {
        this.element.addEventListener('click', this.clicked);
      }
    
      detached() {
        this.element.removeEventListener('click', this.clicked);
      }
    }
    
    从'aurelia framework'导入{autoinject,customAttribute};
    @customAttribute('单击')
    @自动注入()
    导出类SingleClickCustomAttribute{
    点击:(e:事件)=>作废;
    私人价值:()=>承诺;
    私有执行:boolean=false;
    建造师(
    私有元素:元素
    ) {
    this.clicked=e=>{
    如果(这个正在执行){
    返回;
    }
    this.executing=true;
    setTimeout(异步()=>{
    试一试{
    等待这个值();
    }
    捕获(ex){
    }
    此参数=false;
    }, 100);
    };
    }
    附({
    this.element.addEventListener('click',this.clicked');
    }
    分离的(){
    this.element.removeEventListener('click',this.clicked');
    }
    }
    
    像这样使用它

    <button single-click.call="someAsyncMethod()" type="button">...</button>
    
    。。。
    
    要调用的异步方法应如下所示:

    async someAsyncMethod(): Promise<void> {
        await this.saveData();
    }
    
    async someAsyncMethod():承诺{
    等待此消息。saveData();
    }
    

    诀窍是使用.call传递异步函数,并使用state属性。

    感谢您的回复。这些作品很棒,也很整洁。但我仍然想知道是否有任何方法可以将所有的道具传给某个孩子,这是一种非常有用的组合/继承模式。
    async someAsyncMethod(): Promise<void> {
        await this.saveData();
    }