Rails + Haml: Returning from a partial will somehow remove the form tag - ruby

This is lengthy, yes, I apologize. But I want to be clear because this issue is so odd.
I have a terms of service modal that comes up whenever a user has not accepted our terms of service (TOS). For the purposes here, javascript is turned off and what is rendered is strictly html/css. So the page, if thought of in 3 layers, is: bottom layer = page they came to see (i.e. an event page), middle layer: a semi-opaque overlay, top (primary/visible) layer: a modal with the terms of service agreement form in it. This form is simply an accept checkbox and submit button.
I noticed that the TOS was not working properly, but at seemingly random times. Then I noticed that it was broken but only on my event page (/event/foo) even though the same partial is responsible for showing the terms of service agreement no matter where they show up on the site. So for any other page, like /group/bar, the same TOS modal would show up and would work fine.
I then realized that the problem was that the form tag was missing from my HTML! Just gone.
So, taking a step back, the (HAML) code in question is simply:
%div#accept_tosC
%b Before form_for
- form_for #current_user do |form|
%b After form_for
%div#tosC= render :partial => 'general/terms'
%div.left
= render :partial => 'shared/user/tos_form_element'
%div.right
= image_submit_tag "/images/buttons/submit_100x20.png", :id => 'submit', :name => 'submit'
For our /events/foo page, the generated HTML look like this:
<div id="accept_tosC">
<b>before form_for</b>
<div style="margin: 0pt; padding: 0pt;"><input type="hidden" value="put" name="_method"><input type="hidden" value="44c2bf7a64fc59baa3fc7129167f0e2c3e96abb6" name="authenticity_token"></div>
<b>after form_for</b>
The obvious question here is if 'Before form_for' and 'After form_for' make it into the document, why doens't the form tag form_for creates?
For a different page, say /groups/foo, we get exactly what we would expect:
<div id="accept_tosC">
<b>before form_for</b>
<form method="post" id="edit_user_595" class="edit_user" action="/users/595"><div style="margin: 0pt; padding: 0pt;"><input type="hidden" value="put" name="_method"><input type="hidden" value="44c2bf7a64fc59baa3fc7129167f0e2c3e96abb6" name="authenticity_token"></div>
<b>after form_for</b>
I tracked it down to a single partial well inside the code for the "bottom" layer (the page they requested, not the TOS overlay). This partial in question may or may not be viewable to any given individual so we have to check if the user can view this page. The result of that is held in the variable can_view:
:ruby
#some processing to set page info and can_view
return unless can_view
%div#statsC
...and so on...
This is what my code looked like and the form tag did not render. By making the following change, the form element tag showed up as expected for all pages:
:ruby
#some processing to set page info and can_view
- if can_view
%div#statsC
...and so on...
So here is the question: Why would returning from a partial prevent a form element tag from becoming part of the document?

The short answer is that templates work in mysterious ways, and return is just generally not safe to use within them.
The long answer is that, behind the scenes, templates are compiled into Ruby methods that build up a string of HTML code and return that code. I'm guessing that by returning without returning the proper code, you're making something go haywire and discard the string that the form_for is concatenating to.

Related

How to get a Sinatra erb to report back its referring page in params

