Split events and function in Javascript / CoffeeScript - backbone.js

I have been using Backbone a lot lately and love it because it keeps my code clean.
I am just building another site and I really want to use some of the backbone / coffeescript syntax. For example for a one page loader I was thinking of doing something like this:
This is a single HTML page that loads one JavaScript file
#query Events
$(document).ready =>
$(".click").click ->
console.log "hello"
alert()
#Functions
alert: ->
console.log "alert"
So I can split all my functions neatly below and the events at the top. So I can keep track. In the above example I am trying to get the click event to fire on alert.
In backbone I can have an events list which trigger alerts, which I find really useful. But I do not need all the views / controllers / models etc.
At the moment though this does not work. What is the best way to have an event fire another function outside of its own function?
Maybe I am doing this really wrong, but I love the simplicity of splitting this up and keeping it clean.

Okay so i messed up my syntax quite a bit...
$(document).ready ->
$(".click").click ->
console.log "hello"
alert()
alert = ->
console.log "alert"
Works, if anyon else is looking to do this then this has worked for me :)

Related

angular event doesn't update page

I'm converting a page in a mvc application with a lot of inline jquery javascript to angular using typescript.
The first calls works fine but I have a problem: based on a selection in a dropdown, the event updates several controls in the view, make a few ajax calls and finally update the view with the data from the calls.
Seems the conversion is working fine, but at the end of the call the page isn't updated.
I tried to remove all the old jquery code to avoid problems.
batarang and java console reports no errors.
the final ajax call is done and the result shown in a debug.
All seems to work fine, but the page isn't updated.
How can I find the problem?
thanks
Without seeing any code, it's difficult to answer but if you bind an event to an element and want to update something in the callback, you will have to use $apply
scope.$apply(function () {
// your code
});
$apply will trigger a $digest cycle, and should be used when you want to update something while being outside angular's context.
Most likely you are not handling your asynchronous calls correctly. It's impossible to tell from your question but it is a very common mistake with symptoms as you describe.
Make sure you are updating your model within the .then() method of a promise returned from an $http request. For example
someFnDoingHttpRequest().then(function(data){
// updated the model with the data
})
Also (another common mistake) make sure someFnDoingHttpRequest() returns a promise.
If you want to "find the problem" then you can use the following option.
Go to Internet Explorer (10 or 11).
Select "Internet Options" from the settings menu.
Go to the "Advanced" tab (the last tab)
Settings are listed and select "Display a notification about every script error"
Deselect the "Disable Script debugging (Internet Explorer)" and "Disable script debugging (Other)"
Run the program again, you will get notification about the real issue that happens while displaying actual result.

How do I get /mylink#sectionid to work in angularjs?

If I wasn't using angular, then the route mylink would be loaded, then the browser would scroll down to the sectionid section.
In Angular it doesn't scroll. I read some completely crazy whacky solutions involving injecting multiple modules and having crazy unique URLs. I refuse to do things like this.
I want my href values to remain standard. Is there any way in Angular to do this?
Keep in mind, if "mylink" was already loaded, then the links work fine, but if I'm on a different page, say "home", then I navigate to mylink#sectionid, then the scrolling won't occur.
(I mean... if Angular can't do this, I would consider that a bug. It'd be absurd to not support a regularly used syntax since the 90s that is still used today)
EDIT: I think the issue may be the amount of AJAX on this website.
It is certainly possible, you will need to inject in $anchorScroll into your controller
The example from the angular site:
function ScrollCtrl($scope, $location, $anchorScroll) {
$scope.gotoBottom = function (){
// set the location.hash to the id of
// the element you wish to scroll to.
$location.hash('bottom');
// call $anchorScroll()
$anchorScroll();
};
}
From anther route you could handle this via parameter being passed into the route and scroll upon initialization based upon the route param.
I'm not a big fan of my solution, but I listen to onRouteChange, then inject anchorScroll and simply call anchorScroll after a 1000 ms timeout and because the hash is already set nothing more needs to be done. [giving time for all angular stuff to work its self out (the site I'm working on has entirely too much AJAX, but I don't have control of the data yet, so there is nothing I can do about that)]
Anywho, manually initiating anchor scroll works. If anyone knows a better way to do this, that'd be swell.

Better structure to watch window level DOM events in Marionette?

