Rails 3.1 Backbone JST in View will not compile with events - ruby-on-rails

Working with the rails 3.1 asset pipeline and coffeescript.
I've used the rails-backbone gem for some generation to help me along and everything has been working well for me until I tried to put events to my view. As soon as I put anything to the events attribute the JST does not render (it does without it):
Headspace.Views.Shows ||= {}
class Headspace.Views.Shows.IndexView extends Backbone.View
template: JST["backbone/templates/shows/index"]
el: '#show_listing'
initialize: () ->
#collection = #options.collection
this.render()
events:
'click .show_header' : 'show_details'
show_details = ()->
alert('action')
render: ->
$(#el).html(#template({collection:#collection}))
I've also tried an empty events attribute (which doesn't compile unless I put in the empty {}) which does render the JST. I've considered that el is defined as a string instead of a jQuery element (with $()) until the render function. If I do specify:
el: $('#show_listing')
and:
render: ->
el.html(#template({collection:#collection}))
The JST does not compile.

just a mere typo you got there
instead of
show_details = ()->
alert('action')
it is
show_details : ()->
alert('action')
I am guessing you used shift+enter in textmate for the functions snippet? I have done that, too.

Related

Rails React Component Renders But Not Visible on Page

I just added the react-rails gem to my project and tried to add a react component to my index.html.erb file. I used the react component generator to make a simple message/text element, but it does not show up on my view after I deploy to Heroku, but it seems to render when I inspect the page. I have no idea why this isn't working.
Please look at the images I have attached. It looks like the javascript doesn't get compiled??
Body inspection part 2
index.html.erb code
<h2>Dashboard</h2>
<%= react_component('Message', text: 'hello') %>
<h3>Products TEST REACT </h3>
Message.jsx code
var React = require("react")
class Message extends React.Component {
render () {
return (
<div>
<div>Text: {this.props.text}</div>
</div>
);
}
}
Message.propTypes = {
text: React.PropTypes.string
};
module.exports = Message
HTML body inspection
I reverted to the git commit before installing react-rails and used the shakacode react-on-rails gem instead and got it to work

Jasmine, RequireJS and Rails

I'm starting to make the move over to requireJS for a project I'm building. I'm currently using jasminerice, rails 3.2 and the require-rails gem.
I've tried to implement http://ryantownsend.co.uk/post/31662285280/jasminerice-and-requirejs-rails-fix with little success, the specs don't run at all.
I am starting to think it maybe I might be better to use requirejs on it's own or maybe the jasmine gem?
I'm not sold on either jasminerice or require-rails gems, so does anyone have any advice on the best tools, and any tips on how to get it up and running/good tutorials?
Ok as I didn't get any response I managed to find a slightly hacky way of making it work.
If you create a file in your view folder jasminerice/spec/index.html.erb (or haml) and copy the html from the jasminerice gem. Replace the spec.js call with:
%script{"data-main"=>"/assets/#{#specenv}", src:"/assets/require.js"}
Then write your spec file like require template like so:
require.config {
paths:{
'jquery':'/assets/jquery'
'underscore': '/assets/underscore-min'
'sinon':'sinon-1.6.0'
'jasmine-sinon':'jasmine-sinon'
'my_js':'my_js'
'my_spec':'my_spec'
}
}
require ['sinon', 'jasmine-sinon', 'jquery', 'underscore', 'my_js', 'my_spec'], () ->
jasmine.getEnv().execute()
This will prevent jasminerice triggering the tests
jasmine.rice.autoExecute = false
Set up your tests with a beforeFilter similar to this(taken from http://kilon.org/blog/2012/08/testing-backbone-requirejs-applications-with-jasmine/)
describe "MySpec", ->
beforeEach ->
flag = false
#thing = ""
that = #
require ['myjs'], (Myjs) ->
flag = true
that.thing = new Myjs()
waitsFor ->
flag
it 'It should exsist', ->
expect(#thing).toBeDefined()
Hope that helps anyone with a similar issue and if anyone has a better solution please post! :)
I have the same setup, here's what I did (starting from the blog post mentioned in the original question):
1. Create a helper to load all spec files
In a file lib/jasminerice/spec_helper.rb, put the following code:
require "requirejs-rails"
module Jasminerice
module SpecHelper
include RequirejsHelper
def spec_files
Rails.application.assets.each_logical_path.select { |lp| lp =~ %r{^spec/.*\.js$} }
end
end
end
This will create a helper method spec_files which you can call in the Jasminerice runner view to automatically get all your specs, so you don't need to update the list of specs every time you add a new one.
2. Override default Jasminerice index view
Create a view named app/views/jasminerice/spec/index.html.erb with the following:
<!doctype html>
<head>
<title>Jasmine Spec Runner</title>
<%= stylesheet_link_tag "jasmine", "spec" %>
<%= requirejs_include_tag 'application' %>
<%= javascript_include_tag "jasminerice", "spec", :debug => true %>
<script>
jasmine.rice.autoExecute = false;
require([<%= spec_files.map { |f| "'#{f.sub(/\.js$/,'')}'" }.join(',').html_safe %>],
function() { jasmine.getEnv().execute() },
function(err) {
var failedId = err.requireModules && err.requireModules[0];
requirejs.undef(failedId);
define(failedId, function() { return function() { console.debug(failedId + ': ' + err); null }; });
require([ failedId ], function() {} );
});
</script>
<%= csrf_meta_tags %>
</head>
<body>
</body>
</html>
This will require all the specs before running Jasmine (with jasmine.getEnv().execute()). I have an ugly hack in there to take the array of spec paths and generate an array of module names in quotes to pass to require.
I've also included an error callback in case there's a problem loading a module -- if you don't do this, your specs will hang when a module load fails. That's especially a problem when you're running them on the command line through guard-jasmine, which is what I do.
Unfortunately I haven't found a very good way to handle such errors -- here I write some info to console.debug and then required the failed module, returning an anonymous function in its place. This allows the specs to run but produces unpredictable results (which is better than no results). I've been struggling to find a better way to deal with this situation, suggestions would be much appreciated.
3. Write some specs
My Jasmine specs take the form:
define (require) ->
MyModule = require 'my-module'
# any other dependencies needed to test
describe 'MyModule', ->
it 'exists', ->
expect(MyModule).toBeDefined()
etc. Note that all my testing dependencies (jasmine, sinon, jasmine-sinon, etc.) I load outside of require, in spec.js.coffee:
#=require sinon
#=require jasmine-sinon
#=require_tree ./helpers/
I put any other helper functions I need in the helpers directory.
4. Bonus
One other tip: if you have problems because your browser won't reload modules even when they change, I use a trick of adding a dummy argument with a timestamp so that the browser will always see a new file and correctly load it.
I created this function in ApplicationController which I load in a before filter:
before_filter :set_requirejs_config
def set_requirejs_config
opts = { :urlArgs => "bust=#{Time.now.to_i}" }) if Rails.env == "development"
Requirejs::Rails::Engine.config.requirejs.run_config.merge!(opts)
end
This adds a query param bust=... to the end of each module name if we're in development mode, so that we always reload modules and get the most up-to-date version. Somewhere there's a post on SO explaining how to do this in RequireJS, but to get it to work with requirejs-rails you have to put it into ApplicationController (and not config/requirejs.yml) so that it is loaded every time you load the page.
Hope that might provide some hints to anyone else using this configuration!

Backbone.js with jQuery draggable / droppable. Droppable isn't working

I have a Backbone app where I am creating 2 views, one draggable, and one droppable. Dragging works ok, but the droppable callback is never fired. How can I get the droppable view to "see" the draggable one?
My "droppable" view:
class App.Views.Folder extends Backbone.View
template: JST['folders/folder']
className: "folder"
initialize: (options) ->
#collection.on('add', #addOne, #)
#collection.on('reset', #addAll, #)
render: ->
#$el.html(#template(#model.toJSON()))
this.$el.droppable(
drop: -> alert("dropped!")
);
Draggable:
class App.Views.QuestionSet extends Backbone.View
template: JST['question_sets/question_set']
className: "question-set"
initialize: (options) ->
#collection.on('add', #addOne, #)
#collection.on('reset', #addAll, #)
#$el.draggable(
handle: ".drag-question-set"
revert: true
)
render: ->
#$el.html(#template(#model.toJSON()))
Update:
The droppable elements fire the callback correctly when I insert the $(draggable.el) into the same container dive as the droppable view. It just doesn't like it when they are in separate html parents...
There's not too much information in your post, but based on your description, if it depends on where in html you put your droppable, you could try replacing your #$el references with just $("#someid") instead. Also, remember that with backbone, the shortcut #$("#someid") is scoped to the backbone view element (some sub element of the global document set in the constructor), not the global document itself. Assuming you get the $("#someid") parts to work, it's most probably just a scoping issue.
I figured it out... Turns out it was a "tolerance" issue with the Droppable plugin. Setting {tolerance: "pointer"} solved my issue.

