【问题标题】:AureliaJS - Call child function from parentAureliaJS - 从父级调用子函数
【发布时间】:2017-07-05 10:57:25
【问题描述】:

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

这些孩子的形式本身只有两个特定的东西。他们的模型和模型的验证规则。

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

场景与拥有一个基类类似,该基类具有一个方法,并且此方法可以覆盖子类。

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

这是一个例子: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';
 }
}

【问题讨论】:

    标签: forms aurelia aurelia-templating


    【解决方案1】:

    将函数调用绑定到子级,以便您有一个句柄来从父级调用它。我通常更喜欢直接绑定子组件而不是使用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!';
      }
    }
    

    【讨论】:

    • 我也喜欢这个解决方案。在 angular1 中,我曾经完全这样做过,但在 Aurelia 中,我不知道该怎么做。谢谢你的例子。我认为对于将来要创建更多子表单的任何人来说,这都是一个明确的解决方案。很明显,子表单可以有一个属性,父表单可以调用该属性来验证业务规则。
    【解决方案2】:

    你可以这样做:

    父表单.html

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

    父表单.js

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

    帮助说明

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

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

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

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

    希望这会有所帮助!

    【讨论】:

    • 好的,这就是我想要的。谢谢。我也会考虑您的建议。
    • uhhh.. 如果childFormVM 已经拥有虚拟机,为什么他们还需要做 ref 的事情?
    • @AshleyGrant 在他的例子中,childFormVM 是一个字符串。如果 childFormVM 是一个对象,那么 ref 就没有必要了。
    • 对象是什么意思?直到运行时,当用户选择他要打开的表单时,要编写的子表单是未知的。
    • 如果childFormVM 是一个视图模型实例,您可以执行&lt;compose view-model.bind="childFormVM" view.bind="childFormView"&gt;&lt;/compose&gt; 之类的操作。所以在这种情况下,ref 就没有必要了
    【解决方案3】:

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

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

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

    【讨论】:

    • 对于您的第一个建议,如果我理解正确,它与使用父母和孩子都可以访问的服务以及孩子可以在哪里注册功能以便父母可以调用的服务相同?第二个是异步的,我需要它是同步的。因为只有模型有效,父节点才能保存数据。
    • 啊,我明白了。首先,您可以使用服务,但您不必必须。如果您的子组件位于组合 DOM 树中的父组件内,您可以简单地直接注入父组件——也就是说,您可以将 ParentForm 注入每个 ChildForm 的构造函数中。当然,另一种方法是通过绑定来实现,但如果耦合真的故意那么紧密,IMO 构造方法会更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 2019-03-07
    • 2021-06-19
    • 2019-02-07
    相关资源
    最近更新 更多