In a CompositeView, I implemented infinite scrolling like this
List.Foo extends Marionette.CompositeView
initialize: (collection) ->
#page = 1
$(window).on('scroll', #loadMore)
loadMore: =>
if _nearBottom
#page++
App.vent.trigger('list:foo:near_bottom', #page)
_nearBottom =>
$(window).scrollTop > $(document).height - $(window.height) - 200
# Then I have the controller to process the event "list:foo:near_bottom",
# to ask for adding one more page of data in collection.
The code basically works as expected. But I can't find it satisfactory as I think this ComposteView watches some DOM events outside of its scope, aka, the window level DOM events.
I thought to use a layout to watch such events and broadcast it, but my top level layout seems still not broad enough to cover window/document :)
My question is, what would be a better structure to watch these kinds of window/document level DOM event in Marionette? Thanks!
This question has not been answered for a long time, and I changed implementation in that project so I didn't touch it.
Nguyen's comment provided very nice point and reminds me to review this question.
I also have new understanding similar to Nguyen's point.
Something has to be global, we can't avoid it.
These things include but not limited to:
Route
Page scroll
Page load
Window resize
Global key stroke
...
Backbone has Routes to take care of routing events. The others things are not so important and so popular but they still need to be treated similar to routing.
A better approach would be, in my opinion: Watching the global DOM events at global level, send App event which don't care whoever may be interested in it.
If I re-do this feature, I will do something like this(pseudo code)
# App
App.on "initialize:after", ->
#startHistory()
#navigate('somePath', trigger: true) # Normal steps
App.module('WindowWatcher').start()
# WindowWatcher module
ExampleProject.module "WindowWatcher", (WindowWatcher, App, Backbone, Marionette, $, _) ->
class Watcher
constructor: ->
#watchPageScroll
watchPageScroll: ->
$(window).on('scroll', #_checkScroll)
_checkScroll: ->
if #_nearBottom:
App.vent.trigger(scroll:bottom)
_nearBottom:
$(window).scrollTop > $(document).height - $(window.height) - 200
WindowWatcher.on 'start' ->
new Watcher()
Then List.Foo controller will watch the App event scroll:bottom as he like, and supply next page.
There may be other parts interested in this event, for example in Footer view popping a button saying you are at bottom, or another notification saying if you want to see more you need to sign up, etc. They can also listen to the the App vent without need to manage window level DOM, thanks to the beauty of Marionette.
Important update
If you watch App vents directly inside controller but not at module level, make sure the controller will stop listen to this vent otherwise the listeners will increase in App.vents which is a memory leak.
# FooController
onClose: ->
App.vent.off 'scroll:bottom'

this.save in backbone

I am on way to learning backbonejs.
I am working with the popular todo list tutorial.
I have certain questions about which i am a bit confused:
In one the models i found this function:
toggle: function() { this.save({completed: !this.get(’completed’)});}
The thing that i don't understand is this.save function. How does it work? What does it actually saves and where. And what does the code inside this function means: completed: !this.get and so on.
In one of the views i found this line of code:
this.input = this.$(’#new-todo’);
Now what does this.input means? And i also don't understand the sytnax this.$('#new-todo');
Let me know if more code is needed for comprehension. Also if anyone could point me to great learning resources for backbone, it will be awesome. Currently i am learning from 'Backbone Fundamentals' by addyosmani.
Backbone Model and Collection both have url properties.
When set properly backbone will make a HTTP POST request with the model as a payload to the url when saved for the first time (id property has not peen set). I you call save and the models id has been already set, backbone will by default make PUT request to the url. Models fetch function generates a GET request and delete a DELETE request.
This is how backbone is made to work with RESTfull JSON interfaces.
When saving a model you can define the actual model to save like it's done in the example.
Read the Backbone.js documentation. It's ok!
http://backbonejs.org/#View-dollar
this.$('#new-todo') // this.$el.find('#new-todo')
toggle: function() { this.save({completed: !this.get(’completed’)});}
Its basically saving inverse value to "completed" attribute of model. so if model's current attribute is true, it would save it to false !
regarding this.input = this.$(’#new-todo’);
Its basically saving/caching DOM with id "new-todo" from current VIEW's 'el' to view instance's 'input' property. so that we do not have to call jQuery methods for getting the same element when we need in future.
hope this helps.
:)
I too am a backbone newbie and i had been in search of good tutorials that gave good insights into the basics and i found after around 3-4 days of searching. Go through backbonetutorials.com and there is a video compiled which gives exactly what we need to know about Routers, Collections, Views and Models.
The sample working can be found at : http://backbonetutorials.com/videos/beginner/
Although this tutorial is a very basic one, you need to have basic jquery, javascript knowledge. Keep http://www.jquery.com opened in another tab as well when you go through the sample codes. Documentation is extremely useful.
Once you have good knowledge of jquery then if you go through the tutorials, you will understand and pick it up a lot better. And once you get hold of the MV* pattern of backbone you'll love it.
p.s : Do not copy paste codes or functions if you need to learn, type them.!!..
Cheers
Roy
toggle: function() { this.save({completed: !this.get(’completed’)});}
Backbone Model have a url property, when you set a property backbone makes a HTTP request to that url to save that value to the data source.
Here it is setting the value of "completed" attribute with inverse of earlier "completed" value, which will be saved to the data source

Backbone model events/triggers not firing

Having seen it in a couple of tutorials, I'm trying to execute the following line of code in my view
#model.on('change', #render, this)
Unfortunately the change event is not firing and therefore my view is not re-rendering.
I've tried binding to different events and creating a couple of custom events using the trigger function but nothing seems to be firing at all on the model. Furthermore, there are no errors coming from the console. The change event is working fine on a different collection. I'm using Zepto 1.0, Backbone.js 0.9.2 and Underscore.js 1.3.1
EDIT: I'm trying to execute the following from the Router
place: (id) ->
#model = new GM.Models.Place({id: "#{id}"})
#model.fetch
view = new GM.Views.Place(model: #model)
$('#container').html(view.render().el)
And my model is set up like this:
class GM.Models.Place extends Backbone.Model
urlRoot: '/mobile/place'
I am wondering if anyone has experienced similar problems before and has a quick fix.
If not and you need more of the code to find an explanation please let me know...
You're not actually calling the #model.fetch method anywhere. This:
#model.fetch
is not a method call, you need to add parentheses or arguments if you want to call the method:
#model.fetch()
# or
#model.fetch success: -> ...
# etc.
Otherwise you're just producing this.model.fetch; in the JavaScript and that doesn't do anything useful.

Resources