Backbone Views defined in Head not working unless using Routes

I'm using the "rails-backbone" gem in my Rails 3.2 project. By this design my Backbone Views are loaded in the <head> tag. When using Routes everything works fine, but Views do not render if I try to use them directly without Routes. If I put the Views code in <body> everything works as expected.
Is there a way to make the Views work when defined in <head>?
Update:
in <body>:
<script type="text/javascript">
var lv = new ListView();
</script>
in javascript file included in <head>:
window.ListView = Backbone.View.extend({
el: $("#node"), // This does not work
initialize: function(){
_.bindAll(this, 'render');
this.el = $("#node"); // Works if I add this line
this.render();
},
render: function(){
$(this.el).append("<ul> <li>hello world</li> </ul>");
}
});
As I understand it #node does not exist yet, which is why it is not bound to el. I don't quite understands when happens here: var lv = new ListView(), I thought I was creating an instance from a Class (I guess I'm getting a clone of an Object?). Is there another way of making the code work by modifying the code in <body> instead of the included javascript?
Your problem is caused by using a jQuery selector as the value for el in the view.
Read this:
http://lostechies.com/derickbailey/2011/11/09/backbone-js-object-literals-views-events-jquery-and-el/
if you want something to work in the head, but after the DOM is ready, (And apparently since you're using jQuery) just set up your views in the DOMReady event via:
$(document).ready(function(){
...
//set up view here
...
});

Ember.js + jQuery UI Draggable Clone

I am trying to use Ember.js in conjunction with jQuery UI's draggable functionality, but I am encountering problems. Specifically, when using the clone helper, I am not able to drop the element and everything is extremely laggy. If I don't use the clone helper, everything works as expected.
I suspect this is related to jQuery UI cloning the html, including all of the metamorph script tags (used for binding).
I don't need to update the element live while I am dragging it. Is there a way to strip binding tags with ember?
For reference, here is the view logic:
didInsertElement: ->
#_super()
#$().draggable
cursor: 'hand'
helper: 'clone'
opacity: 0.75
scope: #draggableScope
#$().droppable
activeClass: 'dropActive'
hoverClass: 'dropHover'
drop: #createMatch
scope: #droppableScope
My first thought was to try and use a beginPropertyChanges and endPropertyChanges during the drag to prevent an unexpected behavior. This doesn't seem to work nor is it ideal as I would like other bindings to update. Here is the revised code where I attempted this:
didInsertElement: ->
#_super()
#$().draggable
cursor: 'hand'
helper: 'clone'
opacity: 0.75
scope: #draggableScope
start: ->
Ember.beginPropertyChanges()
stop: ->
Ember.endPropertyChanges()
#$().droppable
activeClass: 'dropActive'
hoverClass: 'dropHover'
drop: #createMatch
scope: #droppableScope
Any help would be greatly appreciated.
I got this working by stripping all ember related metadata manually. Here is a small jquery plugin I whipped up:
# Small extension to create a clone of the element without
# metamorph binding tags and ember metadata
$.fn.extend
safeClone: ->
clone = $(#).clone()
# remove content bindings
clone.find('script[id^=metamorph]').remove()
# remove attr bindings
clone.find('*').each ->
$this = $(#)
$.each $this[0].attributes, (index, attr) ->
return if attr.name.indexOf('data-bindattr') == -1
$this.removeAttr(attr.name)
# remove ember IDs
clone.find('[id^=ember]').removeAttr('id')
clone
To get it to work, just set the helper as follow:
helper: ->
$this.safeClone()
I was having the same issue using Ember 1.0.0 RC6. I've found that just replacing the clone string with a function which returns the clone works fine.
this.$().draggable({
// helper: 'clone'
helper: function() {
return $(this).clone();
}
});
In Coffeescript
#$().draggable
# helper: 'clone'
helper: ->
$(#).clone()

Resources