Backbone events being fired without trigger - coffeescript

So I have a strange issue where my backbone events are getting fired even though they haven't been triggered yet. Essentially I'm making a note editor application. In the note itself, a user can press cmd + b to bold text, or any of the other normals. That then triggers an event which bubbles up to the AppController which should be subscribed to that event and call the correct method.
Here is the view for the note where the trigger is called:
class MeetingNote.View.NoteView extends Backbone.View
adminTemplate: _.template($('#AdminNoteTemplate').html())
normalTemplate: _.template($('#NormalNoteTemplate').html())
className: 'note'
events:
'keydown' : 'handleKeyDownsForStyling'
# all of the normal backbone stuff.... init/render/blah
handleKeyDownsForStyling: (e) ->
if #admin == true
if e.metaKey
switch e.which
when 66 then #trigger "boldSelection"
when 73 then #trigger "italicizeSelection"
when 85 then #trigger "underlineSelection"
Then here is my AppController which binds to the event when the NoteView is instantiated
class MeetingNote.View.AppController extends Backbone.View
template: _.template($('#MeetingNoteAppTemplate').html())
className: 'MeetingNoteApp'
initialize: (options) ->
#admin = options.privilege
#render()
render: ->
#$el.html(#template())
$('#container').append(#$el)
#initializeApp()
initializeApp: ->
#adminTools = new MeetingNote.View.AdminTools if #admin == true
notes = new MeetingNote.Collection.NotesCollection()
notes.fetch {
success: (collection) =>
_.each collection.models, (model) =>
note = new MeetingNote.View.NoteView {model: model, privilege: #admin}
#bindNoteEvents note if #admin == true
}
bindNoteEvents: (note) ->
note.on "boldSelection", #adminTools.boldSelection(), note
note.on "italicizeSelection", #adminTools.italicizeSelection(), note
note.on "underlineSelection", #adminTools.underlineSelection(), note
lastly, here is the #adminTools.boldSelection() function
boldSelection: ->
console.log( "yo" )
for some reason, upon page load, that console.log is being fired even though I never sent the trigger by pressing cmd + b in the note View. Anyone have any idea why a Backbone.Event would fire automatically?

This is a function call:
#adminTools.boldSelection()
#------------------------^^
This is a reference to a function:
#adminTools.boldSelection
You're supposed to hand on a reference to a function so that it can call the function later. Your bindNoteEvents should look more like this:
bindNoteEvents: (note) ->
note.on "boldSelection", #adminTools.boldSelection, note
note.on "italicizeSelection", #adminTools.italicizeSelection, note
note.on "underlineSelection", #adminTools.underlineSelection, note
# No parentheses here --------------------^^^^^^^^^^^^^^^^^^

Related

Atom package - setInterval not calling the function

I'm currently working on a package for atom which involves drawing a curve on a canvas. For whatever reason the following code logs "mouseDown" without ever logging "hi" when handleClickDown is called. I've tried without window. but still "mouseDown" is all that's logged. The handleClickDown function is called every time the canvas is clicked if that context helps at all. I'm sure I'm just not understanding something about how setInterval / coffescript works since I'm new to both atom and coffescript. Any help is appreciated.
#printhi: ->
console.log "hi"
handleClickDown: (event, element) ->
console.log "mouseDown"
#mouseDownId = window.setInterval(#printhi,100)
Edit: The following code seems to run fine
console.log "mouseDown"
#mouseDownId = setInterval(()=>
console.log "inner"
,75)
however when using a function like this it throws an error that _this.printhi is not a function
#printhi = () ->
console.log "hi"
handleClickDown: (event, element) ->
console.log "mouseDown"
#mouseDownId = setInterval(()=>
#printhi()
,75)
Ok just answering my own question here as I figured it out eventually. Turns out I was a bit confused on how # and => work in coffeescript. You actually need to remove the # from #printhi: -> so it becomes printhi: -> and then only use it when you're calling it like this #printhi().
Code below worked for me, hope someone finds this helpful
printhi: ->
console.log "hi"
handleClickDown: (event, element) ->
console.log "mouseDown"
#mouseDownId = setInterval(()=>
#printhi()
,75)

Implement typing delay in ScalaJS?

