AureliaJS - 从 parent 调用 child 函数

AureliaJS - Call child function from parent

我正在使用 AureliaJS 构建一个动态表单场景,其中我有一个 parent 表单,其中包含所需的总操作和多个 child 表单,这些表单会根据用户输入进行更改。

这些 child 的表格本身只有两个特定的东西。他们的模型及其模型的验证规则。

所以我的问题是,parent 表单如何调用当前 child 表单的验证规则?从 child 我知道可以调用 parent 的视图模型。但是从 parent,我怎样才能从 child 调用任何函数?

情况与拥有一个基础 class 的情况类似,它具有一种方法,并且该方法可以覆盖 child classes。

有什么建议吗?如果需要,我很高兴改变方法。

举个例子:https://gist.run?id=1865041a15af60600cb7b538018bdccd

app.html

<template>
  <span>This is an APP</span>
  </p>
  <compose view-model.bind="'parentForm'"></compose>
</template>

app.js

import { autoinject } from 'aurelia-framework';

@autoinject
export class App {

}

childForm1.html

<template>
  <label> Price : </label>
  <input value.bind="model.data.price">
  <p/>
  <label> VAT : </label>
  <input value.bind="model.data.vat">
  <p/>
</template>

childForm1.js

import { autoinject } from 'aurelia-framework';

@autoinject
export class ChildForm1 {

  activate(model)
  {
    this.model = model;
  }

 validateRules (){

     if(this.model.data.price != '' && this.model.data.vat == '' )
      this.model.validateMessage = 'VAT is mandatory';
 }
}

childForm2.html

<template>
  <label>Address : </label>
  <input value.bind="model.data.address">
  <p/>
  <label>Phone : </label>
  <input value.bind="model.data.phone">
  <p/>
</template>

childForm2.js

import { autoinject } from 'aurelia-framework';

@autoinject
export class ChildForm2 {

  activate(model)
  {
    this.model = model;
  }

 validateRules (){

   if(this.model.data.phone != '' && this.model.data.address == '' )
      this.model.validateMessage = 'Address is mandatory';
 }
}

index.html

<!doctype html>
<html>
  <head>
    <title>Aurelia</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body aurelia-app>
    <h1>Loading...</h1>

    <script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
    <script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
    <script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
    <script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
    <script>
      require(['aurelia-bootstrapper']);
    </script>
  </body>
</html>

parentForm.html

<template>
  <button click.delegate="changeChildForm1()">Change Child Form 1</button>
  <button click.delegate="changeChildForm2()">Change Child Form 2</button>
  <p/>
  <p/>
  <form>
    <label>User : </label>
    <input value.bind="model.data.user">
    <p/>
    <compose view-model.bind="childFormVM" model.bind="model"></compose>
    <button click.delegate="save()">Save</button>
    <p/>
    <span> Validation Message :  ${model.validateMessage}</span>
  </form>
   <p/>
  <span>Price : ${model.data.price}</span><p/>
  <span>Vat : ${model.data.vat}</span><p/>
  <span>Phone : ${model.data.phone}</span><p/>
  <span>Address : ${model.data.address}</span><p/>
</template>

parentForm.js

import { autoinject } from 'aurelia-framework';

@autoinject
export class ParentForm {
  model = {
   validateMessage : '', 
   data : {
    user : 'My Name'
   }
 };

 childFormVM = 'childForm1';

 validateMessage = '';

 changeChildForm1() {

   this.childFormVM = 'childForm1';
 }

  changeChildForm2() {

   this.childFormVM = 'childForm2';
 }

 save(){
   this.validateRules();
   // How to call the validation rules from child ?
 }

 validateRules (){

   this.model.validateMessage = 'Validate by parent';
 }
}

立即想到的一件事是,您可以在构造函数中将 parent 模型注入到 child 模型中——注入的实例将是相同的,而不是新创建的实例.这样,您的 parent 可以定义一个方法,允许 child 在 parent 上注册自己,然后 parent 可以调用 [=17] 上存在的任何方法=] 在选择时。

不过,这会在组件之间产生相当强的耦合,因此您需要考虑您是否可以接受。

如果不是,另一种解决问题的方法是使用 event aggregator。 parent 形式可以在聚合器上发送事件,而 children 将成为订阅者监听事件。在这种情况下,根据您是否在一个页面上托管多个此类组合,您可能希望为随事件发送的表单包含一个唯一标识符,并将该 ID 绑定到 child 组件,因此他们将知道只监听来自 parent.

的事件

将函数调用绑定到子项,以便您拥有从父项调用它的句柄。我通常更喜欢直接绑定子组件而不是使用 compose,但是您可以通过传递一个复杂的模型对象而不仅仅是模型,并将绑定函数作为模型属性之一传递,使其与 compose 一起工作。

父视图模型:

class Parent {
  model = {};
  child1Validate = null;

  changeChildForm1() {
    if (typeof this.child1Validate === 'function') {
      // the binding was successful; proceed with function call
      let result = this.child1Validate();
      console.log(result);
    }
  }
}

父视图:

<my-child1 model="parentModel" go-validate="child1Validate"></my-child1>

子视图模型:

class MyChild1 {
  @bindable model;
  @bindable goValidate;
  bind() {
    // bind the child function to the parent that instantiates the child
    this.goValidate = this.runValidation.bind(this);
  }
  runValidation() {
    // do the validation and pass result to parent...
    return 'Success!';
  }
}

这就是你可以做到的:

parent-form.html

<compose view-model.bind="childFormVM" view-model.ref="childFormInstance" model.bind="model"></compose>

parent-form.js

save() {
  this.childFormInstance.currentViewModel.validateRules();
}

有用的注释

仅在必要时使用 <compose>。例如,在 app.html 中,您应该将 <compose> 替换为:

<require from="parentForm"></require>
    
<parent-form></parent-form>

使用 kebab-case 而不是 camel-case 来命名您的文件。例如,不使用 parentForm.htmlparentForm.js,而是使用 parent-form.htmlparent-form.js。这不会改变您的代码中的任何内容,但您将遵循很好的 javascript 标准 :)

直接绑定到字符串时不需要使用 .bind。例如,view-model.bind="'parentForm'" 可以替换为 view-model="./parentForm"

希望对您有所帮助!