Render correct partial view with ajax call - c#

I have a controller with partial views, for example I have a partial view , like this:
[HttpGet]
[AutorisatieFilter(Rol = "Personeelsdossier | Rekeningen#Lezen")]
public ActionResult Rekeningen()
{
var model = PersoneelsDossierService.GetRekeningLezenModel(Context, HuidigeDienstverbandId,GetMutatieRol(), Gebruiker.DienstverbandId);
SetMedewerkerSelectie(model);
model.IsBevoegd = true;
try
{
BeveiligingService.ControleerManagerBevoegdheidVoorDienstverband(Context, Context.Klant.Id, int.Parse(Context.Gebruiker.ExternId), HuidigeDienstverbandId, Gebruiker.DienstverbandId);
}
catch(AuthenticationException)
{
model.IsBevoegd = false;
}
return PartialView("~/Areas/MSS/Views/PersoneelsDossier/Rekeningen.cshtml", model);
//return View(model);
}
This is inside the controller name: Personeelsdossier.
The view of Rekeningen looks,like this:

Partial Views do not use the Layout, so they will not include CSS unless you have the CSS in the partial view - They are intended to be render into full views.
Just change the Partial View to a full view if you want to use the layout page, or add your CSS to the Partial View if you want the CSS but no layout...

In our Application, we have special Master pages for Partial Views to include Scripts and CSS for example.
1) Create a new Master Page cshtml in Views\Shared folder (for example, PopupMaster.cshtml). It holds a very basic HTML template:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="~/Content/some.additional.css" rel="stylesheet">
</head>
<body>
#RenderBody()
<script src="maybe.some.additional.script.to.execute.js"></script>
</body>
</html>
2) Instead of return PartialView(...) you can now do return View("MyView", "PopupMaster", myModel);
This will result in a partialview-like result, but with possibility to provide extra css and scripts

Related

C# ASP.NET MVC : update view dynamically

