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

Subscribing to events to a specific instance #28

Open
frederikprijck opened this issue Aug 8, 2014 · 16 comments
Open

Subscribing to events to a specific instance #28

frederikprijck opened this issue Aug 8, 2014 · 16 comments

Comments

@frederikprijck
Copy link

Dear,

I fail to find a way to subscribe to an event of a specific viewmodel instance.

Let's say I have a ViewModelA of which I have two instances, each publishing the same event.
I would like to subscribe seperatbly to the event of both instances.

Am I correct to state that this is not possible using postbox?
I added a fiddle: http://jsfiddle.net/frederikprijck/zohqLcvo/

As you can see I want to subscribe only to the "MyViewModel_Clicked" event published by myViewModel2, which should not do anything since I am not binding myViewModel2 to my HTML. Still my subscriber get invoked when the event is published from myViewModel1.

@rniemeyer
Copy link
Owner

Your only choices would be to:

  • not set up publishing in one of the view models
  • publish on a topic (and subscribe to it) that is unique, like in your case: MyViewModel_Clicked_" + myId
  • in the subscriber, filter out messages from other view models

Hope that helps.

@frederikprijck
Copy link
Author

Hi,

  1. This is impossible, since both viewmodels are identical. It's just a new instance of the same object. Code has to be 100% identical.
  2. This might be a work around, but it's definatly not the way to go. I dont want to identify my events by an id, but by the object who is responsible for publishing it.
  3. Could you show me how to achieve this one? Filtering out lessages from other viewmodels might be the best solution for now.

Thanks for the help so far !

@rniemeyer
Copy link
Owner

@frederikprijck - At some level you would need to be able to identify the two view models from each other. Is this possible in your case?

Another solution, might be to have a method on the view model that "turns on" this functionality in the view model. Then, someone outside of the view model could turn it on for the specific view model that you care about.

@frederikprijck
Copy link
Author

Ofcourse I can identify them, it's two different instances. I do not want to turn on or off the functionality. Let's say I got 2 instances of the same viewmodel, but I want different code to be executed for the same event but different Viewmodel instances

@rniemeyer
Copy link
Owner

I'm sorry, I think we are having trouble connecting on your scenario.

If you are going to use a messaging type solution like postbox, then the two view models would need to publish messages on different topics, if you need to perform different actions for each one, unless you can differentiate each view model based on the message that they are publishing.

Otherwise, you would need to control it externally by subscribing with different handlers to each view model like:

var MyThing = function () {
    this.updated = ko.observable();  
};

var ViewModel = function () {
    this.thingOne = new MyThing();
    this.thingTwo = new MyThing();

    this.thingOne.updated.subscribe(function () {
       console.log("do something special when thing one is updated"); 
    });

    this.thingTwo.updated.subscribe(function () {
       console.log("do something special when thing two is updated"); 
    });
};

@frederikprijck
Copy link
Author

Thanks for the previous example, basicly this is what I want to achieve but not with simple observable subscription. I want to be able to be notified when a button is clicked instead of when an observable is updated. (So the same as your example, but with manual publishing)

I'll try my best to explain my situation again.

I've modified my orginal fiddle to this one: http://jsfiddle.net/zohqLcvo/2/ As you can see I have a button which publishes a message when being clicked. I've added a subscriber which is called for both published message.

This is not what i want, What I want is to have different code executed depending on which viewmodel was publishing the event: http://jsfiddle.net/zohqLcvo/5/
However, this fiddle does not work since I can not call "subscribe" on myViewModel1 and myViewModel2, which i totally understand.

A working example of what I want is: http://jsfiddle.net/zohqLcvo/7/
However, I'm looking for an alternative way to achieve this. I am looking whether there is an existing way to check which object was publishing the message, I do not want to manualy pass 'this' everytime I need this kind of funcitonality. Also I do not like having a big "if" statement in the subscriber, I want to have different subscribers for each ViewModel instance.

@rniemeyer
Copy link
Owner

To me the choices are:

1- Do it like your last sample where you are filtering out based on something in the published message. maybe can create a small API around it.
2- Use some type of unique value in the topic like an id. here is a sample that creates a subscribe API on the view model that hides the topic/id from the subscriber: http://jsfiddle.net/rniemeyer/a2pj01xd/
3- Subscribe against an observable like my sample before. On a click, update that observable with the value. External subscribers subscribe to that observable.

@frederikprijck
Copy link
Author

Thanks for the answers.

  1. This is not a way to go. I hate to have all those "if" statements. Thats not object oriented in any way. It's a hack to make it work, not a standard way of achieving what I want.
  2. Nah, that's not scalable in any way. In an object oriented world, we want to work with objects as much as possible, not using simple id's to make it possible. Nevertheless It will indeed work for my problem. But I'm thinking further than only my scenario. Imagine I have to implement such subscribe method with the id and a ++count everywhere I need this kind of functionality... Not realy what we want to have.
  3. That's idd a valid workaround but won't be usable in every scenario.

Wouldn't it be a good idea to extend postbox to have a the subscribe function take an extra parameter? Like so: http://jsfiddle.net/zohqLcvo/8/
So the subscribe method accepts an object to which it wants to subscribe, it will ignore the same event being published by other objects, even if it has the exact same name. If we pass null/undefined it will still subscribe to every object publishing this message.

