ITGoran
ITGoran

Reputation: 442

Binding complex object to a component

Introduction

My goal is to create custom element in aurelia so that I can reuse it across the application.

In this context I've created component called operator-detail (operator-detail.html and operator-detail.js) which will holds information about operator and my plan is to reuse it in several places in application.

In this use case I have electornicRegistrationForm object which holds reference to operatorDetails and legalRepresentative. Both instances are injected into electornicRegistrationForm module and will be used as part of wizard allowing user to create a document which will be printed later on.

This electronicRegistraionForm is injected into operatorStep component.

operator-detail.html component I've included in operatorStep.html and confirm that component has been rendered correctly.

Problem

How to pass (bind) property operator from operatorStep.js to operator-detail.html component so that values from object operator are displayed (binded) in a two way binding manner.

In following example, value from this.operator.firstName 'First name from operator step' don't get displayed in operator-detail.html component.

Source code

electronicRegistrationForm.js:

import { OperatorDetail } from 'common/operator-detail';
import { LegalRepresentative } from 'common/legalRepresentative';
import { inject } from 'aurelia-framework';
@inject(OperatorDetail, LegalRepresentative)
export class ElectronicRegistrationForm {
  constructor(operatorDetail, legalRepresentative) {
    this.operator = operatorDetail;
    this.legalRepresentative = legalRepresentative;
  }
}

operator-detail.js

import {inject, NewInstance} from 'aurelia-framework';
import {ValidationRules, ValidationController} from 'aurelia-validation';

@inject(NewInstance.of(ValidationController))
export class OperatorDetail {
  constructor(validationController) {
    this.validationController = validationController;
    this.firstName = '';
  }

  attached() {
    ValidationRules
      .ensure('firstName').displayName('Ime').required()
      .on(this);
  }          
}

operator-detail.html

<template bindable="operatorvalue">
  <div>
    <div class="row">
      <div class="col-sm-6">
        <div class="form-group">
          <label for="firstName" t='first_name'></label>
          <input type="text" class="form-control" id="firstName" value.bind="operatorvalue.firstName ">
        </div>
      </div>          
    </div>
</template>

operatorStep.js

import { ElectronicRegistrationForm } from 'model/electronicRegistrationForm';
import { inject, NewInstance } from 'aurelia-framework';
import { RegistrationWizard } from 'registration/registrationWizard';
import { WizardStep } from 'registrationSteps/wizardStep';
import { ValidationController } from 'aurelia-validation';
import {bindable, bindingMode} from 'aurelia-framework';

@inject(ElectronicRegistrationForm, RegistrationWizard, NewInstance.of(ValidationController))
export class OperatorStep extends WizardStep {
  @bindable({ defaultBindingMode: bindingMode.twoWay }) operator;

  constructor(electronicRegistrationForm, regWiz, validationController) {
    super(electronicRegistrationForm, regWiz, validationController);
    this.operator = electronicRegistrationForm.operator;
    this.operator.firstName='First name from operator step';
    this.representative = electronicRegistrationForm.legalRepresentative;
  }
}

operatorStep.html

<template>
  <require from="common/operator-detail"></require>
  <form validation-renderer="bootstrap-form">
    <operator-detail operatorvalue.bind="operator"></operator-detail>
  </form>
</template>

Upvotes: 2

Views: 720

Answers (1)

Fred Kleuver
Fred Kleuver

Reputation: 8047

Declaring a bindable property on a template is for when you have a View without a ViewModel.

The bindable="operatorvalue" in your operator-detail.html doesn't work because you also have a ViewModel defined for this element. If you want to keep it this way then simply remove the bindable="operatorvalue" from the template and instead declare it in your ViewModel like so:

import {inject, NewInstance, bindable} from 'aurelia-framework';
import {ValidationRules, ValidationController} from 'aurelia-validation';

@inject(NewInstance.of(ValidationController))
export class OperatorDetail {
    @bindable operatorvalue;

    constructor(validationController) {
        this.validationController = validationController;
        this.firstName = '';
    }

    attached() {
        ValidationRules
            .ensure('firstName').displayName('Ime').required()
            .on(this);
    }          
}

operator-detail.html would then become:

<template>
    <div class="row">
        <div class="col-sm-6">
            <div class="form-group">
                <label for="firstName" t='first_name'></label>
                <input type="text" class="form-control" id="firstName" value.bind="operatorvalue.firstName ">
            </div>
        </div>
    </div>
</template>

Alternatively, you could make the bindable property declaration in the template work by simply deleting operator-detail.js and putting the validation stuff elsewhere. Then, rather than injecting OperatorDetail into the registration form you could new up the operator object like so:

import { LegalRepresentative } from 'common/legalRepresentative';
import { inject } from 'aurelia-framework';

@inject(LegalRepresentative)
export class ElectronicRegistrationForm {
    constructor(legalRepresentative) {
        this.operator = {};
        this.legalRepresentative = legalRepresentative;
    }
}

Both ways will work.

Working without ViewModels means putting the validation logic in electronicRegistrationForm.js for instance, which also means less code to write.

Working with ViewModels gives you more encapsulation, which is generally preferable if your view-specific logic is more complex than what you've shown here.

EDIT

In case you're using Google Chrome, I highly recommend using the aurelia context extension. You could then inspect the html and see that operator was defined in operator-step, but operatorvalue was undefined in operator-detail. This would tell you that the operatorvalue bindable declaration must be wrong, rather than something else.

Upvotes: 2

Related Questions