Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observers In Octane #18934

Closed
MatthewPringle opened this issue Apr 27, 2020 · 12 comments
Closed

Observers In Octane #18934

MatthewPringle opened this issue Apr 27, 2020 · 12 comments

Comments

@MatthewPringle
Copy link

I have tried to find out what is happening with observers in Octane but I have not managed to find anything specific.

If I am writing an app in Octane 3.17 how should I go about using an observer, should I even use an observer.

I basically want to watch a property on a service which gets updated from a network connection by others devices connected by webRTC.

@pzuraq
Copy link
Contributor

pzuraq commented Apr 29, 2020

In general, observers are not recommended in Octane. There are techniques for using autotracking to derive state, but they are a bit advanced. I'm planning on writing some more blog posts for digging into how these work in the near future.

Also related is the @use and Resources RFC, which would provide some higher level abstractions for using these. We're still working on the design here, stay tuned!

@mcfiredrill
Copy link
Contributor

I've been wondering this as well. I've had some observers that I still use as they didn't quite exactly fit as computed properties, and its unclear if I can convert them to use @tracked instead (it doesn't seem like it would work). I would love to hear any tips or techniques.

@MatthewPringle
Copy link
Author

Yes I am also having issues doing anything complex with computed properties within the new system. For example tracking child properties of objects stores in services.

Some new examples / tips / techniques would be great because at the moment it is looking like I could not rewrite some of my more complex apps with Octane.

@pzuraq
Copy link
Contributor

pzuraq commented May 1, 2020

@MatthewPringle in general, the recommendation there is to create classes for your objects stores if they have a well defined shape where you know the keys ahead of time, and mark those keys as tracked.

If not, and it's a dictionary with many keys, consider using one of these libraries:

@MatthewPringle
Copy link
Author

@pzuraq thanks Chris, I will check them out now.

@mcfiredrill
Copy link
Contributor

It also seems like ember-render-modifiers are a good substitute, for alot of cases?

@MatthewPringle
Copy link
Author

Can I ask a question about an alternative method for a specific use case.

Let's say I have an external library which connects to an api service through sockets. It returns a tracked state, .connected which can either be true or false. Unfortunately it doesn't fire an event when it is disconnected.

In ember I have an internal property .loading that I set when the user presses a button and a message is sent to the api, through sockets.

There is no callback and previously I would have just observed the .connected property and then flipped .loading back to false and reset the state of the component. I am not quite sure how to handle this without an observer, using timers or events.

@pzuraq
Copy link
Contributor

pzuraq commented May 20, 2020

@MatthewPringle the .connected state is tracked, using Ember's tracking? Or using a different form of tracking?

If it's Ember's tracking, then you can define loading as a getter:

class MyClass {
  get loading() {
    return !this.api.connected;
  }
}

If it's not Ember's tracking, how would you use this API in general, in vanilla JS? How would you know when the value changed?

@pzuraq
Copy link
Contributor

pzuraq commented May 20, 2020

For reseting the state of the component, this is where @use and Resources would be very helpful. They allow you to create pockets of managed state that can reset themselves whenever something upstream changes. So, you could do something like:

class MyClass {
  get loading() {
    return !this.api.connected;
  }

  @use myManagedData = managedData(() => this.loading);
}

The reset logic would be kept entirely in the managedData resource.

@MatthewPringle
Copy link
Author

MatthewPringle commented May 20, 2020

It is a very simplified example. Im kinda piggybacking a socket connection.

.loading and .connected are not linked. The component sends data over the socket and when a response arrives the state updates, .loading is reset etc... and all is good.

The issue is the connected status of the socket, the .connected property is set internally and is dependent on the socket being connected to the server, not related to any data loading.

So the issue has arisen where the user presses a button, I set .loading so the UI updates, shows a loading message, but then the socket disconnects before a response. The socket cleans itself up, .connected is set to false etc... But my component, unaware of the change, still has .loading set.

If I were writing the whole thing from scratch I would use events I guess, but this is an older ember component which was previously observed, and could be retrofitted, but seems like a lot of work when an observer would have previously managed this.

@pzuraq
Copy link
Contributor

pzuraq commented May 20, 2020

@MatthewPringle what state is setup on the component exactly that you need to reset? Like I said before I think you could do this with resources and it would flow really nicely, but I think we need some more details to figure out how that would work exactly.

@MatthewPringle
Copy link
Author

MatthewPringle commented May 20, 2020

export default class LoadingBox extends Component {
  @service( 'network' ) NetworkService;
  @reads( 'NetworkService.connected' ) .connected;
  @tracked loading = false;
  @action buttonStart() {
    if ( !this.loading && this.connected ) {
      this.loading = true;
      this.NetworkService.send({
        type: 'start',
        data: {}
      });
    }  
  }   
}

This is the basic component. If the state updates correctly the component is not rendered anymore due to logic in the template. Setting .loading updates the UI and stops the button being spammed.

The issue here is if the socket becomes disconnected the user cannot press the button again as .loading is still true.

There is logic further up to handle the socket disconnects, but as they sometimes timeout and otherwise seem to disconnect and reconnect quite often we do not update the UI nor fire any events for a disconnect straight away and instead the app tries to recover a connection.

The sockets are just there to handle the initial setup of peer to peer connections and as such the UI doesn't depend on a constant connection but an issue has arisen where I need to get a little more data to the socket server and utilising the open socket seemed the way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants