Getting component state from child class

Hi,
I wondering if there’s a solution to this question other than “rewrite the app”…?

I am working on a microscope image viewer where the main model of the app contains a list of the images (size, id, contrast settings etc) and they are shown in various viewports:

The user can also zoom and pan each Image, and the zoom/pan state is held in each ‘viewer’ component.

Now what I’m trying to do is to export the current state and layout of the images, including the zoom/pan state of each viewer.
However, I don’t know if there’s a way that the main app can access the zoom/pan state of each viewer?

Obviously this would be easier if the zoom/pan state of each viewer would be included in the main app.images list, but that’s not how it’s done and would require a major update to change that.

To illustrate the problem I’ve created a simple representation based on the sample Todo app from the docs. In this case, instead of zoom/pan state for each viewer component, I’ve added a color state to each todo component, and I would like to be able to get the color for each todo item when the Get Colors button is clicked:

codesandbox example

Many thanks for taking the time to help,

Will

1 Like

There is probably a better way to do this, but this might help you. Based on your example try adding something like the code below.

getTodoColors() {
    const todoComponents = document.querySelectorAll("viewer");

    todoComponents.forEach((component) => {
      console.log(component.au.viewer.viewModel); // {colors: Array(3), color: 'red'}
    });

    ...
  }

Also good to mention that this is for Aurelia 1.

Hope it helps :grinning:

1 Like

You can extend your Todo class in such a way that your viewer custom element can assign the color to a property in the Todo class. In that case, your getTodoColors can be simplified greatly.

getTodoColors() {
    this.todos.map(x => x.color);
}

Thanks both for your help and suggestions.

I think I’m going to go with @ivan’s approach since that doesn’t require me to make big changes to the underlying models in order to add this feature.

Cheers!

2 Likes

I would consider that solution a bit of a hack. Just spend 5 min reading this and I think you will find it easy to apply.

The lower level component sends a message on what state it is in and the parent can listen for those changes.

Event-Aggregator

@magnusdanielson Thanks for your input. Do you consider the solution above to be bad practice because it is particularly fragile to change (e.g. if/when you rename the “viewer” component) or because the access via component.au.viewer is not a public API (unstable etc) or it’s just wrong to use the view component to access the model (should be the other way around)?

I’m not quite sure I understand your solution. In my case it is a higher level component (the ‘viewer’) that knows about the view state (e.g. color in the example above but it’s the zoom/pan state in my app), not the lower level component.
Which part of that Cheat Sheet doc are you referring to?

I could teach that component to also set the zoom/pan state in the data model that it wraps, so that in the example above the description and the color states are in the same model object. But that would be duplicating the state into 2 places and a bigger change than I’d like to make.

The PR where I’m using the above approach is at Save as Figure by will-moore · Pull Request #465 · ome/omero-iviewer · GitHub and it’s working fine so far.

The link details was removed by the editor. I have updated the link.

Maybe I should point out, that if it works for you then it works and no need to change.

In my mind it makes sense to have the lower level components send events through the EventAggregator and then listen to those events in the parent.

The arguments I have against the other solution is:

  • doing document.querySelectorAll and similar feels like we lost control of the application. It makes sense to me only when we have none aurelia components that does not easily wrap as a custom component.
  • similar component.au.viewer also feels like we lost control of the application. It should be possible to have a normal reference to the object you want to read
  • you might be writing in javascript and I in typescript, but I really enjoy compile type checking and that is lost with the other solution

OK, thanks for expanding on that. I’ll certainly consider the options, Cheeers,