I've been using MVVM messenger libraries for many years (in .NET this is), and I realy like postbox's implementation for kojs. Nevertheless I realy think that having the above functionality implemented makes it even more powerful and awesome.

So, if I am correct, the answer to my initial question is: No, postbox does not support this standard but there are ways to achieve it which I find kinda hacky. But maybe it's a good thing for future development to implement it.

Nevertheless, good work on the library!

@rniemeyer
Copy link
Owner

I am trying to work towards a solution, but I don't think this is a good fit for what you are trying to do. Just to clarify your comments from the last note:

1- I mentioned wrapping it in an API. The API could use some type of index object that would point a view model to handler. Ultimately you wouldn't end up with if/else.
2- It is common for data objects to already have some type of unique identifier. My intention was not really to ++count, but to use whatever id is available.
3. Personally, if I understand your scenario correctly, then I would probably use something like this one. To extend it a bit further, each view model could have its own "postbox" / event emitter just using an observable (and not ko.postbox at all). Would be like: http://jsfiddle.net/rniemeyer/kv5mtb6r/

I could potentially see something like the enhancement that you suggested, although the actual object that is publishing is not really available/involved in just a ko.postbox call. Obviously, the publish API could be changed to take in the object that is initiating it. Not sure how I feel about that though, as those events should then be internal to that object rather than going through the postbox. Maybe some type of mix-in to add postbox capabilities to a view model itself.

@rniemeyer
Copy link
Owner

Another thought for you would be to potentially just create wrapper calls to subscribe and publish that take in an owner.

Sample: http://jsfiddle.net/rniemeyer/nko5L3Lt/

@frederikprijck
Copy link
Author

Thanks Ryan for the support so far.

If I understand correctly, today postbox has no knowledge of who has been publishing the message, that's why you mention the need to also adjust the publishing api.

I understand this is a drastic change, but I dont think it has to be a bad one. If my above assumption is correct, than I think it wouldnt be a bad thing to couple messages to its publishing object. This would give you a lot more control over it. Eventually when you publish a message, you publish it from somewhere/something (an object). If you want to listen to the event being published by any kind of publisher (object), than you could still make sure this is possible by specifying no owner in the subscribe!

The last example is exactly what I want. I do think that it needs some finetuning for performance. Let's say I got a WidgetViewModel, and there is the possibility that I add 50 widgets to my page, each widget publishing the "widgetLoaded" message with a different owner.
When one of the widgets is publishing the message, postbox will still call 50x the subscribe method, tho we will block out 49 of them. So im not 100% sure it's going to be great for bigger applications.

Nevertheless, this is the best solution so far! :-) Thanks

And you gotta admit, this aint no bad thing to make possible in the library it self! :D

@rniemeyer
Copy link
Owner

I agree about performance. Still think it might be better to "mix-in" this type of capability to an individual view model to manage itself, like the previous example: http://jsfiddle.net/rniemeyer/kv5mtb6r/. postbox could even add a call to extend/mix-in a postbox to an object.

Otherwise, it could potentially be managed inside of the postbox API as well (create separate subscribables for each "owner"). Lots of interesting possibilities.

@frederikprijck
Copy link
Author

I have to admit, your last example doesn't look bad at all. Tho the notifyWithOwner looks bettter from the API point of view. But this might be better for performance.

The downside I see is that you still can not realy publish events in this way, but you can idd notify all the subscriber, Which has the same result, but people like to be able publish/subscribe as far as i know.

I guess it's not that easy to find the most ideal solution. Nevertheless I think we've got two decent solutions so far, which might need some digging deeper.

@frederikprijck
Copy link
Author

@rniemeyer

Any updates on this ? This is still marked as "open". I was wondering if there are any future plans to make this available in the library ?

@rniemeyer
Copy link
Owner

@frederikprijck - I need to refresh myself on this conversation. I would be happy to add something, but need to sort out all of the potential suggested solutions. What do you see the API looking like? Do you have interested in pursuing a PR? Thanks!

@frederikprijck
Copy link
Author

Been awhile since I used knockout :( so a PR could be possible, but not at this moment. If you prefer a PR, leave the issue open for abit longer. I wont lose track of it that way and try to make a PR in the future.

What I would love the API to look like would be something self-describing. I want to subscribe to a specific topic on a specific object / function in js.

Without taking a look at the exact ko.postbox API i would go for something like:

subscribe(objectToListenTo, "someKey", handlerFunction);

This reads for me: On Object A, I want to listen on the someKey topic and execute this code.

Publishing:

publish(objectToListenTo, "topic", handlerFunction)

Ofcourse in both subscribe and publish It should be optional to supply this. So we can still use global messaging.
But using this aproach we create a messaging library alowing to be used on a global and instance level.

If possible, I would even try to not modify the external publish API but internally keep track of the object who called publish. This would allow us to compare the object passed to the subscribe method with this one.
If this could be achieved using "this" keyword, we could use the .apply / .call / .bind methods of javascript to manipulate the "publisher", used to compare on subscription.

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