I have a search input field in a ScalaJS app that fires off requests to a backend server whilst the user types in a city name. However, I need to implement a delay so that the request is not fired until after a certain delay (say 1000ms). Without such a delay, there is the chance that I'll get back false positives on the search (E.G. If the user wants to search for "paris", then there will be a false hit on "par" - a small town in Cornwall, England - when the third character is entered)
I've tried transcribing the JavaScript equivalent into Scala, but the setTimeout part doesn't seem to work.
import scala.scalajs.js.timers.{SetTimeoutHandle, clearTimeout, setTimeout}
private def delay = () => {
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0)(() => {})
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms)(fn)
}
}
Then I'm handling the user input event using an Akka Actor
def receive = {
/************************************************
* Client event
* The user has typed something into the search field
*/
case evt: Event =>
delay()(handleInput, 1000.0)
}
Where handleInput is the zero parameter function that obtains the user's input and then fires off a request to the backend.
The anonymous inner function that clears and then resets the timeout is executed, but the handleInput function never gets called
Thanks
Chris W
The problem in your code is that you are giving a function of type () => Unit to setTimeout, but setTimeout takes a by-name parameter. In other words, the argument to setTimeout should be a statement to execute when the timeout expires. If you give it a function, then after the timeout that function value will be evaluated, but the function will not be called!
It is similar to mistakenly trying to do
val result = fn // result is the *function* itself, but does not call it
instead of
val result = fn() // fn is called, result is what it returns
You can fix your call to setTimeout by replacing fn by fn(). Also, it is typically more idiomatic, in those circumstances, to use {} instead of () for the parameter to setTimeout, which also gives a visual clue that it is a by-name parameter:
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms) {
fn()
}
}
You should also adapt your first dummy setTimeout for consistency, although since it is a dummy anyway, that will not change the behavior:
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0) {}

Docpad: using extendTemplateData via mongoose callback

I'm trying to build a simple plugin to get get data from Mongo into an object over which I can iterate when rendering. The full code is in my project, but the essence of it is a failed attempt to emulate the feedr example. I know the mongoose stuff is working as the console log works, but getting the content sent to the docpad object is defeating me
class mongoPlugin extends BasePlugin
name: 'mongo'
# Fetch list of Gigs
getGigsData: (opts) ->
mongoose.connect ('mongodb://localhost/test')
db = mongoose.connection;
db.on 'error', console.error.bind(console, 'connection error:')
db.once 'open', () ->
gigsSchema = mongoose.Schema {
date : String,
location : String
}
Gigs = mongoose.model 'Gigs', gigsSchema
Gigs.find {}, (err, gigs) ->
mongoose.connection.close()
if err then console.error "db error"
else
console.dir gigs
opts["getGigsData"] = gigs
opts.templateData["getGigsData"] = gigs
return gigs
extendTemplateData: (opts) ->
opts.templateData["getGigsData"] = #getGigsData()
Using node-inspector and triggering a regeneration by editing docpad.coffee, I can see that opts has a field templateData, but it is empty, and is very different from docpad.templateData, so I am picking up the wrong object in the plugin. I can see others did a trick of placing a name in { } but I don't know what that does.
After completing the plugin code I see that my database data becomes the argument to a promise, so perhaps that's where it is supposed to be reintegrated with the docpad.config.templateData but that does not seem to happen in practise
So the main issue here is that we have an asynchronous function getGetsData being executed inside a synchronous function, your templating engine. This simply, isn't possible, as the templating engine will go on and do its thing, while the synchronous stuff happens in the background. This is just an issue with just writing node.js/asynchronous code in general.
The fixes for this is pretty easy to do.
opts.templateData["getGigsData"] = #getGigsData() calls getGigsData without passing over the opts, so that when getGigsData tries and uses the opts, it can't, so that would throw an error. The fix for this is to do #getGigsData(opts)
opts.templateData["getGigsData"] = #getGigsData(opts) assigns the return value of #getGigsData(opts) to the template data, however, the result of this is the result of the db.once call, as that is what will be returned in that scope. When you do return gigs, that's actually the return value for the (err, gigs) -> callback on the Gigs.find call, rather than the return value for the getGigsData. It's all about scopes.
As the database stuff is asynchronous, we need to make getGigsData asynchronous. To do this, we change extendTemplateData: (opts) -> to extendTemplateData: (opts,next) -> to make it asynchronous, and change opts.templateData["getGigsData"] = #getGigsData() to simply return #getGigsData(opts,next)
Now that we have the event and call asynchronous. We now need to make the definition of getGigsData support it. So lets change getGigsData: (opts) -> to getGigsData: (opts,next) -> to take in the completion callback (next) that we defined in step 3. And what we will do, is we will call next where we have return gigs, so lets change return gigs to return next()
It should now work. But as a little bit of cleaning, we can make the error handling better by changing if err then console.error "db error" to return next(err) if err. You will need to fix up the indentation as we will need to remove the else block.
Considering all that, and with a bit more cleaning applied, you'll end up with this:
class mongoPlugin extends BasePlugin
name: 'mongo'
config:
hostname: 'mongodb://localhost/test'
# Fetch list of Gigs
getGigsData: (opts={}, next) ->
config = #getConfig()
docpad = #docpad
mongoose.connect(config.hostname)
db = mongoose.connection
db.on 'error', (err) ->
docpad.error(err) # you may want to change this to `return next(err)`
db.once 'open', ->
gigsSchema = mongoose.Schema {
date: String,
location: String
}
Gigs = mongoose.model('Gigs', gigsSchema)
Gigs.find {}, (err, gigs) ->
mongoose.connection.close()
return next(err) if err
return next(null, gigs)
# Chain
#
extendTemplateData: (opts,next) ->
#getGigsData null, (err, gigs) ->
return next(err) if err
opts.templateData.gigs = gigs
# Chain
#

