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

Does it make sense to have an Observable of the entire class? #10

Open
CMCDragonkai opened this issue Oct 5, 2022 · 5 comments
Open

Comments

@CMCDragonkai
Copy link

With these decorators it becomes easy to create observable properties within a class.

But can the class itself be observable?

What that means is that if I subscribe to the class itself, I should get the whole entire class, all the properties of the class and any prototype methods. It'd likely be a BehaviourSubject since it would have a value as soon as the class is constructed.

@garretpremo
Copy link
Owner

@CMCDragonkai, that's an interesting idea. It's definitely possible!

For this to work, we would need to create a new decorator function intended for a class, let's just call it @ObservedClass. We would want to modify the setters for all initially defined properties so that they call .next(this) on an internal Subject. We would also want to define a subscribe() function on the class which just subscribes to the internal subject and returns a subscription.

I wrote up a quick example usage here, let me know if this is what you had in mind.

@ObservedClass()
class Person {

    constructor(public name: string,
                public age: number) {
    }

    incrementAge() {
        this.age += 1;
    }

}

Usage:

const person = new Person('Garret', 26);

const sub = person.subscribe(console.log);

// output: { name: 'Garret', age: 26, [[Prototype]]: Object }

person.incrementAge();

// output: { name: 'Garret', age: 27, [[Prototype]]: Object }

person.name = 'Billy';

// output: { name: 'Billy', age: 27, [[Prototype]]: Object }

sub.unsubscribe();

@garretpremo
Copy link
Owner

I might also add that an implementation like this would likely be incompatible with @Observed properties. For example:

@ObservedClass()
class A {
    @Observed() propA: string;
    readonly propA!$: Observable<string>;
}

Since both @ObservedClass and @Observed() work by overwriting property setters, either the ObservedClass setter or the Observed setter will be overwritten, and cease to work.

If I recall correctly, class decorators are applied before property decorators. That being the case, making a change to propA would update propA$, but would not update the internal ObservedClass subject

@CMCDragonkai
Copy link
Author

I checked out rxdb and they have something like this:

const object = new SomeClass();
object.$
object.prop$

Meaning that $ would be the observable version of the object itself. While its internal props can also be exposed as prop$ as opposed to prop.

This way, the object's methods won't conflict with the observable interface.

What are your thoughts on this? Is it better idea to encapsulate observable object into the $ property, or what you suggested in terms of making the object itself provide the observable interface.

@CMCDragonkai
Copy link
Author

Maybe if $ is used, you won't have the incompatibilities you mention above between the class and the properties?

I imagine that if it was possible to create an observable class, then I'd want to be able to use property decorators to decide which properties should be be considered observable.

@Observe()
class A {
  @ObserveProp()
  propA: string;
  readonly propA$: Observable<string>

  propB: string;
}

In this situation, we would therefore expect:

const a = new A();

// You can subscribe to `propA`
a.propA$.subscribe

// But you cannot subscribe to `propB`
a.propB$ === undefined

// Maybe you can subscribe to the `a.$`
a.$.subscribe

// Maybe you can subscribe to `a`
a.subscribe

I'm not entirely sure if it makes sense to use @ObserveProp when using @Observe on the entire class. This is because I'd imagine that by making the whole class observable, then I'd argue that it would be possible to "derive" a subscription on one of the public properties using pluck.

Maybe the main idea is that it would more efficient to directly subscribe to the property rather than plucking it from the object. This is because the property may change only a few times, but if you pluck from the object, you're getting the object property every time any other properties change.

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Oct 15, 2022

Yes class decorators do get applied before property decorators.

But there should be a way to "share" information between the class decorator and the property decorator.

This could be done with an external variable in the scope of the decorators. Or private properties keyed by a unique symbol.

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

2 participants