Add a custom textfield to button_to in addition to data - ruby-on-rails

I have a button_to called 'DELETE' within which I have added
data: { confirm: "Are you sure you want to delete?" }
I also wish to add a custom textfield asking the user for appropriate reason before deletion and then store it consequently. Can I add a textfield inside data apart from the default confirm or disabled option?
I have done it with window.open as of now but it's just a workaround.

You can't add additional fields to the confirmation box. Because it gets only single parameter - message. See here.
I'd recommend to built a custom confirmation dialog for this task.

Firstly, button_to is not the same as data: {confirm....} (although thinking about it, you're probably using data: {...} on the button_to)
If you were using button_to, you could add extra parameters to your form by using the params option:
<%= button_to "Text", your_path, params: {name: "John"} %>
As described in the docs, the params are passed as hidden fields, and thus should be static data (not editable by the user):
Hash of parameters to be rendered as hidden fields within the form.
Since you wish to use data: {confirm ...}, you have to be clear on how it works:
confirm: 'question?' - This will allow the unobtrusive JavaScript driver to prompt with the question specified (in this case, the resulting text would be question?. If the user accepts, the link is processed normally, otherwise no action is taken.
As stated, this loads a "confirm" JS dialogue, which basically only has ok/cancel to determine whether the user wishes to proceed.
You cannot send extra parameters through the standard confirmation dialogue.
--
What you can do is create a custom confirmation action for Rails.
This involves overriding the $.rails.showConfirmationDialog(link); method, so instead of invoking a lowly confirm dialogue, it can show whatever you need.
Here's a gist:
#app/assets/javascripts/application.js
$.rails.allowAction = function(link) {
if (link.data('confirm')) {
$.rails.showConfirmationDialog(link);
return false;
} else {
return true;
}
};
$.rails.confirmed = function(link) {
link.data('confirm', null);
return link.trigger('click');
};
$.rails.showConfirmationDialog = function(link) {
var message, title;
message = link.data('confirm');
title = link.data('title') || 'Warning';
return // ->> your custom action <<-- //
};
We use the following:
#app/assets/javascripts/application.js
var myCustomConfirmBox;
$.rails.allowAction = function(element) {
var answer, message;
message = element.data("confirm");
answer = false;
if (!message) {
return true;
}
if ($.rails.fire(element, "confirm")) {
myCustomConfirmBox(element, message, function() {
var callback, oldAllowAction;
callback = $.rails.fire(element, "confirm:complete", [answer]);
if (callback) {
oldAllowAction = $.rails.allowAction;
$.rails.allowAction = function() {
return true;
};
element.trigger("click");
$.rails.allowAction = oldAllowAction;
}
});
}
return false;
};
myCustomConfirmBox = function(link, message, callback) {
var flash, icon, wrap;
if (!($("flash#confirm").length > 0)) {
icon = document.createElement("i");
icon.className = "fa fa-question-circle";
flash = document.createElement("flash");
flash.setAttribute("id", "confirm");
flash.appendChild(icon);
flash.className = "animated fadeInDown";
flash.innerHTML += message;
wrap = document.getElementById("wrap");
wrap.insertBefore(flash, wrap.childNodes[0]);
return $(document).on("click", "flash#confirm", function() {
return callback(link);
});
}
};
--
If you wanted to pass an extra parameter through this, you'd have to use it in the JS. I've never done it before, but I know you can append it to the query you're sending to your server.
As such, if you update your code to show your routes and controller code, I should be able to come up with an idea on how to pass a param for you.

Related

Select2 AJAX doesn't update when changed programatically

I have a Select2 that fetches its data remotely, but I would also like to set its value programatically. When trying to change it programatically, it updates the value of the select, and Select2 notices the change, but it doesn't update its label.
https://jsfiddle.net/Glutnix/ut6xLnuq/
$('#set-email-manually').click(function(e) {
e.preventDefault();
// THIS DOESN'T WORK PROPERLY!?
$('#user-email-address') // Select2 select box
.empty()
.append('<option selected value="test#test.com">test#test.com</option>');
$('#user-email-address').trigger('change');
});
I've tried a lot of different things, but I can't get it going. I suspect it might be a bug, so have filed an issue on the project.
reading the docs I think maybe you are setting the options in the wrong way, you may use
data: {}
instead of
data, {}
and set the options included inside {} separated by "," like this:
{
option1: value1,
option2: value2
}
so I have changed this part of your code:
$('#user-email-address').select2('data', {
id: 'test#test.com',
label: 'test#test.com'
});
to:
$('#user-email-address').select2({'data': {
id: 'test#test.com',
label: 'test#test.com'
}
});
and the label is updating now.
updated fiddle
hope it helps.
Edit:
I correct myself, it seems like you can pass the data the way you were doing data,{}
the problem is with the data template..
reading the docs again it seems that the data template should be {id, text} while your ajax result is {id, email}, the set manual section does not work since it tries to return the email from an object of {id, text} with no email. so you either need to change your format selection function to return the text as well instead of email only or remap the ajax result.
I prefer remapping the ajax results and go the standard way since this will make your placeholder work as well which is not working at the moment because the placeholder template is {id,text} also it seems.
so I have changed this part of your code:
processResults: function(data, params) {
var payload = {
results: $.map(data, function(item) {
return { id: item.email, text: item.email };
})
};
return payload;
}
and removed these since they are not needed anymore:
templateResult: function(result) {
return result.email;
},
templateSelection: function(selection) {
return selection.email;
}
updated fiddle: updated fiddle
For me, without AJAX worked like this:
var select = $('.user-email-address');
var option = $('<option></option>').
attr('selected', true).
text(event.target.value).
val(event.target.id);
/* insert the option (which is already 'selected'!) into the select */
option.appendTo(select);
/* Let select2 do whatever it likes with this */
select.trigger('change');
Kevin-Brown on GitHub replied and said:
The issue is that your templating methods are not falling back to text if email is not specified. The data objects being passed in should have the text of the <option> tag in the text property.
It turns out the result parameter to these two methods have more data in them than just the AJAX response!
templateResult: function(result) {
console.log('templateResult', result);
return result.email || result.text;
},
templateSelection: function(selection) {
console.log('templateSelection', selection);
return selection.email || selection.id;
},
Here's the fully functional updated fiddle.

Sorting an array and saving to Mongo doesn't seem to trigger reactivity on my page

I have a template that I am loading from a route like so:
this.route('formEdit', {
path: '/admin/form/:_id',
data: function() { return Forms.findOne({_id: this.params._id}); },
onBeforeAction: function() { AccountUtils.authenticationRequired(this, ['ADMIN']); }
});
In which I have a template defined like:
<template name="formEdit">
<div id="formContainer">
...
{{#each header_fields}}
<div class="sortable">
{{> headerFieldViewRow }}
</div>
{{/each}}
</div>
</template>
And:
<template name="headerFieldViewRow">
{{#with header_field}}
...
{{/with}}
</template>
I then make the container around all the header fields sortable using jQuery UI Sortable:
Template.formEdit.rendered = function() {
$('.sortable').sortable({
axis: "y",
stop: function(event, ui) {
var form = Blaze.getData($('#formContainer')[0]);
var newFormHeaders = [];
$('#headerFieldsTable div.headerField').each(function(idx, headerFieldDiv) {
var header = Blaze.getData(headerFieldDiv);
header.sequence = idx;
Meteor.call('saveHeaderField', header);
newFormHeaders.push({header_field_id: header._id});
});
form.header_fields = newFormHeaders;
Meteor.call('saveForm', form);
}
});
}
Basically, when sorting stops, loop through all the headers, getting the data for each and updating the sequence number, then re-build the array in Forms and save them back. In the server code I have printouts for the two save calls, and the do properly print out the correct order of both the headers and the form.
The problem I am running into is that, after sorting, the visual display of the form and it's headers "snaps" back to the pre-sorted state, even though the data in the DB is correct. If I simply reload the form, either by hitting enter in the Address bar or by simply re-loading it from the menu, everything is displayed correctly. It's as if the reactive piece isn't working.
I have noted that I am getting an error when I update the client code in my server log that reads:
=> Client modified -- refreshing
I20141010-18:25:47.017(-4)? Failed to receive keepalive! Exiting.
=> Exited with code: 1
I don't think this is related as I was getting that error prior to adding this sorting code.
Update: Adding code for saveForm and saveHeader
saveForm:
// Saves the Form to the DB
saveForm: function(params) {
// Structure the data to send
var formEntry = {
title: params.title,
client_id: params.client_id,
header_fields: params.header_fields,
form_fields: params.form_fields,
created_by: Meteor.userId(),
created_on: new Date()
};
if (params._id == null) {
console.log("Saving new Form entry: %j", formEntry);
formEntry._id = Forms.insert(formEntry);
} else {
formEntry._id = params._id;
console.log("Updating Form entry: %j", formEntry);
Forms.update({_id: formEntry._id}, formEntry);
}
return formEntry;
}
saveHeader:
// Saves the HeaderField to the DB
saveHeaderField: function(params) {
// Structure the data to send
var headerFieldEntry = {
label: params.label,
field_type: params.field_type,
field_options: params.field_options,
form_id: params.form_id,
required: params.required,
allows_pictures: params.allows_pictures,
record_geo: params.record_geo
};
if (params._id == null) {
console.log("Saving new HeaderField entry: %j", headerFieldEntry);
headerFieldEntry._id = HeaderFields.insert(headerFieldEntry);
} else {
headerFieldEntry._id = params._id;
console.log("Updating HeaderField entry: %j", headerFieldEntry);
HeaderFields.update({_id: headerFieldEntry._id}, headerFieldEntry);
}
return headerFieldEntry;
}
I think the issue here is that Meteor.call will run on the server - you either need to use a callback or invalidate your template, if you want to return a value Meteor.call. From the docs:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
There is more info in this answer and this answer and the Meteor.call docs.
Hope that helps!

How to detect that unobtrusive validation has been successful?

I have this code that triggers when a form is submitted:
$("form").submit(function (e) {
var geocoder = new google.maps.Geocoder();
var address = document.getElementById("Address").value;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("#LatitudeLongitude").val(results[0].geometry.location);
$("form").submit();
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
$('form').unbind('submit');
return false;
});
What it does: it calls google geocoding service to translate an address into latitude/longitude which is set into a hidden field of the form. If there is a result, then the form is submitted.
The problem is that if validation fails (for instance a required field has not been set) then the call to geocoding is still made. Moreover, if I click a second time on the submit button, even if the required field has not been set, the form is posted.
How can I call the geocoding service only if the unobtrusive validation has been successful?
Rather than attaching to the submit() event, you need to capture an earlier event, and exercise control over how to proceed.
First, let's assume your original button has an id of submit and you create a new submit button with an id of startSubmit. Then, hide the original submit button by setting the HTML attribute display="false". Next, bind to the click event of your new button, and add your code, as modified:
$("#startSubmit").live("click", function() {
// check if the form is valid
if ($("form").validate().form()) {
// valid, proceed with geocoding
var geocoder = new google.maps.Geocoder();
var address = $("#Address").val();
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
$("#LatitudeLongitude").val(results[0].geometry.location);
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
// proceed to submit form
$("#submit").click();
}
}
return false;
});
This will invoke validation, so that geocoding will only occur if the form is valid, then, after geocoding has returned a response, it will submit the form by incoking the click event on the submit button.
I implemented something similar (with thanks to cousellorben), in my case I wanted to disable and change the text of the submit button but only on success. Here's my example:
$("#btnSubmit").live("click", function() {
if ($("form").validate().form()) {
$(this).val("Please Wait...").attr("disabled", "disabled");
$("form").submit();
return true;
} else {
return false;
}
});
The only difference is that my example uses a single button.
This answer may help you, it gives a quick example of how a function is called when the form is invalid, based on the use of JQuery Validate, which is what MVC uses.

How to re-trigger a form submit event in jQuery UI dialog callback

I'm trying to use a jQuery UI Dialog for a sort of "soft" validation--if the form looks suspicious, warn the user, but allow them to continue the submission anyway:
// multiple submit buttons...
var whichButton;
$("#myForm").find("[type=submit]").click(function()
{
whichButton = this;
}
var userOkWithIt = false;
$("#myForm").submit(function()
{
if (dataLooksFishy() && !userOkWithIt)
{
$("Are you sure you want to do this?").dialog(
{
buttons:
{
"Yes": function()
{
$(this).dialog("close");
// override check and resubmit form
userOkWithIt = true;
// save submit action on form
$("#myForm").append("<input type='hidden' name='" +
$(whichSubmit).attr("name") + "' value='" +
$(whichSubmit).val() + "'>");
$("#myForm").submit(); /****** Problem *********/
},
"No": function() { $(this).dialog("close"); }
}
});
return false; // always prevent form submission here
} // end data looks fishy
return true; // allow form submission
});
I've checked this out with a bunch of debugging alert statements. The control flow is exactly what I expect. If I first fail dataLooksFishy(), I am presented with the dialog and the method returns false asynchronously.
Clicking "yes" does re-trigger this method, and this time, the method returns true, however, the form does not actually submit...
I'm sure I'm missing a better methodology here, but the main target is to be able to simulate the behavior of the synchronous confirm() with the asynchronous dialog().
If I understand your problem correctly - here's the solution.
(I've separated actions into separate functions (easier to manage)):
submitting the form (normally) would check if there are errors -
dataLooksFishy()
if there are errors - a dialog should pop-up
if user clicks "yes" - form will be submitted with "force=true"
var
form = $("#myForm"),
formSubmit = function(force){
if (dataLooksFishy() && force !== true) return showWarning(); // show warning and prevent submit
else return true; // allow submit
},
showWarning = function(){
$("Are you sure you want to do this?").dialog({ buttons: {
"Yes": function(){ $(this).dialog("close"); formSubmit(true); },
"No": function() { $(this).dialog("close"); }
}});
return false;
},
dataLooksFishy = function(){
return true; // true when there are validation errors
};
// plain JS form submitting (also works if you hit enter in a text field in a form)
form[0].onsubmit = formSubmit;
I couldn't test it with your form as you have not posted it here.
If you have problems with this solution, please post more of your code here and I'll try to help.

.Ajax with jQuery and MVC2

Im trying to create an ajax (post) event that will populate a table in a div on button click.
I have a list of groups, when you click on a group, I would like the table to "disappear" and the members that belong to that group to "appear".
My problem comes up when using jQuery's .ajax...
When I click on the button, it is looking for a controller that doesnt exist, and a controller that is NOT referenced. I am, however, using AREAS (MVC2), and the area is named Member_Select where the controller is named MemberSelect. When I click on the button, I get a 404 stating it cannot find the controller Member_Select. I have examined the link button and it is set to Member_Select when clicked on, but here's the ajax call:
$.ajax({
type: "POST",
url: '/MemberSelect/GetMembersFromGroup',
success: function(html) { $("#groupResults").html(html); }
});
I havent been able to find any examples/help online.
Any thoughts/suggestions/hints would be greatly appreciated.
Thanks!
Have you tried navigating to /MemberSelect/GetMembersFromGroup to see what you get? - if it's 404'ing it's because the route can't be matched to a controller/ action.
I've not used the new areas functionality, but I'm not sure that the URL you've got is correct...I would have thought it would have been /AREANAME/MemberSelect/GetMembersFromGroup...but I could be wrong..!
When I did this, it worked fine. I didn't use POST and I don't know what AREAS means.
$("#item").autocomplete({
source: function(req, responseFn) {
addMessage("search on: '" + req.term + "'<br/>", true);
$.ajax({
url : ajaxUrlBase1 + "GetMatchedCities/" + req.term,
cache : false,
type : "GET", // http method
success : function(msg){
// ajax call has returned
var result = msg;
var a = [];
if (result !== null){
for(var i=0; i < result.length; i++) {
a.push({label: result[i].prop1, id: result[i].prop2});
}
}
responseFn(a);
}
});
}
});
Use:
area_name/controller_name/action_name
Instead of doing $.ajax I would use jQuery Form Plugin.
and have my form set as:
Html.BeginForm("Index","AdminArea/Admin",FormMethod.Post,
new { id="form-user", name="form-user"})
To use jQuery Form Plugin have a look here:
http://arturito.net/2010/12/02/asp-net-mvc2-jquery-form-post-tutorial/
You cold save your url in a Hidden Form element in (Html.HiddenForm()) and use the #id javascript operator to retrieve it. Just found this out today.

Resources