tinymce in mvc 3 razor, Ajax.ActionLinks fail after first ajax call - ajax

I am using Tinymce inside an asp.net mvc 3 Razor application. An Ajax.ActionLink loads the tinymce editor via a call to a controller action named "GetContent". The GetContent method loads a text file from the file system. All is well. But, after I save the tinymce text via an $.ajax call, the Ajax.ActionLink no longer fires the controller method. In other words, something in the $.ajax post breaks the Ajax.ActionLink on the client so that it no longer calls the GetContent controller action.
Interestingly, the Ajax.ActionLink still loads the tinymce editor, but from the browser cache. In the example below I have 2 links "FileOne" and "FileTwo", which load two different text files. Before I call $.ajax the links load the file from disk. After I call $.ajax the links load the last "FileOne" or "FileTwo" from the browser cache.
This is the view. The $.ajax post occurs inside the tiny_mce_save_click() function, which is wired to the tinymce save button click:
#model TestTinyMCE.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#section JavaScript
{
<script src="#Url.Content("~/Scripts/tiny_mce/jquery.tinymce.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$().ready(function () {
init_tiny_mce();
});
function init_tiny_mce() {
$('textarea.tinymce').tinymce({
// Location of TinyMCE script
script_url: '/Scripts/tiny_mce/tiny_mce.js',
//javascript function called when tinymce save button is clicked.
save_onsavecallback: "tiny_mce_save_click",
encoding: "xml",
theme: "advanced",
plugins: "save",
theme_advanced_buttons1: "save",
theme_advanced_toolbar_location: "top"
});
}
function tiny_mce_save_click(tinyMceInstance) {
$.ajax({
type: 'POST',
url: '/Home/SaveContent',
data: $('form').serialize(),
success: function (data, status, xml) {
$('#results').html(data);
},
error: function (xml, status, error) {
$('#results').html(error);
}
});
return false;
}
</script>
}
#using (Html.BeginForm())
{
<ul>
#foreach (string fileName in Model.FileList)
{
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent" })</li>
}
</ul>
<div id="divContent">
#Html.Partial("GetContent", Model)
</div>
}
The partial view "GetContent" is:
#model TestTinyMCE.Models.HomeModel
#{
Layout = null;
}
<div id="divContent">
<fieldset id="fsContent">
<span id="results"></span><legend>Edit Content #Html.DisplayTextFor(m => m.FileName)</legend>
#Html.TextAreaFor(m => m.Content,
new Dictionary<string, object>{
{"class","tinymce"}, {"cols","80"}, {"rows","10"}}
)
#Html.HiddenFor(m => m.FileName)
</fieldset>
#if (#IsAjax)
{
<text>
<script type="text/javascript">init_tiny_mce();</script>
</text>
}
</div>
This is the controller. The GetContent method no longer gets called after the $.ajax post occurs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TestTinyMCE.Models;
namespace TestTinyMCE.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new HomeModel());
}
public ActionResult GetContent(HomeModel homeModel)
{
if (!string.IsNullOrEmpty(homeModel.FileName))
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
if (!System.IO.File.Exists(physicalPath))
homeModel.Content = string.Format("The file '{0}' does not exist.", physicalPath);
else
homeModel.Content = System.IO.File.ReadAllText(physicalPath);
}
return View(homeModel);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult SaveContent(HomeModel homeModel)
{
string path = string.Format("~/App_Data/{0}.htm", homeModel.FileName);
string physicalPath = Server.MapPath(path);
System.IO.File.WriteAllText(physicalPath, homeModel.Content);
ViewBag.Result = "The file was successfully saved.";
return View();
}
}
}

The problem is broswer caching. To prevent caching on the Ajax.ActionLink you must add AjaxOption HttpMethod = "POST". In the above code change ActionLink to
<li>#Ajax.ActionLink(fileName, "GetContent", new { FileName = fileName }, new AjaxOptions() { UpdateTargetId = "divContent", HttpMethod = "POST" })</li>.
See http://forums.asp.net/t/1681358.aspx?Disable+cache+in+Ajax+ActionLink+extension+method+in+asp+net+MVC

Related

MVC Core ajax and return result is a view

