Does JavaScript get excuted when partial view is loaded? - javascript

Background
I'm working with ASP.NET MVC. I've got a partial view which contains JavaScript. I'm using AJAX get to load the partial view into a <div> tag. The JavaScript registers a click event for a group of radio buttons.
Problem
It doesn't seem to be executing: when the radio buttons are clicked, the form doesn't get submitted.
Here is my partial view:
<% using (Ajax.BeginForm(ActionName.Approve, ControllerName.Supervisor, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "Result"}, new { id = "IsSupervisorApprovalRequiredForm" }))
{%>
<p>Is supervisor approval required?</p>
<label for="IsSupervisorApprovalRequired">Yes</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "0", new { #class = "IsSupervisorApprovalRequiredYes" })%>
<label for="IsSupervisorApprovalRequired">No</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "1", new { #class = "IsSupervisorApprovalRequiredNo" })%>
<%} %>
<script type="text/javascript">
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
</script>
Question
Does JavaScript get executed when partial view is loaded?

Yes and no. The order of execution in your scenario is as follows:
Page gets requested
ASP.NET Renders Partial View into the parent page
Javascript gets executed on that entire page
For your particular problem. You'll need to load that Javascript snippet on page load before it can actually bound to the events. Your code should look like the following:
<% using (Ajax.BeginForm(ActionName.Approve, ControllerName.Supervisor, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "Result"}, new { id = "IsSupervisorApprovalRequiredForm" }))
{%>
<p>Is supervisor approval required?</p>
<label for="IsSupervisorApprovalRequired">Yes</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "0", new { #class = "IsSupervisorApprovalRequiredYes" })%>
<label for="IsSupervisorApprovalRequired">No</label><%=Html.RadioButton("IsSupervisorApprovalRequired", "1", new { #class = "IsSupervisorApprovalRequiredNo" })%>
<%} %>
<script type="text/javascript">
$(function() {
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
});
</script>

Wrap the statement in $(function() {...}); so it will get called when the document is ready.
So it would look something like this:
$(function() {
$("#IsSupervisorApprovalRequired").click(function() {
$("form#IsSupervisorApprovalRequiredForm").submit();
});
});

This might also be caused by the HTML generated by the HtmlHelper. Multiple HTML elements with the same ID are not allowed, but the helper will generate something like:
<input id="IsSupervisorApprovalRequired" name="IsSupervisorApprovalRequired" type="radio" />
<input id="IsSupervisorApprovalRequired" name="IsSupervisorApprovalRequired" type="radio" />
As a result, when you match "#IsSupervisorApprovalRequired" with jQuery, it's looking for an element with that ID. Since two of them exist, the function will only be bound to the first one, causing the second radio button's "click" event to never fire.
As an alternative, try this:
$("input[name=IsSupervisorApprovalRequired]").click(function () { /* ... */ });
This approach checks the "name" attribute of the element instead of its ID. Since "name" values, unlike IDs, don't have to be unique, jQuery is able to handle multiple elements matching that pattern and should bind the event correctly.

Related

I want to see the textarea in Rails JUST as javascript does

I have a form that allows a user to submit HTML, and I just want to make a few changes to the text (e.g. replace a company name with one in Active Record), and then replace the HTML inside of a textarea. However, it's not playing too well. For whatever reason, trying to replace the textarea with the contents that were submitted in its POST request does not work. Nothing happens.
Here's my view form:
<div class="modal-body">
<div class="form-group row">
<div class="col-sm-12">
<%= form_tag(convert_email_template_path(format: :js), method: :post, :authenticity_token => true, id: 'importEmailForm', remote: true) do %>
<%= text_area_tag 'import_email', nil, rows: 10, placeholder: "Paste the headers of your email here.", class: "form-control" %>
<% end %>
</div>
</div>
</div>
<div class="modal-footer">
<%= link_to '<i class="fa fa-download"></i> Import'.html_safe, '#', onclick: "importEmail();", class: "btn btn-success btn-sm" %>
</div>
So basically, importEmail() looks like this:
function importEmail() {
setTimeout(function () {
$.ajax({
type: 'POST',
url: $("#importEmailForm").attr("action"),
data: $("#importEmailForm").serialize(),
});
}, 100)
};
which basically submits the form in the view. I need to capture this POST data, make a change or two, and then proceed with replacing textarea with the modified contents.
Now if I do this in plain javascript, this works fine:
#views/templates/convert_email.js.erb
var sourceBody = $('#import_email').val();
var start_position = sourceBody.indexOf("<html>");
var stop_position = sourceBody.indexOf("</html>");
var html = sourceBody.substring(start_position, stop_position);
var fixed_html = html.replace(/(=\n)/g, '');
// Grab body.
var iframe = document.getElementsByClassName("fr-iframe")[0]
var innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
// get the element in question
const input = innerDoc.getElementsByTagName("body")[0];
// Before pasting data, let's make sure the code view is turned OFF.
if ($('textarea').froalaEditor('codeView.isActive') == true) {
$('textarea').froalaEditor('codeView.toggle');
}
// dispatch keyboard events
input.innerHTML = fixed_html;
// Close modal.
$('#Modal').modal('toggle')
But if capture this POST data with params[:import_email], and do something like this:
#controllers/templates.rb
body = params[:import_email]
#body = body.gsub("=\n", "")
and pass that to the javascript view.
#views/templates/convert_email.js.erb
// Grab body.
var iframe = document.getElementsByClassName("fr-iframe")[0]
var innerDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
// get the element in question
const input = innerDoc.getElementsByTagName("body")[0];
// log codeview status
var codeView = 0
// Before pasting data, let's make sure the code view is turned OFF.
if ($('textarea').froalaEditor('codeView.isActive') == false) {
codeView = 0;
$('textarea').froalaEditor('codeView.toggle');
} else {
codeView = 1;
}
function
// dispatch keyboard events
input.innerHTML = "<%= #body %>";
resizeFroala();
// Close modal.
$('#Modal').modal('toggle')
Nothing happens. Perhaps there's a way to treat a textarea as source code or something?
Using the first method (javascript only), this is what fixed_html looks like in the console:
I want to get #body to look like this, but in order for me to do that, I have to do this in the controller:
body = params[:import_email]
#body = body.gsub("=\r", "\r").gsub("\n", "").gsub("\r", "").gsub("\t", "")
but trying to write this to the console looks like this:
Sorry for any confusion. Hope this makes sense. I just basically want to do what works flawlessly in Javascript, but in Rails.
Think I finally figured it out. Didn't realize rails used <%== %> in addition to <%= %>
Changed my code to:
input.innerHTML = "<%== j #body %>";
and this did the trick. Wow.

How to move an object to another scope through checkbox using Ajax?

I've lost in thought. I have a checkbox that I want to use to transfer a corresponding object to another scope on my page. Moreover, I want only to transfer one field of this checked object. I had used earlier Ajax by creating a .json file and then I responded to it in my controller. In my case input checkbox doesn't seem to have a remote: true option.
views/tasks/_index.html
<h3>Tasks database</h3>
<table>
<% #tasks.each do |task| %>
<tr class='tasks' id="task_<%= task.id %>">
#Some stuff
<td>
<input type="checkbox" class='check' data-id="<%= task.id %>" >
</td>
</tr>
<% end %>
</table>
<h3>Completed</h3>
<div class="complete-tasks">
</div>
Hence, I'm trying to accomplish that using an event via javascript.
So far I've managed to write some javascript code that moving my entire object.
application.js
$(document).on('ready page:load', function () {
$('.check').on('change', function(){
if ($(this).is(':checked')) {
id = $(this).attr('data-id');
task = $(this).closest("tr");
$('.complete-tasks').last().after(task);
}
});
})
But I want to relocate only one 'title' field using Ajax. Can someone please explain me how to accomplish that? I suspect I need to pass in some id's in checkbox and to define $.ajax.
models/task.rb
class Task < ActiveRecord::Base
scope :complete, -> { where(complete: true) }
scope :incomplete, -> { where(complete: nil) }
belongs_to :user
end
All you have to do is make the request to the url and then move the table row/title cell.
$(document).on('ready page:load', function () {
$('.check').on('change', function(){
if ($(this).is(':checked')) {
id = $(this).attr('data-id');
//Url is the site you want to hit.
var jqXHR = $.ajax(url);
var element = this;
jqXHR.done(function(data){
//Ajax finish here.
task = $(element).closest("tr");
$('.complete-tasks').last().after(task);
});
}
});
});
Edit: Fixed context use.
Check the jQuery documentation on how to set up the jqXHR properly:
http://api.jquery.com/jquery.ajax/

Rails: Why can't my object created by ajax be used to trigger javascript?

I'm trying to implement an ajax voting system for a model "Things", where if a user votes on Thing then a new Thing appears in the old one's place, which he can then vote on, and so on. This takes place on the Thing view itself.
I'm pretty sure this exact code was working a week ago, but now it mysteriously broke. Now, the Thing is replaced upon the first vote, but after that, voting no longer brings forth a new Thing:
views/things/show.html.erb
<div id="randajax">
<%= link_to image_tag("UpArrowGray.jpg", class: "rand_up_vote"), upvoterandom_thing_path(#rand.id), remote: true, method: :get %>
<script type="text/javascript">
function reload_script() {
$(".rand_up_vote").click(function () {
$.get( "<%= upvoterandom_thing_path(:id => #rand.id) %>", function( data ) {
$('#randajax').html(data);
reload_script();
});
});
}
reload_script();
</script>
</div>
controllers/things_controller.rb
def upvoterandom
#thing = Thing.find(params[:id])
UpVote.create!
#rand = Thing.all.first
render text: "<a data-method=\'get\' data-remote=\'true\' href=\'" + upvoterandom_thing_path(#rand.id) + "\'><img alt=\'Upvote\' class=\'rand_upp_vote\' src=\'/assets/UpArrowGray.jpg\' /></a>".html_safe
end
Apparently the issue is that an item created with ajax can't execute javascript. The web console shows that the upvoterandom_thing_path is getting executed, but nothing happens to the randajax div. I tried giving the Thing created from the original javascript a differenc class with different javascript and that didn't work either, although a seperate item ("THIS_WORKS") with the same class executed the same javascript normally:
views/things/show.html.erb
<%= link_to "THIS_WORKS", "#", class: "TEST_TEST", remote: true %>
<div id="randajax">
<%= link_to image_tag("UpArrowGray.jpg", class: "rand_up_vote"), upvoterandom_thing_path(#rand.id), remote: true, method: :get %>
<script type="text/javascript">
function reload_script() {
$(".rand_up_vote").click(function () {
$.get( "<%= upvoterandom_thing_path(:id => #rand.id) %>", function( data ) {
$('#randajax').html(data);
reload_script();
});
});
}
reload_script();
</script>
<script type="text/javascript">
$(".TEST_TEST").click(function () {
$('#randajax').html("TEST");
});
</script>
</div>
controllers/things_controller.rb
def upvoterandom
#thing = Thing.find(params[:id])
UpVote.create!
#rand = Thing.all.first
render text: "<a data-method=\'get\' data-remote=\'true\' href=\'" + upvoterandom_thing_path(#rand.id) + "\'><img alt=\'Upvote\' class=\'rand_up_vote\' src=\'/assets/UpArrowGray.jpg\' /></a>".html_safe
end
Can anyone explain why this is happening or how I can fix this?
When you set it up, you do this in your javascript:
$(".rand_up_vote").click(function () {
...
This sets up the click event on the elements. When one of the elements is replaced, or a new one is added, it hasn't had this event added to it, so it won't work like the others.
To fix this, you need to add this same event to the new element, OR remove the event from all elements and then add it to all elements (if you don't remove it first you risk firing the same event twice on a click or whatever).
EDIT with a proposed solution.
The simplest way is to add an onclick attribute to your elements, to run the reload_script() function. Change this so that it doesn't add an onclick to the events. I was about to start editing your code and i noticed that the reload_script function calls itself at the end of the anonymous function you're adding as the onclick event: this means that when you click on a .rand_up_vote element, it will do the AJAX get, replace the html, and then add another onclick to all the elements. This is definitely not right - was this an earlier attempt to fix this problem?
You probably just need something like this:
<div id="randajax">
<%= link_to image_tag("UpArrowGray.jpg", class: "rand_up_vote"), upvoterandom_thing_path(#rand.id), remote: true, method: :get %>
<script type="text/javascript">
function reload_script() {
$.get( "<%= upvoterandom_thing_path(:id => #rand.id) %>", function( data ) {
$('#randajax').html(data);
});
}
</script>
</div>
now edit the html so that each .rand_up_vote element has an onclick="reload_script()" attribute.

Partial view with ajax and jQuery UI.Dialog

I am using a standard MVC4 EF5 setup and have a standard view which loads data from the db onto a table.
At the start of the table I have a column for each record with an Add button. The functionality I want is to click the button, popup a model dialog box with a form and add something to the item in the grid that was clicked (a 1 to many).
Lets say I have a list of vans available shown in the list. And when I click the add button beside the particular van where I want to add a passenger, I want a popup to show that allows me to type the details of the passenger so they can be assigned to that van.
I think I am over complicating this. But my brain is fried. I tried partial views with ajax. I tried jQuery UI.Dialog. Im just lost. I am trying to figure out how to find the id of the record I clicked (given the buttons are all generated by a for each loop in the view as normal and numbering them 1 to X does not tell me the id of the record I clicked). So even if I get the popup showing, I wont know which van to assign the passenger to.
If your woundering where the passenger list is coming from, its another table. And effectively any passenger can be assigned to any van. Its hypothetical.
Im actually working on a document generator and so there is a many to many relationship between document parts and documents (a given document part, can appear or belong to many documents, and a document can contain many document parts). I know its messy, this is why I did not want to use the real example.
I'm thinking its maybe an easy enough problem to solve but I have been at it since Friday and the brain left home!
Edit: Adding Code:
Here is the main view: The main problem I am having with this is the way the grid is constructed. I think its partially razor, partially html, partially html helper, and partially javascript. I don't know which part is which, but I just need to get a popup to show for each button in the table, and to have an id I can assign values to. I cant figure out how to do it here.
Html.Grid(dbaccess().Where(c => something = something
).Select(o => new
{
Name = o.Name,
Comment = o.Comment,
Status = o.Status,
}
, "grdConfiguration", 0, htmlRowClass: (p) => (row++ % 2 != 0) ? "" : "oddRow"
, columns: new[]{
//THIS IS THE PROBLEM LINE BELOW .... It shows a button in the table, but...
//how do I make it unique. Is it even necessary to do so.
// How do I get the ID of the record at this location when this button is pressed.
//This is the code as originally posted: For context
new Helpers.GridColumn(value: (a) => "<input type=\"button\" class=\"btn\" id=\"BtnHello\" value=\"Add\" />"),
//for some reason when I try the following alternative as suggest by the answers so far - it doesn't work.
new Helpers.GridColumn(value: (a) => "<input type=\"button\" class=\"btn\" data-ThisId=\"#model.SomeId\" value=\"Add\" />"),
//THIS IS THE PROBLEM LINE ABOVE....
there is more columns but this button calls the jQuery...
On this view I also have some Div tags in which to load the partial... I can actually get this to popup. But that's about all I can do. And only when I click the first button in the table.
<div id='SomePopUp' style='display: none;'>
//#using (Html.BeginForm())
//{
// <div>
// <span class="display-label">Quantity: </span>
// <span class="display-field"><input type="text" id="txtQuantity" /></span>
// </div>
// <div>
// <span class="display-label">Comments: </span>
// <span class="display-field"><textarea rows="7"></textarea></span>
// </div>
//}
</div>
I also have a script section on this view with the code for the popup:
<script type="text/javascript">
$("#BtnHello").click(function ()
{
$("#SomePopUp").dialog(
{
resizable: false,
height: 400,
width: 400,
modal: true,
title:"add to {Some ID}:", //I want the id to show here so I know I have the record I want.
buttons:
{
Submit : function ()
{
$(this).dialog('Some Text');
},
Cancel: function ()
{
$(this).dialog('close');
}
}
});
});
</script>
I have a controller:
[HttpGet]
public ActionResult AddExtra(int id)
{
//Fairly sure I should be doing something with this id, but how do I get it from the button.
return PartialView();
}
And for the partial view I have
#model CM.ViewModels.AddExtraPackagesViewModel
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Add Something</h3>
</div>
<div>
//I was using ajax here...
#*#using (Ajax.BeginForm("DoSomething", "Something", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "list-of-something"
}))
{
<div class="modal-body">
#Html.TextBoxFor(x => x.Quantity);
#Html.TextAreaFor(x => x.Comment);
</div>
<div class="modal-footer">
<button class="btn btn-success" id="submit">Save</button>
Close
</div>
}
</div>
I made a little view model too but...
public class AddExtraViewModel
{
public int Id { get; set; }
public string Quantity { get; set; }
public string Comment { get; set; }
}
I apologise if this is all over the place but I did not write the original code. There were about 7 other programmers here before me and I'm just struggling to get through it.
Any help would be appreciated.
I think you would want something like this (using jQuery and jQuery UI):
Controller:
public ActionResult SomeAction(int id) {
return View(new YourModel { Id = id });
}
Partial View:
#model YourProject.Models.YourModel
// Partial view content e.g. the form etc.
Your view:
/<!-- html etc. -->
<table>
<tr>
<td>Add</td>
</tr>
</table>
<script>
$(function(){
$(".add-button").click(function(){
var options = {
autoOpen: false
}
var dialog = $("<div>").dialog(options);
var id = $(this).data("theId");
dialog.load("the/url/to/the/controller/action", { id: id }, function(){
dialog.dialog("open");
dialog.find("form").submit(function(){
// do stuff
dialog.remove();
return false;
});
});
});
});
</script>
if you are building buttons in a forloop you don't want to define an id on the button. Duplicate id's on a view can cause lots of issues. Use a class on the buttons instead to trigger off of and use $(this) in your script to get details of the button that was clicked. To access buttons on a partial or on items that are added to your page after page load you need to tie the click event for that button to the document like this
$(document).on("click", ".btnDetails", function(){
//your script here
});
The other example uses "this" and shows how you can pass the id of the clicked button back to the controller. The controller will need to be a little different though
public PartialViewResult PopulatePartial(int ID){
var Model = //populate your model based on the passed id
return PartialView("PartialViewName", Model);
}

Empty string passed to getElementById() at query.unobtrusive-ajax.js:16

I'm developing an ASP.NET MVC4 application and have started using jQuery actionlinks.
However when I run the following Razor code (and click the view ticket actionlink) I get a generic jQuery error (twice) saying that an empty string was passed to getElementById().
I have no idea where this error is happening since firefox merely links to the jQuery code.
This is my Razor code: (I know the js functions show and hideticket are empty but that is to simplify the code):
<script>
function ShowTicket(id) {
$("#viewTicketButton" + id).hide();
$("#hideTicketButton" + id).show();
$("#viewTicket").show();
}
function HideTicket(id) {
$("#viewTicketButton" + id).show();
$("#hideTicketButton" + id).hide();
$("#viewTicket").hide();
}
</script>
<h3>Your tickets</h3>
<table border="1">
<tr>
<td>Title:</td>
<td>Urgency:</td>
<td>Status:</td>
</tr>
#foreach (SupportTicketViewData t in Model.supportTicketViewDataList)
{
<tr>
<td>#t.title</td>
<td>#t.text</td>
<td>#t.status</td>
<td>#Ajax.ActionLink("View Ticket", "ViewTicket", new { id = t.id },
new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "viewTicket",
OnComplete = "ShowTicket(" + t.id +");"
}, new { id = "viewTicket" + t.id })</td>
<td><button id="#Html.Raw("HideTicket" + t.id)" onclick="HideTicket(#t.id);">Hide Ticket</button></td>
</tr>
}
</table>
<div id="viewTicket">
</div>
Also I get a result from the GET request just fine since it get's inserted into the div element however I get 2 errors when debugging in firefox.
Also when I click the viewTicketButton the button doesn't hide as it should.
Warnings 'Empty string passed to getElementById()' occurs when sending form created via Ajax.BeginForm or Ajax.ActionLink with unobtrusive validation turned on.
In my case adding handlers to all events supported by Ajax.BeginForm fixed issue with warnings:
#using (Ajax.BeginForm(“SomeAction”, null, new AjaxOptions() {
OnBegin = “someFunction”,
OnComplete = “ShowTicket”,
OnFailure = “someFunction”,
OnSuccess = “someFunction”
}
....
I believe that this should fix your issue.
More details about issue on my blog post.
I believe you cannot simply do
OnComplete = "ShowTicket(" + t.id +");"
The argument must be a javascript function. If what you want to call is parameterless, you can do
OnComplete = "ShowTicket"
where show ticket is the function object, so this is fine.
In your case however, you've got to pass the ID to ShowTicket. Try the following:
OnComplete = "function() { ShowTicket(" + t.id +"); }"
You will likely have to add the slashes to compensate for the double quotes that you need in the id tag
eg:
Html.Raw("id=\"SomeIdString\"")

Categories

Resources