For learning purposes, I am developing a Ruby/Sinatra "to do list" program. In views, I have the usual index page, which lists uncompleted tasks, and also a completed page. On both pages (and on many future user-created categories pages), there is a similar table, with similar methods for moving and deleting items. These call the same methods, or I'd like them to. But if they call the same methods, at the end of those methods the user is redirected to the index view. But if users are on the completed page, they want to stay on that page after deleting an item (or whatever).
To solve this problem, I think what I need to do is pass a variable from the erb file to the route block, reporting what view (erb file) the user was most recently on. I thought there would be some built-in Sinatra methods to report this, and I looked hard (e.g., I tried all plausible-sounding ways of accessing the request object), but I couldn't find any. Then I tried using <input type="hidden" name="pg_type" value="completed"> but for reasons totally mysterious to me, this doesn't work (maybe because Sinatra uses type="hidden" to route PUT and DELETE requests?). When I inspect the parameters with p params, I see that the pg_type param was created, but its content is an empty string ("").
An example form from (this is a template used by the other views) task_table.erb is this:
<form method="post" action="/delete/<%= task.id %>">
<label for="delete_button">
<input type="hidden" name="pg_type" value="<% #pg_type %>">
<button type="submit" name="delete">Delete</button></label>
</form>
Here is the route block for that method:
post('/delete/:id') do
store.delete(params[:id].to_i)
session[:message] << " " + "Deleted task!"
redirect "/" if params[:pg_type] == "index"
redirect params[:pg_type] # But params[:pg_type] always == "" :-(
end
So how can a pass info about the referring page from my erb pages to my route blocks? Or, if my strategy for solving the problem of returning the user to the correct page is wrong or could be improved (I wouldn't be surprised), please explain.
I'd love to have any more detailed feedback on this program. Other problems I'm trying to solve are how to add unit tests of put and delete methods and how to add user accounts (not started with that though).
In your code you forgot a '=' before #pg_type:
<input type="hidden" name="pg_type" value="<%= #pg_type %>">

Grails formRemote redirects rather than to just call method

I'm new to Grails and got some problem with the g:formRemote command..
I want a g:textArea box send a message to my controller and save this messages.
After that the page should be updated via the formRemote Ajax, so that the messages appear on the page.
But instead of updating the page, the formRemote call assumes the given url to be a real link and wants me to redirect to this (non-existing) .jsp site.
The Method I want to start is called in my controller tho
I tried many solutions offered in similar problems, but it seems this problem is different from theirs
Heres the code:
<div id="history">
<g:render template="posts" collection="${ messages }" var="message" />
</div>
<div class="postMessageForm">
<g:formRemote name="postChatMessage" url="[controller: 'meetingRoom',
action: 'postMessage']" update="history">
<div class="msg_box">
<g:textArea name="message" value="" style="width: 630px"/><br/>
</div>
<div style="float: right;">
<g:submitButton name="Send" style="width: 90px; height: 40px;"/>
</div>
</g:formRemote>
</div>
and this is the Action which is called in my MeetingRoomController:
def postMessage() {
if (params.message != "") {
def thisUser = lookUpUser()
def thisRoom = thisUser.joinedRoom
def chatPost = new ChatPost(
message: params.message,
author: thisUser
)
thisRoom.addToChatHistory(chatPost)
}
// def messages = currentChatHistory()
// render template: 'posts', collection: messages, var: 'message'
I saw this kind of approach in Jeff Browns Twitter tutorial.
Possible failures i am seeing:
the out-commented render template command has something to do with the Ajax (When I do not comment it the only thing that happens is that the template posts will be rendered on the redirected page
usage of both Ajax and jQuery (But i dont believe that can be the point because I just have used g: and groovy stuff and havent even imported a jQuery lib)
this could be easier with remoteFunction (I dont really know how to get the remoteFunction work in this case tho)
I hope this information is enough to let someone see what I am missing
When the submit button is clicked on your form, the data is sent to the method listed in the url parameter of the formRemote tag. Then you are inside that method, you get to the commented out render tag that outputs data back to the gsp page in the div mentioned in the update tag of the formRemote tag.
formRemote relies upon a javascript library to handle the ajax stuff as mentioned in the grails documentation:
7.7.1 Ajax Support
By default Grails ships with the jQuery library, but through the
Plugin system provides support for other frameworks such as Prototype,
Dojo:http://dojotoolkit.org/, Yahoo UI:http://developer.yahoo.com/yui/
and the Google Web Toolkit. This section covers Grails' support for
Ajax in general. To get started, add this line to the tag of
your page:
You can replace jQuery with any
other library supplied by a plugin you have installed. This works
because of Grails' support for adaptive tag libraries. Thanks to
Grails' plugin system there is support for a number of different Ajax
libraries including (but not limited to):
jQuery Prototype Dojo YUI MooTools
So remove what is in the history div, uncomment the two lines in your postMessage method, and include one of the referenced javascript libraries.

Rhomobile back function

I'm having a problem getting the back button function to work in Rhomobile.
I've tried various methods of url_for(:index, :back => ....) etc etc and nothing seems to work. The problem with this method is (even if it worked) that it only allows navigation to a set place, rather than a dynamic history/back navigation.
The closest I've come to a working solution is by using this in the application_helper:
def page_back
WebView.navigate_back
end
and then Back in the view.
This works, and i can navigate across views and even controllers. However, it generates a "Error loading page" error, even though it does actually render the right page...
Does anybody have any ideas?
Ok this is what I did in the end. I've decided against using rhodes now but here is what I came up with for this problem:
Added data-add-back-btn="true" to:
<div data-role="page" data-add-back-btn="true">
Then:
<div data-role="header" data-position="inline">
<h1>Title</h1>
Back
</div>

EditorFor can't post MVC 3

I've come across a peculiarity with EditorFor() helper in MVC 3.
I have a form view that is strongly type (trimmed down):
#model GoGoLegal.Models.Address
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div>
#Html.EditorFor(model => model)
</div>
<div>
<input type="submit" value="Post" />
</div>
}
and I have an Address EditorTemplate.
When the Input Button is click without anything in the fields then Validation gets thrown, however when there are values in the fields and the Input Button is clicked then nothing happens.. at all... It doesn't hit the controller, both HttpWatch and FireBug don't even register the event. I'm wondering whats happen.
I've also tried to replace
#Html.EditorFor(model => model)
with
#Html.EditorForModel(Model)
And still the same thing.
Any thoughts on this?
Have you tried clearing your cache. Sometimes a cached version of the page gets confused. Try Ctrl-F5 after you've loaded the page, then see if the page posts.
Also, look at your code and make sure there aren't any malformed tags. That can also confuse the browser, maybe you don't close everything.
Also check the actual HTML in the browser with a "View source" and see if the HTML looks correct.

Firefox 3.5.2 Refresh(F5) causes Highlighted Form value to get copied to next field

I am having a strange issue in Firefox 3.5.2 with F5 refresh.
Basically, when I focus on an input field and hit f5 the contents of that input field gets copied to the next form field after the F5 refresh.
But, if you inspect the HTML source code, the values are correctly loaded.
I am not having this issue in IE8 or Safari 4.0.3.
The problem does not occur if I do a hard refresh or run window.location.refresh(true).
After F5 Refresh: http://i805.photobucket.com/albums/yy339/abepark/after.jpg
Here's an overview of what's going on.
I believe the thing you should look into is the autocomplete attribute,
you should set it to off on the input box. However be careful since this will trigger two effects.
When you refresh the page it won't remember the old values
The default dropdown of the already used values on that input box will also be disabled.
If you want to keep the second behavior you should set the autocomplete attribute back to on with JS.
Browsers can remember form field contents over a refresh. This can really throw your scripting off if it is relying on the initial value of a field matching what's in the HTML. You could try to prevent it by calling form.reset() at the start.
Different browsers have different strategies for detecting when a form or a field is the same as in the previous page. If you have clashing names, or names that change on reload, it is very possible to end up confusing them. Would have to see some code to work it out for sure.
In the backend, I am using ASP.NET MVC 1.0 with the Spark View engine. When I examine the source code after an F5 refresh in Firefox 3.5.2, the page renders correctly; however, if you look at the page visually the adjacent form field field gets populated with the value from the previous field.
I included enough code so you can just get an idea of what I'm trying to do.
Again, the rendering is fine and the final view/HTML code is fine. It's what I see on the screen that is incorrect. I am using hidden vars; but the issue occurred before using it as well.
Note in the code below, I have 2 distinct ID fields: "date_{projectTask.ProjectTaskId}" and "finishDate_{projectTask.ProjectTaskId}, which gets renders to something like "date_1" and "finishDate_2".
<table>
<for each="ProjectTask projectTask in projectTasksByProjectPhase">
<input type="hidden" value="${projectTask.ProjectTaskId}" />
<tr>
<td class="date">
<div class="box">
<div class="datefield">
<input type="text" id="date_${projectTask.ProjectTaskId}" value="${startDate}" /><button type="button" id="show_${projectTask.ProjectTaskId}" title="Show Calendar"><img src="~/Content/yui/assets/calbtn.gif" width="18" height="18" alt="Calendar" ></button>
</div>
</div>
</td>
<td>
<div class="box">
<div class="datefield">
<input type="text" id="finishDate_${projectTask.ProjectTaskId}" value="${finishDate}" /><button type="button" id="finishShow_${projectTask.ProjectTaskId}" title="Show Calendar"><img src="~/Content/yui/assets/calbtn.gif" width="18" height="18" alt="Calendar" ></button>
</div>
</div>
</td>
</tr>
</for>
</table>
FYI: ${} are used to output variables in the Spark View engine.
I am also using the YUI 2.7 Connection to make Ajax calls to update the datebase for "change" and "enter/tab key press" events. I am able to verify that the AJAX calls are made correctly and the form field values are still valid.
The problem occurs when I just do a F5 refresh; for some reason, the "finishDate_1" gets populated with the value from "date_1".
This problem occurs just by clicking on "date_1" and hitting F5; so, the adjacent field just gets populated even if there are no AJAX calls.
Here's the Javascript code I call towards the end of the body"
YAHOO.util.Event.onDOMReady(
function() {
var idList = YAHOO.util.Dom.getElementsBy(function (el) { return (el.type == 'hidden'); }, 'input');
len = idList.length;
var startDatePickers = new Array();
var finishDatePickers = new Array();
for (var i = 0; i < len; i++) {
var id = idList[i].value
startDatePickers[i] = new DatePicker("date_" + id, "show_" + id, "cal_" + id);
startDatePickers[i].valueChanged.subscribe(updateDate, 'S');
finishDatePickers[i] = new DatePicker("finishDate_" + id, "finishShow_" + id, "finishCal_" + id);
finishDatePickers[i].valueChanged.subscribe(updateDate, 'F');
}
}
}
The form field gets copied over before any Javascript code is processed because I call the Javascript code towards the end of the body after all HTML is rendered. So, I'm guessing it's a refresh issue in Firefox? What do you guys think?
As you can see above, I created my own calender date picker objects which allows you to either enter the date in the text manually or by clicking on a button to view the calendar and select a date. Once you enter or select the date, an AJAX call will be made to update the datebase in the back end.
Thanks everybody for the quick responses.
#Anonymous: whoever you are, you are awesome!
#bobince: thanks for the feedback as well.
I added a dummy form tag with the attribute autocomplete="off" and that solved the problem!
I was scratching my head because I didn't get this issue in Safari 4.0.3 or Internet Explorer 8.
<form action="" autcomplete="off">
<!-- my code -->
</form>
The values were loading correctly in the back end (ASP.NET MVC 1.0/Spark View engine) and the HTML source code reflected this, but the input field values were not getting populated correctly. I was using the YUI Connection Manager and Javascript to support edit-in-place and the date pickers.
I tried changing the XHR call to a GET call instead of POST and the same issue was happening.
Anyway, the problem was that the Firefox was not setting the correct values for the input fields for F5 refreshes.
Again, thanks so much! You guys rock!
All element id's must be unique, if two elements have same id's then that could be reason why Firefox inserts same values to elments that didn't orginally have those values entered.
I had a similar problem related to my question at Input control shows incorrect value, even 'though inspect element shows the right value is there
The problem occurred for me in Firefox, but not Chrome, for some but not all controls on the form, and when I pressed F5, but not ctrl-F5.
The "dummy form" seems to have resolved it for me.

Resources