I have two classes:
export class Shipment {
shipmentId: number;
widget: Widget;
}
export class Widget {
widgetId: number;
name: string;
}
Then I have a ShipmentUi
view-model that has an instance of shipment (this.shipment
).
And in the ShipmentUi
view I compose part of the UI show the WidgetUi that allows selection of the Widget:
<compose view-model="src/views/widgetUi" model.bind="shipment"></compose>
The WigetUi's view-model saves off the shipment. So WidgetUi has a this.shipment
.
And then widgetUi's view shows a selector:
<select value.bind="shipment.widget" >
<option class="dropdown-toggle" repeat.for="widget of widgets"
model.two-way="widget">${widget.name}</option>
</select>
In my compose tag (on ShipmentUi's view), I would rather bind to shipment.widget
.
This would have WidgetUi's view-model only get a this.widget
. (The WidgetUi
class does not need to see or know about the shipment
. Its sole function is to allow selecting a Widget
. It should not need to care if it is for a shipment or for something else.)
But, as I understand Javascript, this is not going to work.
Because if I just pass in the reference to shipment.widget, then WidgetUi will have just a reference to the widget part. At first WidgetUi's this.widget
will have the same reference as ShipmentUi's this.shipment.widget
.
But when the user selects a different widget, WidgetUi this.widget
will get a different reference (to the newly selected widget in the dropdown). But ShipmentUi's this.shipment.widget
will will still reference the original widget.
When binding to child objects in Javascript, do you always have to pass in the containing object if you want to know about swap of the child object?
The reason for this question is that my tests are not 100% conclusive. So I am hoping someone can clear it up for me.
I am also hoping I am wrong somehow, as I really don't like the idea of having to expose all the data in the containing classes. (Giving access to shipment
in the WigetUi
class in this case.)
Fabio Luz requested some clarification on what I am asking. So here is an attempt. This walks through the example above, but changing it to the way I would LIKE it to work.
I have two Widgets. Sharp Widget and Dull Widget.
ShipmentUi.js:
This class has the variable this.shipment.widget
. I am going to say that its value is 'A3' (arbitrary memory value). 'A3' is a reference to a widget that has a name of 'Sharp Widget'.
I then pass the widget down to the WidgetUi class:
<compose view-model="src/views/widgetUi" model.bind="shipment.wiget"></compose>
WidgetUi.js:
The WidgetUi class has:
activate(widget: Widget) {
this.widget = widget;
}
So now in WidgetUi this.widget
also has a value of 'A3'. That value is a memory reference to the Widget that has a name of 'Sharp Widget'.
Now the user uses this select
element to change the Widget:
<select value.bind="widget" >
<option class="dropdown-toggle" repeat.for="widget of widgets"
model.two-way="widget">${widget.name}</option>
</select>
This time I bind to widget
(instead of this.shipment.widget
like I did above).
Then user picks a widget with the name of 'Dull Widget' using the select
. That widget has a value of 'B7'. And 'B7' is a reference to the widget named 'Dull Widget'.
As I understand JavaScript, WidgetUi's this.widget
now has a value of 'B7' (which is a reference to 'Dull Widget'). (This is done via the Aurelia data binding system.)
But ShipmentUi's this.shipment.widget
is still 'A3' (which is a reference to 'Sharp Widget').
This is not what I wanted when I bound this.shipment.widget
to the compose
element. I wanted the updates to the widget object to be reflected in the shipment. (Note, if I had just updated widget.name
, then it would have been updated.)
So, from what I can see, I have to pass in the full parent to the compose
element (this.shipment
in this case), if I want an assignment to be captured.
I am hoping I am wrong (or there is a workaround), because passing the parent object makes me share details that the "child" class does not need to know about. (ie it breaks data encapsulation)
I guess I could make a "holder" between each layer of my classes. For Example: this.shipment.holder.widget
and holder
would just have the widget in it. But this is kinda ugly... I hope there is another way...
So, my question is: Am I right with my above statements? And if I am, is there another way that keeps my object model clean?
If I understand the question correctly you're looking for a way to share the minimum amount of data with the widgetui component. Instead of giving it the whole shipment object so that it can manipulate the shipment.widget
property, you'd rather give it a property accessor to the widget property.
Good news: this is exactly what @bindable
is designed to do. All you'll need to do is stop using compose and craft a custom element with @bindable
properties representing the minimum amount of data the custom element needs to do it's job. For example:
widget-picker.js
import {bindable, bindingMode} from 'aurelia-framework';
export class WidgetPicker {
@bindable({ defaultBindingMode: bindingMode.twoWay }) widget;
@bindable widgets;
}
widget-picker.html
<select value.bind="widget">
<option repeat.for="widget of widgets" model.bind="widget">${widget.name}</option>
</select>
usage:
<widget-picker widget.bind="shipment.widget" widgets.bind="widgets"></widget-picker>
Example:
https://gist.run/?id=4c726da335aaecefd80b
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With