I am learning C# using DotNetFiddle to code.
I have a web with a bunch of radio buttons and when I click them I want to update the view, for example: delete one button.
Also I need to do the delete logic in the backend and from the view I have to call a POST method to pass data.
So I'm trying to call the POST method with ajax and return RedirectToAction to display the new view, but somehow is not working.
You can check and test my current code here: dotnetfiddle
Controller
using System;
using System.Web.Mvc;
using System.Collections.Generic;
namespace HelloWorldMvcApp
{
public class HomeController : Controller
{
public SampleViewModel sampleViewModel = new SampleViewModel();
[HttpGet]
public ActionResult Index()
{
return View(sampleViewModel);
}
[HttpPost]
public ActionResult UpdateList(string id)
{
sampleViewModel.deleteElement();
return RedirectToAction("Index");
}
}
}
Model
using System;
using System.ComponentModel.DataAnnotations;
namespace HelloWorldMvcApp
{
public class SampleViewModel
{
public List<string> list = new List<string> { "a", "b", "c", "d", "e"} ;
}
}
View
#model HelloWorldMvcApp.SampleViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<!-- template from http://getbootstrap.com/getting-started -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>
<!-- CSS Includes -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
</head>
<body>
<br/>
<div class="container">
<br></br>
<ul>
#foreach (var c in Model.arr)
{
<li>
<input type="radio" name="option" id="#c">#c
</li>
}
</ul>
</div>
<!-- JS includes -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/4.0/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
$('input[type=radio]').click(function() {
var elemId = $(this).attr("id");
$.ajax({
url: '#Url.RouteUrl(new{ action="UpdateList", controller="Home"})',
data: { id : elemId },
type: "POST"
});
});
</script>
</body>
</html>
UPDATE
I've edited your Fiddle into this: https://dotnetfiddle.net/zQq3aR
It does exactly what I describe below in the original answer, but uses the main view. Note that this is NOT what I would recommmend... we're rendering the entire view again, and then replacing the entire page contents with the new content, but I'm not too familiar with DotNetFiddle and don't know how to add more views (can you?).
C#:
[HttpPost]
public ActionResult PostToMe(string id)
{
// TODO: Do some stuff here, then return the updated view
return ButRenderMe(id);
}
[HttpPost]
public ActionResult ButRenderMe(string id)
{
// Remove some content from the model
sampleViewModel.arr = new [] { "a", "b", "c", "all of the above" };
return View(sampleViewModel);
}
JavaScript:
$.ajax({
url: '#Url.RouteUrl(new{ action="PostToMe", controller="Home"})',
data: { id : elemId },
type: "POST",
success: function(data) {
document.open();
document.write(data);
document.close();
}
});
The better way to do this in a normal MVC project would be to have a PartialView used for rendering the content you want to replace... in this case the list. In your "update" action you would update your data, and then re-render just that PartialView and return it. Then replace just that section with the new content.
With the code above, from my Fiddle, we may as well be making a normal page post instead of an AJAX call.
There are a lot of factors here, but hopefully this helps.
ORIGINAL ANSWER
So this is a bit confusing, given that your question doesn't match your code in either the DotNetFiddle or the question itself, but let's see if this helps.
I'm pretty sure that returning a RedirectToAction Result doesn't return the content from the other action your redirecting to. I think in this case, since it's an AJAX request, MVC knows that this isn't the proper usage, and returns a 200 OK status with the following example content. So with this Controller Action:
[HttpPost]
public ActionResult RedirectMe()
{
return RedirectToAction("UpdateList");
}
We get this Response:
<html><head><title>Object moved</title></head><body>
<h2>Object moved to here.</h2>
</body></html>
So again, I don't think that will work the way you want.
I think what you want is to Render the other action, and return that content back. And that should be as simple as calling that other action:
[HttpPost]
public ActionResult PostToMe(string id)
{
return ButRenderMe(id);
}
[HttpPost]
public ActionResult ButRenderMe(string id)
{
return Content("Test");
}
That returns the content "test". I think that you should be able to render a normal view in that other action if you want.
I looked at your DotNetFiddle as well...
In your DotNetFiddle your JavaScript is calling into UpdateList which doesn't seem to exist... not sure why it would even return a 200 OK... The Actions you have are Index and UpdateFlights. If you open the F12 Developer Tools in Chrome you should see that the POST returns no Content.
So I added a Controller Action called UpdateList:
[HttpPost]
public ActionResult UpdateList(string id)
{
sampleViewModel.deleteElement();
return Content("Test");
}
And I can see that "test" is returned as the Response content in the F12 Tools.

The following sections have been defined but have not been rendered for the layout page with one line conditional

I am getting the error below but I have it defined on the Layout page below. I changed the #(IsSectionDefined to #if(IsSectionDefined because I need to write null in the else statement. Why would this be an issue?
The following sections have been defined but have not been rendered for the layout page "~/Areas/Directors/Views/Shared/_MembersFormLayout.cshtml": "FormCallback".
Layout.cshtml
<form data-bind="form:{ id: #Model.FormId, callback: #if (IsSectionDefined("FormCallback")){RenderSection("FormCallback", false);}else {#(Html.Raw("null"))}}">
Page.cshtml
#section FormCallback{members.event.updateImage}
I was able to fix it with his helper function found at Is there a way to make a #section optional with the asp.net mvc Razor ViewEngine?
.
<form class="clearfix" action="#Request.RawUrl" data-bind="form:{ id: #Model.FormId, callback: #this.RenderSection("FormCallback", #<text>null</text>)}">
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
if (IsSectionDefined(name))
{
return RenderSection(name);
}
return defaultContents(null);
}

No overload for method 'RenderPartial' takes 3 arguments