MVC Core, NET 5, not razor pages.
On a view I have three select components (bootstrap-select). I populate them via ViewModel.
"Get request -> Controller -> return View(viewModel);"
What I want...
When I changed value in any select component I do a post request (ajax) to the same controller (other method) and return view with repopulated data.
"'Post request -> Controller -> return View(changedModel);"
As I understood when I did ajax request I should handle it result in success and other cases.
What I should to do to reload page with new data?
Is it possible to achive this with this approach?
Yes, this is possible and you do not need to reload the page, just append the returned html to wherever you want it.
$.ajax({
type: "POST",
url: {your_url},
dataType: "html",
success: function (html) {
$("#someDiv").html(html);
}
});
What I should to do to reload page with new data?
If the post action return the same view as the get action and you want to reload the whole page, I think there is no need to use ajax. You can just redirect to post action with a form submission. If the view returned by the post action is a partialview you want render to the current view, you can use it like that in #cwalvoort answer.
Based on advices of cwalvoort and mj1313
I did:
Render main page with partials. ViewModel transfered to a partial as a parameter
On main page I added eventListners to controls with JS.
When control changes - ajax request to backend happens Controller/GetPartialView
Result from ajax replace html in partial section
Programmatically show needed components, re-add eventListners
PS Really need to learn Blazor or UI Framework :)
Code samples:
// JS
document.addEventListener("DOMContentLoaded", function (event) {
BindSelectActions();
});
function BindSelectActions() {
$('#selectGroups').on('hidden.bs.select', DoPartialUpdate);
$('#selectCompanies').on('hidden.bs.select', DoPartialUpdate);
$('#selectPeriods').on('hidden.bs.select', DoPartialUpdate);
}
function DoPartialUpdate(e, clickedIndex, isSelected, previousValue) {
// ToDo: Implement common script with "CallBackend" function
$.ajax({
type: "POST",
url: 'https://localhost:44352/TestController/TestGetPartial',
// no data its a stub at the moment
// data: $('#form').serialize(),
success: function (data, textStatus) {
$("#testControls").html(data);
$('#selectGroups').selectpicker('show');
$('#selectCompanies').selectpicker('show');
$('#selectPeriods').selectpicker('show');
BindSelectActions();
}
});
}
// Controllers
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public async Task<IActionResult> Main()
{
// ViewModel = _helper -> _mediator -> query -> context
return await Task.Run(() => View(new TestViewModel()));
}
[HttpPost]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult TestGetPartial(TestViewModel model)
{
// ViewModel = _helper -> _mediator -> query -> context
var result = new TestViewModel();
result.IsPageReload = "yes";
result.TestCollection = new string[] { "A", "B", "C" };
result.Companies = new List<SelectListItem> { new SelectListItem { Value = "999",
Text = "Test" } };
// ModelState.Clear();
return PartialView("_TestPartial", result);
}
// Main and partial views
#model TestViewModel
#{
ViewData["Title"] = "Test";
}
<div id="testControls">
#await Html.PartialAsync("_TestPartial", Model)
</div>
#section Scripts {
<script type="text/javascript" src="~/js/test.js" asp-append-version="true">
</script>
}
#model TestViewModel
<form>
<div class="d-flex flex-row justify-content-between mt-4">
<div><select id="selectGroups" asp-for="Groups" asp-items="Model.Groups"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Group"></select></div>
<div><select id="selectCompanies" asp-for="Companies" asp-items="Model.Companies"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Company"></select></div>
<div><select id="selectPeriods" asp-for="Periods" asp-items="Model.Periods"
class="selectpicker" data-live-search="true" data-style="btn-outline-dark"
title="Period"></select></div>
<div><button type="button" class="btn btn-outline-dark">Import</button></div>
</div>
</form>
<div>
#{
if (null != Model.TestCollection)
{
foreach (var item in Model.TestCollection)
{
<p>#item</p>
<br>
}
}
}
</div>

AJAX pagedlist with partial view

