formsaureliaaurelia-templating

AureliaJS - Call child function from parent


I'm using AureliaJS to build a dynamic forms scenario, where I have a parent form with the gross operations needed and multiple child's form's, that change based on user input.

These child's form's have only two specific things themselves. Their model and the validation rules for their model.

So my question is, how can the parent form call the validation rules from the current child form? From child I know that is possible call parent's view model. But from parent, how can I invoke any function from the child?

The scenario is similar off having one base class, that has one method and this method could be overriding on the child classes.

Any suggestion? I'm glad to change the approach if needed.

Here's an example: 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';
 }
}

Solution

  • That's how you can do it:

    parent-form.html

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

    parent-form.js

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

    Helpful Notes

    Only use <compose> when necessary. For example, in the app.html you should replace <compose> for:

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

    Use kebab-case instead of camel-case to name your files. For example, instead of parentForm.html and parentForm.js, use parent-form.html and parent-form.js. This won't change a thing in your code but you will be following nice javascript standards :)

    When binding directly to a string you don't need to use .bind. For example, view-model.bind="'parentForm'" could be replaced for view-model="./parentForm"

    Hope this helps!