Closed nonchip closed 5 years ago
You probably need to debug the jQuery off() calls or jQuery.event.remove() calls that remove handlers, and see if the handler that is being removed is the simpleWebSocket handler. If so, see where that call is coming from. jQuery automatically removes event handler attached to HTML elements that are being removed from the DOM, but I don't know if simpleWebSocket is associated with any HTML/UI, or whether the listener is a jQuery handler on a data array or similar. No obvious reason why jsviews/jsobservable would be removing that handler, but you'll need to investigate further....
Incidentally you might find it useful to use compiled view models and the merge() feature (see also here and here) to replace your code above - and maintain all the data-link bindings. In your approach above you may be replacing objects without re-binding all the child objects and properties (in the 'object hierarchy'), which could also be causing some of your issues...
You probably need to debug the jQuery off() calls or jQuery.event.remove() calls that remove handlers, and see if the handler that is being removed is the simpleWebSocket handler.
i guessed so, but i'm afraid i have no idea how to do that right now, but going to research a bit, maybe i'll figure it out.
I don't know if simpleWebSocket is associated with any HTML/UI
it shouldn't be, at least i don't see a reason for the library to do so, and my instance of it is created like this:
$ ->
loc = window.location
ws = $.simpleWebSocket {
url: "ws" + (if loc.protocol == "https:" then "s" else "") + "://" + loc.host + "/ws"
}
Incidentally you might find it useful to use compiled view models and the merge() feature
i don't know what exactly you mean by "compiled view models" (i am using the <script type="text/x-jsrender" />
style templating language essentially just with the data
object as the template's "environment").
EDIT: ok if i'm reading this right the view model is an intermediate object to store the template's data instead of link
ing a plain object to a render, seems smart, since i already more or less do that with my data
object (syncing it from the websocket message etc), i can as well use the "right way" instead.
the merge function seems to do exactly what i want to achieve, gonna try that right now before i do any debugging on my own reinvention of the wheel, thanks :)
ok rewrote my stuff to use the view models:
vmChar = $.views.viewModels {
id: 'id'
getters: [
'name'
'alpha'
'beta'
'gamma'
'delta'
{getter: 'editable', defaultVal: false}
]
}
chars = vmChar.map []
ws.listen (msg)->
console.log 'received msg:', msg
if msg.gamedata and msg.gamedata.chars
console.log 'char'
chars.merge msg.gamedata.chars
$'#chars'.html $.templates("#tpl_char").render chars, helpers
now it "freezes" as soon as the first websocket message got handled. also i have no idea how to implement the "don't update this element if we're editing it right now" logic using the new system. but i can take care of that part after i figured out the websocket, guess i'll try another library for now, simpleWebSocket doesn't seem to be maintained that well :/
ok that definitely was an issue with simpleWebSocket, even just a simple new Websocket().onmessage
works fine. only remaining problem is for some reason when doing the switch to the view model i replaced the link
call with a render
call, which obviously broke the datalinking, but after fixing that it works perfectly, thanks for your help :)
whoops, that was a bit quick to close, it's actually not fully working, you see, i'm using logic like this:
<input type='checkbox' data-link='editable()' title="edit">
{^{if editable()}}
<input data-link="somevalue()">
{{else}}
<span data-link="text{:somevalue()}"></span>
{{/if}}
but now clicking the checkbox doesn't do anything anymore after the switch to the new model
EDIT: my mistake, i had a template error because i forgot to define an unrelated getter that was used in the same line. now i just have to figure out how to prevent merge
from changing anything that locally is set editable=true
...
tried this ugly hack:
vmChar = $.views.viewModels {.....}
vmcm = vmChar.prototype.merge
vmChar.prototype.merge = (data)->
if this._editable
return
return vmcm.call(this,data)
in the hopes the prototype would propagate correctly but it doesn't seem to have any effect.
also tried without the .prototype
but that also didn't affect anything.
if i apply the hack to the instance instead of the model it also doesn't seem to do anything, probably because it just doesn't propagate through the array (the instance is a vmChar.map []
).
my solution now is really ugly ~but works~, by overriding every single property getter:
protectEditable = (name)->
getter=(value)->
if !arguments.length
return this['_'+name]
if this._editable
return
$.observable(this).setProperty('_'+name, value)
getter.set=(value)->
this['_'+name]=value
return getter
vmChar = $.views.viewModels {
id: 'id'
getters: [
'name'
'alpha'
'beta'
'gamma'
'delta'
'mod'
{getter: 'editable', defaultVal: false}
]
extend: {
name: protectEditable('name')
alpha: protectEditable('alpha')
beta: protectEditable('beta')
gamma: protectEditable('gamma')
delta: protectEditable('delta')
editable: protectEditable('editable')
}
}
aaaaaaaand screwed up again. the underscore in $.observable(this).setProperty('_'+name, value)
was just preventing it from show how it actually got overwritten during the edit.
i have no idea what to do now /o\
nevermind, it all works now, removing the underscore was the right thing, i just forgot to add an unmap
call in my websocket sending function so the server never saw the changes and reverted them on the next update.
I'm glad it worked for you. Can you point me to the simpleWebSocket repo that you are using?
I wondered whether it might make sense to include a sample on jsviews.com of using web sockets, and the ViewModel approach with merge. If you are interested in creating a sample that we could publish there, let me know.
For the server piece it could if appropriate use the https://github.com/BorisMoore/jsrender-node-starter project. We could augment that project to provide the backend, and publish to heroku, for example.
If that idea makes sense/interests you, you could send me your suggested code (for a sample) and I could take a look... I could also look at the scenario you are addressing of "don't update this element if we're editing it right now", if it needs better support in the ViewModel merge feature...
Can you point me to the simpleWebSocket repo that you are using?
well i'm using plain websocket objects now because the simpleWebSocket repo broke.
in case you still want to see the code, I released the tool there: https://gitlab.com/nonchip/lite-roller.nonchip.de
specifically important i guess would be the main coffee file: https://gitlab.com/nonchip/lite-roller.nonchip.de/blob/master/static/main.coffee and the server code (which is moonscript running in openresty's ngx.lua module): https://gitlab.com/nonchip/lite-roller.nonchip.de/blob/master/apps/socket.moon
Thanks. I took a look. Of course you are using a few languages which I have never used myself, like lua, moonscript.... Glad to see you using a full range of JsViews features, and that you are able to integrate that well into your environment. What is the nature of the resulting app/site. Is it deployed?
For creating a sample, it would need to be stripped down to something minimalist, using only javascript (with nodejs on the server), so it may not straightforward to use your code as starting point for doing that... But thanks for showing me your code... Maybe in the future I'll look at doing a sample, when I have got up to speed on websockets etc.
BTW congratulations on your excellent English!
it is a tool for synchronized "dice" (d2
aka coins) rolling and result tracking for a pen&paper RPG, and yes it is deployed (at the same fqdn as the project's name), but not (yet?) able to support more than one campaign of users at once, so please don't mess up our game :P
i'm using the websocket to broadcast (actually poll, since i wanted to avoid RPC hell between server threads, but hey one round trip per second over websocket is way better still than ajax spamming) the state represented by the jsviews data so everyone sees/edits the same thing essentially.
if you want to deploy it yourself locally to play around with it:
eris
script inside it in your PATH (or adjust the eris
commands below)requirements.txt
eris rebuild
to compile everything and eris run
to actually start the serverconfig.moon
to custom_config.moon
, edit as you see fit, and rerun eris rebuild
before running the server againThanks for giving me that background. It gives me a better sense of what you are doing...
I won't have time for a while, but at some point I may look more closely at it, and play with it locally... :)
I got a (coffee)script that continuously gets updates to a structure via websockets and syncs those to an jsviews observable array by matching
id
members inside them, using the function:the first thing my websocket listener does, before even invoking it or touching any related data, is debuglog the received message:
problem now is, as soon as i hit the "remove" case (both adding and changing stuff works perfectly fine) it removes the item as expected, but then stops receiving any websocket events, meaning i can still see the events roll in in the network debug tab and reloading the page "unsticks" it (so it's not a server issue), but the listener doesn't show any more debug output and no functions are invoked from it, as if my script was somehow just stuck/paused/busylooping. though profiling it i don't see any such thing happen.
could it be the remove function (which then through jquery removes some event handlers, presumably for the removed UI elements attached to it) just somehow messes with my (also jquery based) simpleWebSocket handler?
it even still SENDS data to the server via the exact same socket (and as mentioned, i see the server reply to it in the network debug thing), it just refuses to handle the responses. e.g. i can modify other data, which is then reflected correctly in the websocket updates, and everyone else watching the same data (who has already reloaded to unstick their page) sees it. but the local (non-reloaded) version refuses to see any of that.