I can't quite figure out how to get a partial view to render a paged list using ajax.
The closest I've got it to working is the example from Using paging in partial view, asp.net mvc
I'm basically trying to create a page with a list of comments per user where the page can be changed in the same way as the answers tab on the stackoverflow users page.
The paging works fine the on the first pager click, but then the the partial view is all that is returned once I click on the pager again.
Controller:
public class ProductController : Controller
{
public IQueryable<Product> products = new List<Product> {
new Product{ProductId = 1, Name = "p1"},
new Product{ProductId = 2, Name = "p2"},
new Product{ProductId = 3, Name = "p3"},
new Product{ProductId = 4, Name = "p4"},
new Product{ProductId = 5, Name = "p5"}
}.AsQueryable();
public object Index()
{
return View();
}
public object Products(int? page)
{
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 3); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return PartialView("_Products");
}
}
Views:
Index.cshtml:
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<h2>List of Products</h2>
<div id="products">
#Html.Action("Products", "Product")
</div>
#section scripts{
<script type="text/javascript">
$(function() {
$('#myPager').on('click', 'a', function() {
$.ajax({
url: this.href,
type: 'GET',
cache: false,
success: function(result) {
$('#products').html(result);
}
});
return false;
});
});
</script>
}
_Products.cshtml:
#using PagedList.Mvc;
#using PagedList;
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
<div id="myPager">
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Products", new { page }))
</div>
Model
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
Can anyone show me what I'm doing wrong?
I ended up using the unobtrusive ajax example from the pagedlist source [https://github.com/troygoode/PagedList][1]
partial view:
#using PagedList;
#using PagedList.Mvc;
<ul id="names" start="#ViewBag.Names.FirstItemOnPage">
#foreach(var i in ViewBag.Names){
<li>#i</li>
}
</ul>
#Html.PagedListPager((IPagedList)ViewBag.Names, page => Url.Action("Index", new { page }), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing( new AjaxOptions(){ HttpMethod = "GET", UpdateTargetId = "unobtrusive"}))
Index:
#{
ViewBag.Title = "Unobtrusive Ajax";
}
#using PagedList;
#using PagedList.Mvc;
#Styles.Render("~/Content/PagedList.css")
<h2>Unobtrusive Ajax</h2>
<p>Example of paging a list:</p>
<div id="unobtrusive">
#Html.Partial("UnobtrusiveAjax_Partial")
</div>
Controller:
public class UnobtrusiveAjaxController : BaseController
{
// Unobtrusive Ajax
public ActionResult Index(int? page)
{
var listPaged = GetPagedNames(page); // GetPagedNames is found in BaseController
if (listPaged == null)
return HttpNotFound();
ViewBag.Names = listPaged;
return Request.IsAjaxRequest()
? (ActionResult)PartialView("UnobtrusiveAjax_Partial")
: View();
}
}
Just in case, since the original question wasn't answered. I guess the problem was that on click handlers weren't reattached to the new pager elements generated by AJAX request. I also don't like unobstrusive AJAX solution in this case, since pager id is hardcoded in the nested view while passing it in some other way may be too cumbersome.
<script type="text/javascript">
// better not to clutter global scope of course, just for brevity sake
var attachHandlers = function() {
$('#myPager a').click(function() {
$('#myPager').load(this.href, function() {
attachHandlers();
});
return false;
});
};
$(document).ready(function () {
attachHandlers();
});
</script>

Trying to load a partial view with Ajax.ActionLink

After reading a few other posts on here I've got this far with attempting to load a partial view on the click of a link (the text link may actually change to be an image once I get past this proof of concept stage). The problem is I am being directed to the partial view on click instead of it populating a div within the view I am clicking the link on:
The layout:
<script src="#Url.Content("~/scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
The View:
#Ajax.ActionLink("Item 1 Ajax", "Details", "Portfolio", new { name = "test" }, new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "row1Content",
OnComplete = "portfolioItemLoaded"
})
<div id="row1content"></div>
<script type="text/javascript">
function portfolioItemLoaded() {
alert("Done");
}
</script>
The controller:
public class PortfolioController : Controller
{
public ActionResult Details(string name)
{
return PartialView(name);
}
}
The partial (obviously named test.cshtml):
<p>Item 1 test</p>
Have I missed something obvious? It's the first time I have attempted to use AJAX in MVC, so there might be a school boy error or two.
I am planning on having several different Ajax Action Links that call different partial views, hence the reason I am passing the name in to the controller.
Code fixed
Do you wanna try this, you have a better control, if error, etc....
HTML with helper
#Html.ActionLink("Item 1 Ajax", "Details", "Portfolio", Nothing, New With {.Id = "myLink"})
JavaScript
<script type="text/javascript">
$(document).ready(function() {
$('#myLink').click(getIt);
});
function getIt() {
$.ajax({
type: "POST",
url: $(this).attr('href'),
error: function () {
//show error handler
},
success: function (r) {
$('#row1content').html(r);
}
});
return false;
}
</script>

asp.net mvc-4: What should receive an ajax call