This is driving me crazy, i am getting:
No overload for method 'RenderPartial' takes 3 arguments
a compresed version of my _layout:
<head>
#{
var footer = new footer(User);
var pageTitle = ViewData["Title"].ToString();
}
</head>
<body>
#{
Html.RenderPartial(
"_footer",
footer,
new ViewDataDictionary(this.ViewData) { { "pageTitle", pageTitle } }
);
}
</body>
in my shared _layout, i am trying to pass a model to the partial view and a string which is provided by ViewData not sure what is going on.
i am setting title from the page _mypage.cshtml that uses that layout:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewData["Title"] = "My Title";
}
<h1>Hello there</h1>
_footer is a partial view where i am using the footer object and also calling other partial view where i need the title.
You can do it using ViewBag.
MyController.cs:
// set the title on a ViewBag inside your action
ViewBag.pageTitle = "My Title";
_layout.cshtml:
// call your partial view passing the model
#Html.Partial("footer", footer);
_footer.cshtml:
<!-- use the ViewBag data with # -->
<h1>#ViewBag.pageTitle</h1>

how to show MVC4 Directory.GetFiles in view

I'm a beginner at programming and I'm trying to build a mvc application that can search a directory and display all the ones found in a view.I have an error message pop up when I search. If someone would tell me what I'm doing wrong or point me in the right direction it would be greatly appreciated.
error message is this:
> The view 'C:\Users\carrick\Downloads' or its master was not found or
> no view engine supports the searched locations. The following
> locations were searched:
> ~/Views/DirectorySearch/C:\Users\carrick\Downloads.aspx
> ~/Views/DirectorySearch/C:\Users\carrick\Downloads.ascx
> ~/Views/Shared/C:\Users\carrick\Downloads.aspx
> ~/Views/Shared/C:\Users\carrick\Downloads.ascx
> ~/Views/DirectorySearch/C:\Users\carrick\Downloads.cshtml
> ~/Views/DirectorySearch/C:\Users\carrick\Downloads.vbhtml
> ~/Views/Shared/C:\Users\carrick\Downloads.cshtml
> ~/Views/Shared/C:\Users\carrick\Downloads.vbhtml
my controller looks like this
public class DirectorySearchController : Controller
{
//
// GET: /DirectorySearch/
public ActionResult Index()
{
return View();
}
public ActionResult GetDirFiles(string directorySearch)
{
//first check directorySearch is a valid path
//then get files
Directory.GetFiles(directorySearch);
ViewBag.message = directorySearch;
return View(ViewBag.message);
}
}
}
and my view
#{
;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>GetDirFiles</title>
</head>
<body>
<div>
<h2>Search Results</h2>
<ul>
<li>#Viewbag.message;</li>
</ul>
</div>
</body>
</html>
This line:
return View(ViewBag.message);
You are telling it to render the view with the name of the directory files, hence why you are getting that error messaging. ViewBag is already passed into your view so you don't need to pass it yourself.
You most likely just want to have the empty parameter call of
return View();
Which will by default return the view of with the name of the method in your controller.
Besides that you are not passing the files to the view, you are passing the path. You will need to do something like this. Note the case of ViewBag(not Viewbag)
Controller:
ViewBag.message = string.Join(",", Directory.GetFiles(directorySearch));
View:
<li>#ViewBag.message</li>
Or you can write a simple loop in your view
Controller:
ViewBag.message = Directory.GetFiles(directorySearch);
View:
#foreach(string file in ViewBag.message)
{
<li>#file</li>
}
In this line
return View(ViewBag.message);
Change it to
return View();
The first argument is the ViewName. ViewBag is passed to the view ambiently/implicitly, so you dont need to pass it on.

Add CSS references to page's <head> from a partial view

