Rails and Javascript - don't mess with the client

I am new to the list and Rails (and Ruby, for that matter) and whilst I was a bit skeptical due to the hype it's causing, I am starting to really like it and there seems to be a really active and friendly community behind it which is an added bonus.

I have been writing web applications for a while now using different languages and frameworks (including hand-written ones just following best practices). I like it how Rails combines a lot of best practices and how its conventions guide the user to writing good applications (which I am convinced is possible on any kind of platform, but you need to know what you're doing, whilst Rails often takes you by the hand and stops you from getting off track).

One thing I am having serious doubts about, though, is the usage of JavaScript in Rails and I would appreciate your comments on my thoughts. I hope this hasn't been discussed before, I did my best to try and search for comments on it, the only thing I did see mentioned was an article by Dan Webb about Ruby being the devil on the client-sides shoulder, but it seems to have been taken off the web.

There is two things I would like to comment on: 1. How Rails creates a mess on the client-side violating the MVC 2. Why I don't see the use of RJS

1. How Rails creates a mess on the client-side violating MVC Whilst this has often been obscured by messy coding practices, you can easily think of the client side of a web application as implementing the MVC-paradigm. M: HTML is the model. Good HTML does not include any presentational information. (e.g. that is why nowadays there is a wide consensus that HTML-tables are not meant to be used for layout) V: CSS is the view. It defines how HTML data is presented on the screen. I admit that a certain part of the presentation is pre-defined by the ordering within the HTML, but stylesheets still allow you to fundamentally shape a page C: JS is the controller. It defines the beaviour of elements on the page and in more advanced applications (especially AJAX) it manipulates the HTML data.

However, HTML allows you to break the paradigm. You can include CSS in your HTML (using style-tags) and you can include JS in your HTML (using onclick, onmouseover etc. event-handler-tags). The fact that MVC is so pervasive on the server side these days seems to indicate that it has some value, though. So you can mess it up on the client side but, as with any best practice, you usually realise after a while that you're better off when you don't. It's become very common practice to keep your CSS out of your HTML. For JS however, there is still a lot of mixing going on as it seems. However, it is easily possible to keep your Javascript entirely out of your HTML. The approach is often termed Unobtrusive JavaScript (UJS). Sounds like a new feature, but essentially it's just doing what should always have been done. There is very good libraries supporting it such as prototype and lowpro (especially its behaviour feature).

Now, coming to Rails: On the server side it provides a very clean implementation of MVC, strictling separating the three components. On the client side, however, Rails creates a considerable mess on the client side by heavily mixing Javascript into HTML (especially promoted by RJS), thus mixing V and C. From the server's point of view HTML, JS and CSS are all part of the view. Fair enough. But from the client's point of view they're not.

An attempt has been made by Luke Redpath and Dan Webb by providing the UJS plugin (http://www.ujs4rails.com/). However, Dan lines out himself that it is a somewhat half-hearted attempt (danwebb.net - The State (And Future) Of The UJS Plugin) The plugin may create cleaner code in the end and may save you band-width by keeping your JS out of your HTML (thus having to download it once only). But to my mind, UJS is above all about programming practice, about clean and maintainable code and the plugin doesn't help that.

2. Why I don't see the use of RJS I have spent a fair amount of time pondering about it, I have read a lot of enthusiastic tutorial, but still I fail to see the use of RJS. Essentially, RJS provides Ruby functions that output some bits of JS code. Such as other functions (for example the form helpers, I hope I am using the right terminology here) output HTML. In terms of HTML that makes sense. HTML is a markup language, not a programming language. There is no way to write functions, makros or whatever in HTML. So if you have a certain complex HTML-construct (e.g. a complex table or a custom control made up of various tags), you would have to write it by hand again and again. So it's useful to have a Ruby function that does the job and reduces the work to a single line of Ruby instead of say 20 lines of HTML. However, JS is a programming language. If there is something complex that you expect to be doing in many places, you can write a Javascript function. So why do I need a Ruby function that renders three lines of Javascript where I can just have a Javascript function that reduces the three lines to one function call. Even more so, why do I need a Ruby function like page.hide that translates to a single JS call Element.hide.

That doesn't make your code DRYer, it doesn't make life easier. Yes, I know, it saves you from writing JS. But JS is not difficult and there are brilliant libraries and it's well worth the effort of learning. Because what price do you pay using RJS the way I see it used most of the time? - It creates an extra layer of complexity. It wraps Javascript with Ruby (in a way you can think of RJS as compiling Ruby to Javascript). - it seems to me that with any serious JS-based application you will quickly run into the limitations of RJS and you will need real JS, anyway (Dan Webb seems to think along similar lines: danwebb.net - RJS Minus R) - when it comes to debugging you will still have to do it on the client-side. You won't recognize your own code, because you wrote it in Ruby, but what you're looking at, now, is JS. Firebug is a very powerful and useful tool for debugging. But you will have to work in Javascript. And you're life will be much easier if your code is well-structured within one JS-file and not spread out all over the place inside HTML, in JS-files and in RJS-files that are downloaded using AJAX on demand. That is a nightmare to debug.

So for the moment, for my first Rails project, my approach will be: I will use Rails for the server side and I won't let it mess with the client-side. Rails belongs to the server and that's where it's brilliant. For the client, there is HTML, JS and CSS already and used properly they can be brilliant in their own way. I will treat JS just as CSS: I will put it in the public folder and write only clean UJS using prototype and lowpro which make it dead easy. I will debug using Firebug on the client side. I am thinking about writing my own version of "javascript_include_tag :defaults". My version would not only include the default scripts but would also check if there is a js-file by the same name as the controller in /public/javascripts. So /app/controllers/user_controller.rb would have a corresponding /public/javascripts/user.js that represents the client-side controller and is included whenever a resource within the user controller is requested.

I am very keen on hearing your comments! Thanks Stefan

1. How Rails creates a mess on the client-side violating MVC Whilst this has often been obscured by messy coding practices, you can easily think of the client side of a web application as implementing the MVC-paradigm. M: HTML is the model. Good HTML does not include any presentational information. (e.g. that is why nowadays there is a wide consensus that HTML-tables are not meant to be used for layout) V: CSS is the view. It defines how HTML data is presented on the screen. I admit that a certain part of the presentation is pre- defined by the ordering within the HTML, but stylesheets still allow you to fundamentally shape a page C: JS is the controller. It defines the beaviour of elements on the page and in more advanced applications (especially AJAX) it manipulates the HTML data.

You're hijacking the MVC pattern. Your argument is essentially this

- MVC is good - I can map M, V, and C onto separate languages used on the client - Therefore, this separation on the client is (or would be) good

I'm not saying that a separation of structure, presentation, and behavior is not good. It can be very good indeed. But this is not because it can be mapped contortedly onto MVC.

MVC is good for reason, namely that is resolves several forces in favorable ways. For your argument to hold, you'd have to demonstrate that these same forces apply to the targets of your MVC and that they are resolved equally favorable.

However, HTML allows you to break the paradigm. You can include CSS in your HTML (using style-tags) and you can include JS in your HTML (using onclick, onmouseover etc. event-handler-tags). The fact that MVC is so pervasive on the server side these days seems to indicate that it has some value, though. So you can mess it up on the client side but, as with any best practice, you usually realise after a while that you're better off when you don't.

You don't have an argument here. It boilds down to praise by association. Because MVC is good "there", doesn't imply that it is good elsewhere.

Now, coming to Rails: On the server side it provides a very clean implementation of MVC, strictling separating the three components. On the client side, however, Rails creates a considerable mess on the client side by heavily mixing Javascript into HTML (especially promoted by RJS), thus mixing V and C.

You're missing one essential point. You fail to distinguish between between structure/presentation/behavior as they exist in views and HTML/CSS/JS as they are or not mixed-up in generated web pages.

This distinction is crucial. And it is, of course, a Rails best practice, to keep behavior out of views and put it in helpers or controllers. At its root, this is just separation of concerns.

Your complaint about Rails apparently boils down to the fact that the "compiled" output mixes concerns. There are independent arguments why this may not be good, but you don't touch them.

2. Why I don't see the use of RJS I have spent a fair amount of time pondering about it, I have read a lot of enthusiastic tutorial, but still I fail to see the use of RJS.

I think you're discarding the enthusiasm of others too easily. You seem to imply that they are misguided in seeing a use in RJS when you don't.

Essentially, RJS provides Ruby functions that output some bits of JS code.

[...]

Javascript function. So why do I need a Ruby function that renders three lines of Javascript where I can just have a Javascript function that reduces the three lines to one function call. Even more so, why do I need a Ruby function like page.hide that translates to a single JS call Element.hide.

I think the answer is obvious: Because it is a Ruby function, not a JS function.

That doesn't make your code DRYer, it doesn't make life easier.

Well, it makes life immensely easier for those Rails developers who don't know JavaScript.

Yes, I know, it saves you from writing JS. But JS is not difficult and there are brilliant libraries and it's well worth the effort of learning. Because what price do you pay using RJS the way I see it used most of the time?

So, all Rails developers have to learn JS because it is easy and simply relying on their framework to do it's part is somehow making them pay a price that they don't perceive.

- It creates an extra layer of complexity. It wraps Javascript with Ruby (in a way you can think of RJS as compiling Ruby to Javascript).

For those who don't know JS, it is a layer of simplicity.

- it seems to me that with any serious JS-based application you will quickly run into the limitations of RJS and you will need real JS, anyway (Dan Webb seems to think along similar lines: http:// www.danwebb.net/2006/11/17/rjs-minus-r)

Don't do a "serious JS-based application" with RJS. That's no-brainer. Don't do anything that seriously involves X unless you know X well enough.

- when it comes to debugging you will still have to do it on the client-side.

The beauty is that for most common uses of RJS you rarerly ever have to debug. If someone runs into this regularly, they are using RJS well out of its scope. The others may go ahead.

I am very keen on hearing your comments! Thanks

I'm quite sure that what I wrote is not what you'd hope to hear. I'm adversarial on purpose because I agree with some of your conclusions, but I'm convinced that your arguments don't pull their weight there.

First and foremost, your arguments suffer from absolutism. You imply that because something is not good for your purpose it can't be good for any purpose. In the case of RJS this is patently not true.

Regarding unobtrusive JavaScript, you'd have to argue what tangible advantages it brings for developers or users. Prima facie, it makes thinks more contorted for developers, for they can't just attach behavior where it applies. Maybe this can be offset by improved accessibility and graceful degradation, but these don't come free even with unobtrusive JS.

For the record, I know JS fairly well and like to use it. But I recognize this as a minority position among software developers (aka backend programmers).

Michael

I did not replace javascript_include_tag, but I do use a global array called @js_includes, and in my layout, I do something like

<% if @js_includes %>   <% @js_includes.each do |js| %>     <%= javascript_include_tag js %>   <% end %> <% end %>

Then in my controller, I will do one of two things:

1) for js that applies to certain actions, include the js in the action with

def my_action   @js_includes = ['my_js']   ... end

2) if the js applies to the whole controller, write a before_filter

before_filter :set_includes

private def set_includes   @js_includes = ['my_js'] end

[Hm, now that I think about it, I should probably use the before_filter all the time and tack on :only or :except. That'd be a bit more DRY.]

I have not needed the flexibility yet, but I could also adjust my assignment lines

@js_includes = ['my_js']

to be

@js_includes << 'my_js' unless @js_includes.include?('my_js')

I have done the same thing for CSS, but as I learn more about CSS, I'm finding that this approach it is not as necessary.

Peace, Phillip

I think you're discarding the enthusiasm of others too easily. You seem to imply that they are misguided in seeing a use in RJS when you don't.

It seems like I didn't manage to get my point across. What I was hoping to express is that I can see the enthusiasm, but do not understand it, so I am thinking there might be a point that I am missing. That's why I wrote the post after all. If it was just to complain I could have saved my time and do things differently myself, after all, I do have an approach that I am happy with myself for the moment.

But when so many people think it's a good thing, it a least deserves to be looked at more deeply, I think. So I was hoping for people to contradict and - unlike your expectation - your post was very much what I was hoping for and I hope there will be others.

And if you are telling me that one of the main reasons behind RJS is that people prefer to not write JS, then that explains the enthusiasm and it helps me in taking my own decision that I personally don't need RJS. So honestly thanks for that!

I am not trying to say MVC is good, because it's MVC. It's the same reasoning as above: I see it used very widely by many people, I have used it and until now always to my advantage. What we do and think is a result of our experience, I guess. So far I have made good experiences with MVC, so I believe it's a good thing. I haven't heard strong arguments against it yet, but if you have any, I am more than happy to hear them. I am keeping my JS out of my HTML, because it makes it easier to reuse code, I can apply the same behaviour to different markup. I can simply link in a JS file to an HTML file without having to touch any of the markup at all. I can even move my JS between projects using different platforms. It doesn't matter whether the server part of my web app runs on php, rails, some java framework or whatever. They can all statically serve the JS file and it will work as long as they produce the same markup. Also it makes it easier to debug using Firebug when I have a single clean JS-file and don't have to jump around between HTML-tags and JS- files. Finally I like it when things are cleanly separated.

but I'm convinced that your arguments don't pull their weight there.

Same goes here. If you do have any arguments, it'd be nice of you to share them. It's exactly these arguments that I was hoping for.

First and foremost, your arguments suffer from absolutism.

Only for the sake of simplicity (of my statements) and to call for counter-arguments. Maybe I overdid it a little this time. Thanks for replying in spite of that, Michael!

Stefan

Thanks for sharing your approach Phillip. I might use that, too, and it'll save me overwriting js_include_tag.

stefan

I am not trying to say MVC is good, because it's MVC. It's the same reasoning as above: I see it used very widely by many people, I have used it and until now always to my advantage. What we do and think is a result of our experience, I guess. So far I have made good experiences with MVC, so I believe it's a good thing. I haven't heard strong arguments against it yet, but if you have any, I am more than happy to hear them. I am keeping my JS out of my HTML, because it makes it easier to reuse code, I can apply the same behaviour to different markup. I can simply link in a JS file to an HTML file without having to touch any of the markup at all. I can even move my JS between projects using different platforms. It doesn't matter whether the server part of my web app runs on php, rails, some java framework or whatever. They can all statically serve the JS file and it will work as long as they produce the same markup.

These are all good reasons, but they have nothing to do with MVC. Call it separation of concerns, if you will. In true MVC, the model has a very active role, it encapsulates state and its associated behavior. Plain HTML markup is lifeless structure. If you insist on making it into a model, you can add model behavior to it using JavaScript. Then, on top of that, you can add presentational behavior and call it controller or presenter if you go for MVP.

JavaScript in itself is not "the controller". It's a means of implementing behavior. The interesting distinction is whether this behavior is part of the business logic (model) or presentational (view/controller).

Also it makes it easier to debug using Firebug when I have a single clean JS-file and don't have to jump around between HTML-tags and JS- files. Finally I like it when things are cleanly separated.

When you insist that everything you're doing is so very clean, you're implying that what others do differently is in some way dirty. Think of it.

> but I'm convinced that your arguments don't pull their weight > there.

Same goes here. If you do have any arguments, it'd be nice of you to share them. It's exactly these arguments that I was hoping for.

Please read again what I wrote previously. Consider my comparison of generated HTML/JS with the output of a compiler. Consider how state, behavior, and presentation are separated in the representation the developer is working on (models, view, controllers, helpers, ...) and how they are separated -- or not -- in the result generated by a template engine. I say that it is the separation of concerns in the source representation that counts. You're claiming that it has to be present in the generated result.

If you don't appreciate the distinction I make in the previous paragraph, it won't make much sense for me to continue the discussion.

Your argument for separation of concerns in the output is that it gives you better modularity:

I am keeping my JS out of my HTML, because it makes it easier to reuse code, I can apply the same behaviour to different markup. I can simply link in a JS file to an HTML file without having to touch any of the markup at all. I can even move my JS between projects using different platforms. It doesn't matter whether the server part of my web app runs on php, rails, some java framework or whatever. They can all statically serve the JS file and it will work as long as they produce the same markup.

That's fair enough. Still, I can have the same advantages even if I'm using RJS and other practices you abhor. Nothing keeps me from using highly decoupled, unobtrusive scripts on top of that. But when it comes to application-specific scripting, I'd like to use all the tools made available by the framework, including RJS. No, I won't port that app to PHP, certainly not.

Michael

Michael,

I seem to be offending you in some way I cannot clearly grasp. I seem to be giving you the impression that by stating my point of view I was ruling out all others. From what I write you seem to read that me having my own preferences somehow implies that I consider other people's preferences as bad. Is that it?

I can only repeat that none of this is intended, but I am not sure that will convince you, since I already tried to express that in my last mail.

However, by now, I myself feel rather intimidated and judged unfairly by your replies. I don't feel like I was given a chance. I don't feel encouraged to keep on voicing my opinions (which I consider strictly my opinions and that don't imply any deprecation of other people's opinions). What can I say, if to you anything I do say seems to be short-sighted, unjustified judgments or suffering from absolutism?

I talk about what I, Stefan, personally think, independently of other people's opinions and without meaning to disqualify them. You repeatedly judge and interpret my statements stating intentions that you can merely guess. You do so using a rather forceful language: "if you _insist_...", "if you _don't appreciate_..." "you're implying that what others do differently is in some way dirty" (very clearly: no, I'm not, I never said so and never meant to, it's your interpretation)

I don't feel my views respected, I feel judged unfairly and I feel helpless trying to convince you that I am not intending what you are perceiving. I cannot say where the problem lies, I tried, but it seems I can't express myself clearly enough in this case. I don't feel this is good grounds for further discussion, although I was hoping it would be possible, as I really appreciated your initial reply.

Thank you for taking the time Stefan

Most of the time I'm a really nice person and I didn't know that I could be intimidating at all. That I'm responding rather forcefully has a very specific reason: Our positions are probably not very far apart, but I think you're doing it/them a disservice.

A case in point is your MVC analogy applied to HTML/CSS/JS. As I tried to show before, it does not get off the ground. However, I do think that there are good arguments for separation of structure/presentation/behavior on the client-side *if* one is working at that level. I'm not sure, and open to arguments either way, whether the same forces and solutions apply at the higher level of Rails views.

If you'd like to discuss something less loaded, and as you are a JavaScript afficionado, I'd appreciate your comments (in the proper place) on this http://schuerig.de/michael/blog/index.php/2007/12/22/javascript-fsm/ (yes, I'm a bit self-promotional here).

Michael

And I thought it was going to be a javascript flying spaghetti monster! What was I thinking?

-Corey

You... pirate! You!

Arrrr, Michael

Stefan,

I largely agree with your points. The first point is addressed with the new prototype library that is included with Rails 2.0, it provides event observers that allow you to separate out the JS code from your HTML entirely. (I would rather call it separation of concerns then an MVC pattern to avoid confusion). Some of the rails helpers still spew JS in your HTML but if that bothers you, you should be able to roll you own.

The second point has also been a bit of a mystery to me: I haven't come across anyone who actually uses RJS. All advanced web programmers I know hand code their JS and also would not want to give up their Firebug. If anyone has used it to good effect I'd love to hear about it.

Cheers, Jan

Hi Stefan,

Strongly agree with your point 2: one vote for JS here :slight_smile:

Though I'm not a master on JS, I found it really easier to write JS directly than use RJS when I tried Rails the first time. Learning the use/api of RJS won't be obviously simpler than learning some JS.

And in Rails 2.0, we have a new powerful tool .js.erb!

Thanks, Jan

Hi Jan + Jan,

thanks for both your views.

To FJan: Event observers in prototype? You're talking about the prototype Event object? (I'm asking, because I thought that it has been around for a while, but there might be something new that I overlooked).

To Jan: Googling on .js.erb didn't come up with a lot of hits. I take it this is Ruby embedded in JS along the lines of Dan Webb's MinusR plugin?

To both of you: If you are using a lot of JS, what structure do you use? Something like Phillip wrote earlier? One JS file per controller? How do you serve/include your JS? For the moment, I like Phillip's approach, maybe with an added convention that if there is a JS-file by the name as the controller then it is included automatically without having to be added to Phillip's global array.

Thanks Stefan

For the logical structure, I suggest a separation into generic/framework code. Put generic code into an application object.

var Application = {   ... };

Then for each resource have an object or class. For example

var User = {   ... };

or

var User = Class.create({   ... });

Have these specific objects/classes be initialized/instantiated appropriately. How? Two possibilities: Arrange it so that only the relevant files are loaded per controller or write config information in a script block in the header.

Michael

The reason I use a global array is to have a finer level of control over what gets loaded when. I don't always do things the "proper" way, so I will mix administrative actions in a controller with non-administrative ones, then control access to them by a :require_admin before_filter. I might be naive, but so far this has been working out very well for me. In that situation, I might have some Javascript that is specific to the administrative functions, so I don't want it loaded if the user/action is not an admin. There might be another way to accomplish this, but it has worked out well for me to use the global array and assign the specific js or two action by action.

On the other hand, if my JS applies to essentially all of the actions in a controller, I can see how it would be DRYer to have a construct that will automatically load a .js file with the name of the controller. If for no other reason than to know how to do it, I think will see if I can make that happen.

Thanks for sharing your thoughts, Stefan.

Peace, Phillip

To Michael G: if what you mean by autocompleter is something like a date field that completes automatically, then here's your example. If you are talking about something more complex like a field that offers a list of suggested completions that is downloaded from the server via AJAX, I think you will still get the idea which is the same.

Your HTML might be <input id="date" type="text" value="Enter date here..." />

Then you could add some behaviour unobtrusively like this (providing you use prototype and it's Event object, but the idea is independent of it):

var input = document.getElementById("date");

Event.observe(input, 'focus', function() {   if (this.value == "Enter date here...") this.value = ""; });

Event.observe(input,'change',function() {   // some code to check that this.value is a valid date and autocomplete it });

Event.observe(input, 'blur', function() {   if (this.value == "") this.value = "Enter date here..."; });

I hope this is enough of an example to give you an idea. You can also go one step further and use Dan Webb's behaviour code in lowpro. It allows you to attach a behaviour to a CSS class rather than to an individual id as above.

That way you could attach a behaviour to a CSS-class 'date' and then add as many <input type="text" class="date"/> as you wanted to your page and they would all expose the same behaviour without any extra JS.

To Michael S:

Have these specific objects/classes be initialized/instantiated appropriately. How? Two possibilities: Arrange it so that only the relevant files are loaded per controller or write config information in a script block in the header.

Possibility one is understood. Can you give an example for possibility two, I am not sure I understand. How would you write that config information so that only those objects/classes that are needed are loaded from the server. Thanks.

To Phillip: Thanks for the explanation. As far as implementation is concerned, I was thinking that it should be fairly easy. I am not fluent enough in RoR yet to provide a piece of code, but it should be possible in the layout to get the name of the controller class? Then I would just use your snippet and add an if-block that checks if there is a js-file by the same name in /public/javascripts and if there is, include it. If not, nothing. That keeps you flexible, no need to create a js-file for each controller, but if you want to you can, without having to add extra code.

I guess, it should also be possible to do the whole thing using .js.erb instead of plain .js, so if you like you can use the same structure and still embedd ruby into your javascript. Which just brings me to a new idea: You could then have a ruby function like "include_js" that you can call from your .js.erb and that allows you to include further js-files (the function just adds them to your global array that is then rendered in the layout just as you do). I think I might like that, because that way javascript files are included where they are used. I have always thought that it's not very pretty to have to include your JS (be it libraries or just some of your own objects you're making use of...) inside the HTML file whilst they're being used inside other JS files. And as far as I know, unlike CSS, JS doesn't allow any includes or imports, so in that case .js.erb would really provide an extra feature, even for those like me who like their plain JS.

Stefan

That's not what I was trying to say. You could use one of the available JS dependency mechanisms to load only the files that are needed; as far as I can tell, these mechanisms all involve adding script elements to the document head.

However, I had in mind something simpler entirely. For the kind of apps I've been working on most of the time, I usually don't have a collection of loosely coupled scripts adding this or that behavior to a page. Rather, I have some fairly general framework code + application-/resource-specific code + configuration.

I don't have a good example handy, but have a look at this page

http://www.schuerig.de/michael/webdesign/demo/form.html

and its linked scripts, in particular

http://www.schuerig.de/michael/webdesign/demo/javascripts/boilerplate.js

and in there the last few lines of BoilerPlate#baseInitialize.

Michael

Very keen. I express the same qualms as you regarding rails generated javascript. SO, in the end I just write the JS myself. It's actually a pretty cool language!

Stefan Klein wrote: