Getting a toggle image to work properly on mvc page? - asp.net-mvc

Using MVC 3.
(The code below was an attempt to recreate the toggle effect of stackoverflow's checked answer)
I have a ajax actionlink when you click it calls an update to db to switch the flag/image. However it works one time, if I click it again it will not toggle. Because there is no postback, model.IsIssue is not updated (its server code). Not sure the best approach to fix the issue. Should I handle it in the code, where I check the current flag in db and pass it back to the view. Or via Jquery, not sure how to code it (my preference)?
My code in the view (toggle):
<div>
#Html.Raw(
Ajax.ActionLink("[replacethis]",
"ToggleEnabled",
new { questionId = question.QuestionID, reviewId = step.ReviewID, flag = question.IsIssue },
new AjaxOptions { UpdateTargetId = "toggleimage" + question.QuestionID })
.ToHtmlString()
.Replace("[replacethis]",
string.Format("<div id='toggleimage{0}'><img src='/Content/images/{1}' border='0' alt='toggle'/></div>",
question.QuestionID, question.IsIssue ? "issue_on.png" : "issue_off.png")
)
)
My action controller:
public ActionResult ToggleEnabled(int questionId, int reviewId, bool flag)
{
using (var db = new NexGenContext())
{
db.Database.ExecuteSqlCommand(
"EXEC SP_AddUpdateQuestionFlagged #QuestionID, #ReviewID",
new SqlParameter("#QuestionID", questionId),
new SqlParameter("#ReviewID", reviewId)
);
return flag ? Content("<img src='/Content/images/issue_off.png' border=0 />") : Content("<img src='/Content/images/issue_on.png' border=0 />");
}
}

I would just change a css class to toggle the image.
So instead of holding the image in img ... I would use a div and placing the image as a background.
So, instead of replacing the html with the DIV and IMG in it (what removes the callback to handle the click), just change the class of the div.
In your view:
<div id="myimage" class="NormalState"></div>
In JS:
$('#myimage').click(function () {
$.ajax({
url: 'yourControllerActionURL'
type: 'POST',
contentType: "application/json; charset=utf-8",
data: {'questionId': question.QuestionID, 'reviewId': step.ReviewID, 'flag': question.IsIssue },
success: function (data) {
if(data.isClicked == true){
$('#myimage').addClass('ClickState').removeClass('NormalState');
}
else{
$('#myimage').addClass('NormalState').removeClass('ClickState');
}
}
});
});
In your CSS:
.NormalState { background-color: #ABCDEF;}
.ClickState { background-color: #FF0000;}
In your controller:
You need to return JSON with isClicked set as true or false. You could also return the css class to apply from the controller, but that way your controller knows about the view and that is not the idea of the MVC pattern. So telling the view what happenned more suitable.

Related

How to open a popup and post data to controller simultaneously using ajax call in MVC

I'm trying to implement search functionality in my Form View. The search window opens in a popup (in a partialView) and asks for search queries(figure). Now the user enters all the search fields and POST request is made and eventually popup window displays a table of search result.
Form View (which has the button to open popup window)
#Ajax.ActionLink("Search current form", "SearchAction", new { #id = "SearchBtn" }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "result", InsertionMode = InsertionMode.Replace, OnSuccess = "openPopup" }, new { #class ="btn btn-primary"})<br />
<div id="result" style="display:none"></div>
<script type="text/javascript">
$(document).ready(function () {
$("#result").dialog({
autoOpen: false,
title: 'Search Window',
resizable:0,
width: 1000,
height: 700,
modal: true
});
});
function openPopup() {
$("#result").dialog("open");
}
</script>
SearchForm View (implemented as partial view)
#using (Html.BeginForm("SearchAction", "ViewModel", FormMethod.Post, new { #id = "searchform" }))
{
//some form elements
<div class="text-center">
<input type="submit" value="Go" class="btn btn-primary"/>
</div>
}
<div class="alert-danger">#ViewBag.emptyResult</div>
#if (Model != null)
{
//display the search results
}
Now to retain the popup I have to bind Go button to a ajax action in the same way as Form View. Also by reading this How to pass formcollection using ajax call to an action? I came to know that Ajax actions posts JSON data into the controller as opposed to key value pair which is easily accessible by FormCollection. So my question is how do I implement submit button(Ajax.Actionlink) in my search form so that it posts data into controller using FormCollection and retains the popup window as well.
Turns out I just needed to define a placeholder for the result table in my search popup.
<div id="showData" class="text-center table table-bordered bg-light"></div>
Now get your search results using Ajax call
function GetSearchResult() {
var searchParams = [];
//get search queries from textbox ids
$.ajax({
type: 'POST',
dataType: "json",
traditional: true,
data: {
s: searchParams
},
url: "/{controller name} /{action}",
success: function (result) {
var col = [];
if (isJson(result)) {
var sR = JSON.parse(result);
//create a html table using javascript and fill it which the result you got
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table); //table is what you created dynamically using javascript
}
else {
alert("No results found, Please try again");
}
}
});
}
Add this action in your controller
[HttpPost]
public JsonResult AjaxMethod(string value, string Id)
{
var updatedList = GetSearchResults(); //get search result from your repository
return Json(updatedList);
}
And as far as creating a html table thorugh javascript is concerned this helped me a lot!

Rails / Trix Editor save changes via AJAX to server

I am using the very simple to implement Trix Editor provided from Basecamp in an "Edit View".
How would one save automatically changes, without having the user to interact through the update button?
I am thinking about something like this:
(OLD SCRIPT)
window.setInterval(function() {
localStorage["editorState"] = JSON.stringify(element.editor)
}, 5000);
What I actually want to do:
post a ajax "post" request to the rails server. something like:
$('trix-editor').on('blur', function() {
var sendname = $('#note_name').val();
var sendlink = $('#linkinput').val();
var sendnote = $('input[name="note[note]"]').val();
$.ajax({
type: "POST",
url: "/notes",
data: { note: { name: sendname, link: sendlink, note: sendnote } },
success: function(data) {
alert(data.id);
return false;
},
error: function(data) {
return false;
}
});
(There is as well the problem with authentification and devise. Only if you are loged in you should be able to send an ajax post request ..??)
Even better would be to save changes only when the user changes some data, and then wait 5s and then push the updated data via json to the server. I have no clue how to do that...
PS: would have loved to tag this question with a "trix-editor" tag, sorry have not enought rep for doing so...
If you are using plain JavaScript, use a hidden input field:
<form>
<input type="hidden" id="noticeEditorContent"/>
<trix-editor input="noticeEditorContent" id="x" style="min-height: 200px;"></trix-editor>
</form>
Now you have access to the element with the ID x.
Which means, with getElementById, you can do something like that:
var richTex = document.getElementById("x");
With this variable, you can either set an interval as you already explained, or you are using jQuery to do the job:
$('#x').on('input', function() {
localStorage["editorState"] = JSON.stringify($('#x').val());
});
Just a suggestion. You can write this code a bit nicer and cleaner.
Now it depends. Is setting an interval every 5 seconds better or writing every change to the LocalStorage?
Suggestion:
Save the input when the user deselects the field:
$('#x').on('blur', function() {
localStorage["editorState"] = JSON.stringify($('#x').val());
});
Update: Here is a working JSFiddle.
so I came up with this code which saves via ajax on 'trix-blur' (which fires when the user disselects the trix-editor). There is only the question left if this code is secure enought with devise, or if now anyone can send data to be saved?!?
I have the authentification in the notes controller like that:
before_action :authenticate_user!
and here is the javascript part (with a custom messages functionality):
$('trix-editor').on('trix-blur', function() {
var sendname = $('#note_name').val();
var sendlink = $('#linkinput').val();
var sendnote = $('input[name="note[note]"]').val();
var sendid = $('#note_id').val();
$.ajax({
type: "PUT",
url: "/notes/" + sendid,
dataType: "json",
data: { note: { name: sendname, link: sendlink, note: sendnote }, id: sendid, commit: "Update Note" },
success: function(data) {
addMessage('auto saved ...', 'msg-success');
return false;
},
error: function(data) {
alert('error');
return false;
}
});
var addMessage = function(msg, msgclass) {
$('#notifications').append('<div id="msg" class="msg '+msgclass+'">'+msg+'</div>');
setTimeout(function() {
$('#msg:last-child').addClass('msgvisible');
}, 100);
displayMessage();
};
var displayMessage = function() {
setTimeout(function() {
hideMessage();
}, 2000);
};
var hideMessage = function() {
$('#msg').addClass('msghide');
setTimeout( function() {
deleteMessage();
}, 300);
};
var deleteMessage = function() {
$('#msg').remove();
if ($('#notificatosn').find('#msg') > 1) {
displayMessage();
}
};
});
Per the Trix project page the trix-editor emits different events on specific conditions.
The trix-change event is what you need; it fires whenever the editor’s contents has changed.
So, the first line of your JavaScript code could be
$('trix-editor').on('trix-change', function() {
/* Here will be your code to save the editor's contents. */
})

My asp.net MVC partial view is unresponsive after initial ajax call?

I am working on an asp.net mvc 4 application and I am using a PartialView for a table in the Index view. After I make the initial ajax call the request is successful and the UI is updated accordingly. But when I try to update again, the JavaScript is unresponsive for the click event.
Is this typical?
AJAX call:
$("#button").click(function(){
$.ajax({
url: 'url',
type: "POST",
data: $("#form0").serialize()
}).done(function (allData) {
$("#mypartialId").html(allData);
ResizeContent();
}).fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
Something like this is in my controller Index action:
if(Request.IsAjaxRequest())
{
return PartialView("_PartialView", model);
}
return View(model);
Razor Ajax.BeginForm, has id of form0 by default:
#using (Ajax.BeginForm("Index", "MyController",
new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "mypartialId"
}))
My partial rendering in Index:
<div id="mypartialId">
#{Html.RenderPartial("_PartialView", Model);}
</div>
This works once but when I click the button switch is in the partialView the JavaScript becomes unresponsive. I am guess its something with the Index view not reloading with the partialView...would there be any way around that?
I used .on() function instead of just the .click() as #Sergey Akopov suggested. After that I had another problem after that because the data coming back was a table, so IE9 was giving me UI problems.
The fix was to remove the white space from the table tags like so, before injecting it into the DOM
var expr = new RegExp('>[ \t\r\n\v\f]*<', 'g');
table = table.replace(expr, '><');
$("#mypartialId").html(table);
Here is a link to the 'td' issue displaying weird in IE9.

Dynamically added link action produces 'This request has been blocked...' error

When I add category in controller action I return JSON object:
return Json(new { categoryName = category.Name, isPrimary = isPrim ? "1" : "-1", categoryId = categoryId }, JsonRequestBehavior.AllowGet);
In JS handler function I add item on page:
...
var totalLink = "<li style='color: #bbbbbb;'>" + result.categoryName + "<a class='removeCategoryButton' href='#lnk#'>remove</a></li>";
var lnk = '#Url.Action("RemoveCategoryFromLocation", "Location", new{locationId = Model.Location.TicketId, categoryId=-1})';
totalLink = totalLink.replace('#lnk#', lnk);
totalLink = totalLink.replace('-1', result.categoryId);
$('#otherCategories').append(totalLink);
...
When I click on remove link I call the following function:
$(function () {
$('.removeCategoryButton').click(function (event) {
event.preventDefault();
$.ajax({
url: this.href,
type: 'POST',
context: this,
success: function (result) {
if(result.categoryName == 1) {
$(this).closest('li').remove();
}
}
});
return false;
});
});
But I get the following error:
This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.
This error happens only when I add item and want to remove it as soon after add on page. If I refresh page and click on remove link it works without problem.
Just to note when I get the error from above category is removed, so call works it just from some reason pop this error.
You seem to be adding the remove links dynamically and yet you have subscribed to the .click event handler only once when the DOM is ready. So make sure you do it in a lively manner. But since the .live() method is deprecated, depending on the jQuery version that you are using you should use either .delegate() or the .on() methods.
So with the latest version of jQuery it is recommended to use .on():
$(document).on(events, selector, data, handler);
$(document).on('click', '.removeCategoryButton', function () {
$.ajax({
url: this.href,
type: 'POST',
context: this,
success: function (result) {
if(result.categoryName == 1) {
$(this).closest('li').remove();
}
}
});
return false;
});
Notice that you no longer need to wrap this in a document.ready callback.

Replace the Ajax.ActionLink by the same functionality with jQuery

With asp.net mvc we can do an ajax call like this:
#{
var ajaxOpts = new AjaxOptions { UpdateTargetId = "main-content", OnBegin = "fctTabLoading", OnComplete = "fctTabLoaded", InsertionMode = InsertionMode.Replace };
}
#Ajax.ActionLink("my link text", "MyAction", "MyController", new { id = Model.RequestID }, ajaxOpts)
Which produce the following html:
<a data-ajax="true" data-ajax-begin="fctTabLoading" data-ajax-complete="fctTabLoaded" data-ajax-mode="replace" data-ajax-update="#main-content" href="/MyController/MyAction/19">my link text</a>
Now I would like to execute the same ajax call but from jQuery and I don't know how to proceed!
I would like something like:
$.ajax({
type: "Post",
url: myURL,
begin: fctTabLoading,
complete: fctTabLoaded,
mode: "replace",
update: "#main-content",
cache: false,
success: function () { alert('success'); }
});
I know the above ajax script won't work because 'mode' and 'update' are not recognized. So I am blocked.
It drives me crazy :(
Why I cannot use the MVC ActionLink? Because I first need to show a jquery dialog to let the user confirm then only do the ajax call in order to refresh a specific div on my page.
Any help is greatly appreciated.
Thanks.
You could start by replacing your Ajax link with a normal link:
#Html.ActionLink(
"my link text", // linkText
"MyAction", // actionName
"MyController", // controllerName
new { id = Model.RequestID }, // routeValues
new { id = "mylink" } // htmlAttributes
)
which will produce the following markup:
my link text
and then in a separate js file unobtrusively AJAXify it:
$(function() {
$('#mylink').click(function() {
$.ajax({
url: this.href,
type: 'POST',
beforeSend: fctTabLoading, // corresponds to your OnBegin callback
complete: fctTabLoaded, // corresponds to your OnComplete callback
success: function(result) {
$('#main-content').html(result);
}
});
return false;
});
});
As you know, the Ajax.ActionLink uses jquery.unobtrusive-ajax.js to execute the ajax links.
If you look at that file, you will see that the event handlers use jquery's live event binder. This binds the event listener to the document object. So, if you wanted to confirm before this event was triggered, you could bind directly to the element like the following:
$('#YOUR_ELEMENT').click(function () {
var confirmed = confirm("CONFIRM_MESSAGE");
if (!confirmed ) {
return false;
}
return true;
});
To use jquery dialog you could do the following:
function confirmDialog () {
$('#YOUR_DIALOG').dialog(
{ buttons: { "Ok": function() { return true; },
{ "Cancel": function() {return false;}
}
});
}
and then you would set confirmed in the previous function to confirmDialog().
***The dialog options may not be exactly what you want, but this should get you going.

Resources