Is there a way to add CSS references to a page from a partial view, and have them render in the page's <head> (as required by the HTML 4.01 spec)?
If you're using MVC3 & Razor, the best way to add per-page items to your section is to:
1) Call RenderSection() from within your layout page
2) Declare a corresponding section within your child pages:
/Views/Shared/_Layout.cshtml:
<head>
<!-- ... Rest of your head section here ... ->
#RenderSection("HeadArea")
</head>
/Views/Entries/Index.cshtml:
#section HeadArea {
<link rel="Stylesheet" type="text/css" href="/Entries/Entries.css" />
}
The resultant HTML page then includes a section that looks like this:
<head>
<!-- ... Rest of your head section here ... ->
<link rel="Stylesheet" type="text/css" href="/Entries/Entries.css" />
<head>
You could also use the Telerik open source controls for MVC and do something like :
<%= Html.Telerik().StyleSheetRegistrar()
.DefaultGroup(group => group
.Add("stylesheet.css"));
in the head section
and
<%= Html.Telerik().ScriptRegistrar()
.DefaultGroup(group => group
.Add("script.js"));
in the script section at the botttom of your page.
And you can keep adding scripts on any view , or partial view and they should work.
If you don't want to use the component you can always inspire yourself from there and do something more custom.
Oh, with Telerik you also have options of combining and compressing the scripts.
You could have the partial view load in a javascript block that drops in the style to the head, but that would be silly considering that you probably want the javascript block in the head section for the same reason.
I recently discovered something pretty cool though. You can serialize a partial view into a string and send it back to the client as part of a JSON object. This enables you to pass other parameters as well, along with the view.
Returning a view as part of a JSON object
You could grab a JSON object with JQuery and ajax and have it loaded with the partial view, and then another JSON property could be your style block. JQuery could check if you returned a style block, if so then drop it into the head section.
Something like:
$.ajax(
{
url: "your/action/method",
data: { some: data },
success: function(response)
{
$('#partialViewContainer).html(response.partialView);
if (response.styleBlock != null)
$('head').append(response.styleBlock);
}
});
You can use a HttpModule to manipulate the response HTML and move any CSS/script references to the appropriate places. This isn't ideal, and I'm not sure of the performance implications, but it seems like the only way to resolve the issue without either (a) a javascript-based solution, or (b) working against MVC principles.
Another approach, which defeats the principles of MVC is to use a ViewModel and respond to the Init-event of your page to set the desired css/javascript (ie myViewModel.Css.Add(".css") and in your head render the content of the css-collection on your viewmodel.
To do this you create a base viewmodel class that all your other models inherits from, ala
public class BaseViewModel
{
public string Css { get; set; }
}
In your master-page you set it to use this viewmodel
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<BaseViewModel>" %>
and your head-section you can write out the value of the Css property
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
<%= Model.Css %>
</head>
Now, in your partial view you need to have this code, which is kinda ugly in MVC
<script runat="server">
protected override void OnInit(EventArgs e)
{
Model.Css = "hej";
base.OnInit(e);
}
</script>
The following would work only if javascript were enabled. it's a little helper that i use for exactly the scenario you mention:
// standard method - renders as defined in as(cp)x file
public static MvcHtmlString Css(this HtmlHelper html, string path)
{
return html.Css(path, false);
}
// override - to allow javascript to put css in head
public static MvcHtmlString Css(this HtmlHelper html,
string path,
bool renderAsAjax)
{
var filePath = VirtualPathUtility.ToAbsolute(path);
HttpContextBase context = html.ViewContext.HttpContext;
// don't add the file if it's already there
if (context.Items.Contains(filePath))
return null;
// otherwise, add it to the context and put on page
// this of course only works for items going in via the current
// request and by this method
context.Items.Add(filePath, filePath);
// js and css function strings
const string jsHead = "<script type='text/javascript'>";
const string jsFoot = "</script>";
const string jsFunctionStt = "$(function(){";
const string jsFunctionEnd = "});";
string linkText = string.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\"></link>", filePath);
string jsBody = string.Format("$('head').prepend('{0}');", linkText);
var sb = new StringBuilder();
if (renderAsAjax)
{
// join it all up now
sb.Append(jsHead);
sb.AppendFormat("\r\n\t");
sb.Append(jsFunctionStt);
sb.AppendFormat("\r\n\t\t");
sb.Append(jsBody);
sb.AppendFormat("\r\n\t");
sb.Append(jsFunctionEnd);
sb.AppendFormat("\r\n");
sb.Append(jsFoot);
}
else
{
sb.Append(linkText);
}
return MvcHtmlString.Create( sb.ToString());
}
usage:
<%=Html.Css("~/content/site.css", true) %>
works for me, tho as stated, only if javascript is enabled, thus limiting its usefulness a little.

Categories