Validation of field in one-to-many relationship


#1

I have one form including a one-to-many relationship (using tabs):

class Parent{
     id: number;
    globalInformation: string;
    children :  Child[];
}

class Child{
    id: number;
    someField : number;
}

Now in my html-form I want to get the input field globalInformation disabled if the field someField of any related childs is set.
Currently I have no idea how I can handle that. Can anyone give me a hint?

Following Approach I could imagine:

<input type="text" 
       value.bind="parent.globalInformation" 
      disabled.bind="parent.childs.filter((element, index, array) => {  return (element.someField) }) > 0">

Any ideas?


#2

If your approach works, then some is more elegant than filter in this case:

parent.childs.some((element, index, array) => {  return (element.someField) })

#3

Unfortunately my solution doesn’t work :’(
But thanks for that hint :slight_smile:


#4

What if you assigned this expression to a VM property and bind to the property?


#5
<input type="text" 
       value.bind="parent.globalInformation" 
      disabled.bind="parent.childs.filter((e, i) => {  return (e.someField) }) > 0">

I suppose you already have something like above, in working form:

<input type="text" 
       value.bind="parent.globalInformation" 
      disabled.bind="hasOneValidItem(parent)">
export class MyVm {
  hasOneValidItem(parent: Parent) {
    return parent.children.some(child => child.someField > 0);
  }
}

Now the issue comes when in some of the children, someField fields were changed from lesser than or equal to 0 to greater than 0.

By default, Aurelia does not do deep observation, like this scenario. So in order to update the binding, there needs to be some hint:

  • Binding Signaler (global signaling)
  • Value Converter (local signaling)

Or you can use dirty checking, which is pretty cheap if the number of children is not big.

With binding signaler, simply change the template to:

<input type="text" 
       value.bind="parent.globalInformation" 
      disabled.bind="hasOneValidItem(parent) & signal :some-field-changed">

Then you can inject BindingSignaler imported from 'aurelia-templating-resources' and signal somne-field-changed.

With value converter, simply change template to:

<input type="text" 
       value.bind="parent.globalInformation" 
      disabled.bind="hasOneValidItem(parent) | signalFieldChange: signalCount">

The value converter is just an identity function, basically to return whatever the value is given to it. Its main purpose is to help binding reevaluate the expression whenever view model signalCount got changed. Then whenever you modify the Child.someField, increase view model signalCount by one and you get your UI updated.


#6

Wouldn’t the following work as long as children array is immutable.

@computedFrom('parent.children')
get isDisabled() { return parent.children.some(child => child.someField> 0); }

#7

I guess it would work, but the children array is mutable.


#8

Just make sure to always set it using the spread operator.

{…do stuff to all the children}
parent.children = […parent.children];