I'm new to ASP.NET MVC(-4).
I want to make an Ajax call from my website using jquery and fill in a div on the page using the returned html. Since it is only a div I do not need a full html page with header and full body and stuff.
What should be on the receiving side?
Should it be a normal view, a partial view, some special type of resource or handler or some other magic?
You can use this With Post and Get operaitons
Script
$.ajax({
url: '#Url.Action("SomeView")',
type: 'GET',
cache: false,
data: { some_id: id},
success: function(result) {
$('#container').html(result);
}
});
Controller
public ActionResult SomeView(int some_id)
{
....
return PartialView();
}
View
<div id="container">
#Html.Partial("SomeViewPartial")
</div>
OR you can use AjaxActionLink
View
#Ajax.ActionLink("text", "action", "controller",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "container",
OnSuccess = "onSuccess",
})
Script
function onSuccess(result) {
alert(result.foo);
}
Controller
public ActionResult SomeView(int some_id)
{
return Json(new { foo = "bar" }, JsonRequestBehavior.AllowGet);
}
Also You can use Ajax.ActionLink to update only content page. with using this:
In ~/Views/ViewStart.cshtml:
#{
Layout = Request.IsAjaxRequest() ? null : "~/Views/Shared/_Layout.cshtml";
}
Since it is only a div I do not need a full html page with header and full body and stuff
You want a PartialView
You can return a View which has the Layout property value set to null
public class UserController : Controller
{
public ActionResult GetUserInfo()
{
return View();
}
}
and in GetUserInfo.cshtml
#{
Layout=null;
}
<h2>This is the UserInfo View :)</h2>
And you can call it from any page by using jQuery ajax methods
$("#someDivId").load("#Url.Action("User","GetUserInfo")");
If you want the Same Action method to handle an Ajax call and a Normal GET request call, ( Return the partial view on Ajax, Return normal view on Normal Http GET request), You can use the Request.IsAjax property to determine that.
public ActionResult GetUserInfo()
{
if (Request.IsAjaxRequest)
{
return View("Partial/GetUserInfo.cshtml");
}
return View(); //returns the normal view.
}
Assuming you have the Partial View (view with Layout set to null) is presetnt in Views/YourControllerName/Partial folder

"UpdatePanel" in Razor (mvc 3)

Is there something like UpdatePanel (in ASPX) for Razor?
I want to refresh data (e.g. table, chart, ...) automaticly every 30 seconds.
Similar to clicking the following link every 30 seconds:
#Ajax.ActionLink("Refresh", "RefreshItems", new AjaxOptions() {
UpdateTargetId = "ItemList",
HttpMethod = "Post"})
Edit:
I may should add that the action link renders a partial view.
Code in cshtml:
<div id="ItemList">
#Html.Partial("_ItemList", Model)
</div>
Code in Controller:
[HttpPost]
public ActionResult RefreshItems() {
try {
// Fill List/Model
...
// Return Partial
return PartialView("_ItemList", model);
}
catch (Exception ex) {
return RedirectToAction("Index");
}
}
It would be create if the PartielView could refresh itself.
You can try something similar to the following using Jquery (have not tested though)
<script type="text/javascript">
$(document).ready(function() {
setInterval(function()
{
// not sure what the controller name is
$.post('<%= Url.Action("Refresh", "RefreshItems") %>', function(data) {
// Update the ItemList html element
$('#ItemList').html(data);
});
}
, 30000);
});
</script>
The above code should be placed in the containing page i.e. not the partial view page. Bear in mind that the a partial view is not a complete html page.
My initial guess is that this script can be placed in the partial and modified as follows. Make sure that the ajax data type is set to html.
<script type="text/javascript">
setInterval(function()
{
// not sure what the controller name is
$.post('<%= Url.Action("Refresh", "RefreshItems") %>', function(data) {
// Update the ItemList html element
$('#ItemList').html(data);
});
}
, 30000);
</script>
Another alternative is to store the javascript in a separate js file and use the Jquery getScript function in ajax success callback.
Well, if you don't need the AJAX expierience than use the HTML tag:
<meta http-equiv=”refresh” content=”30; URL=http://www.programmingfacts.com”>
go here: http://www.programmingfacts.com/auto-refresh-page-after-few-seconds-using-javascript/
If someone wants the complete code for a selfupdating partial view have a look!
Code of the Controller:
[HttpPost]
public ActionResult RefreshSelfUpdatingPartial() {
// Setting the Models Content
// ...
return PartialView("_SelfUpdatingPartial", model);
}
Code of the Partial (_SelfUpdatingPartial.cshtml):
#model YourModelClass
<script type="text/javascript">
setInterval(function () {
$.post('#Url.Action("RefreshSelfUpdatingPartial")', function (data) {
$('#SelfUpdatingPartialDiv').html(data);
}
);
}, 20000);
</script>
// Div
<div id="SelfUpdatingPartialDiv">
// Link to Refresh per Click
<p>
#Ajax.ActionLink("Aktualisieren", "RefreshFlatschels", new AjaxOptions() {
UpdateTargetId = "FlatschelList",
HttpMethod = "Post", InsertionMode = InsertionMode.Replace
})
</p>
// Your Code
// ...
</div>
Code to integrate the Partial in the "Main"-View (ViewWithSelfupdatingPartial.cs):
#Html.Partial("_FlatschelOverview", Model)
The <meta refresh ..> tag in HTML will work for you. Its the best option
Traditional controls don't works in ASP MVC
You could do it using Jquery timers http://plugins.jquery.com/project/timers
Other option could be to use the Delay function
In your target is as simple as refresh the whole page, this SO link will be of your interest: Auto refresh in ASP.NET MVC
Hope It Helps.

Resources