how to handle implicit return in coffeescript when using async.js

i'm trying to get
testFunction: () ->
console.log "testFunction"
async.series(
(->
console.log "first"
),
(->
console.log "second"
)
)
i've also tried to no success
testFunction: () ->
console.log "testFunction"
async.series(
(->
console.log "first"
return undefined
),
(->
console.log "second"
return undefined
)
)
to run, i would expect console output of "testFunction", "first", "second" but I'm getting "testFunction", "second" and it seems like there is a problem with coffeescript's use of implicit returns, (I guess).
Attached is a screen shot of the javascript output compiled from the above coffeescript.
Every function that does work for async needs to take a callback as its only argument.
one = (callback) ->
console.log "one"
callback()
two = (callback) ->
console.log "two"
callback()
testFunction: () ->
console.log "testFunction"
async.series [one, two], (error) ->
console.log "all done", error
You've got a number of problems. The first is that you're not passing the correct arguments to async.series. It expects:
async.series([functions...], callback)
while you're calling
async.series(function, function)
Since the length attribute of the first function is undefined, it assumes its an empty array and skips straight to the "callback" (second function). It sounds like you may want to pass an array of two functions and omit the callback.
The second problem is that functions passed to async.series must call a callback in order for progression to continue. The callback is the first argument to each function:
testFunction: () ->
console.log "testFunction"
async.series([
((next) ->
console.log "first"
next()
),
((next) ->
console.log "second"
next()
)
])
async ignores the return value of most (all?) functions that you pass to it.

Iced coffee script with multiple callbacks

I'm using Iced coffescript with upshot js when I am refreshing multiple data sources. The refresh method has TWo call backs one for success and one for error and I want to wait for each call to make either callback.
I can't see how to do this with idced coffescript without making an additional function. My question is - is there a more elegant way that I can defer to one of multiple callbacks?
This is the code I have currently:
refreshMe = (key, value, result) =>
value.refresh(
(success)=>
result success
,
(fail, reason, error)=>
result undefined, fail
)
#refresh = () =>
success={}
fail={}
await
for key, value of #dataSources
refreshMe key, value, defer success[key], fail[key]
This is the only way I have found to do it too. I'm using it in Backbone and wrap (for example) a model's #save function with an #icedSave:
# An IcedCoffeescript friendly version of save
icedSave: (callback) ->
#save {},
success: (model, response) -> callback(true, model, response)
error: (model, response) -> callback(false, model, response)
Here's some code I use for converting Promises .then (-> onSuccess), (-> onError) to errbacks (err, result) ->:
# You can write like this:
await value.refresh esc defer e, result
# onError - function to be called when promise rejected.
# onSuccess - function to be called when promise is fulfilled.
module.exports = esc = (onError, onSuccess) ->
util = require 'util'
return (result) ->
if util.isError result
# Always send back an error to first handler.
onError? result
else if onSuccess?
console.log onSuccess, result
# `await fn esc done, defer result`
onSuccess? result
else
# `await fn esc done`
onError? null, result
You could modify the esc function a bit to handle multiple arguments for each callback.
iced.Rendezvous lib is made explicitly for this case: return at the first of multiple callbacks. From the docs:
Here is an example that shows off the different inputs and outputs of
a Rendezvous. It does two parallel DNS lookups, and reports only when
the first returns:
hosts = [ "okcupid.com", "google.com" ];
ips = errs = []
rv = new iced.Rendezvous
for h,i in hosts
dns.resolve hosts[i], rv.id(i).defer errs[i], ips[i]
await rv.wait defer which
console.log "#{hosts[which]} -> #{ips[which]}"