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

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.

Related

How to pass generic list of values to external js file from controller/view in ASP.NET MVC

The file name is called clik.js here I have to access some values from controller/view
Using view I have tried this code:
<head>
#if (ViewBag.status != null)
{
<script type="text/javascript">
var tagAccess='#ViewBag.status[0]';
</script>
<script src="~/JavaScript/click.js"></script>
}
</head>
Using this code, I could get single value in js file. But I have to get all values
//var tagAccess='#ViewBag.status';
If I write the code like this, I didn't get any output
You can serialize the Item using this:
<script>
var tagAccess = #Html.Raw(Json.Serialize(#ViewBag.status[0]));
</script>

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);
}

How to read an HTML file into an angular component so as to allow for the updating of the HTML file without redeploying code

I am trying to have a HTML file in my assets folder that is nothing but some header tags, dates and feature lists to serve as release notes for our website. I have an angular modal component that I want to read this file each time its route is called, rather than the alternative of having the HTML in the component itself which would require us to redeploy anytime we updated the release notes.
As mentioned I originally had this as part of my components HTML file but this was then being compiled into javascript each time and unable to be updated without a redeploy. Everything I have tried to search for doing something similar seems to be pointing me to just doing it that way.
ReleaseNotes.html
<!DOCTYPE html>
<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
<body>
<h1>Example header one</h1>
<h3>03/01/2019</h3>
<h4>Patch 1.03 Title</h4>
<ul>
<li>Feature that was added</li>
<li>Feature that was added</li>
<li>Feature that was added</li>
</ul>
<hr>
release-notes-modal.component.ts
export class ReleaseNotesModalComponent implements OnInit {
faTimesCircle = faTimesCircle;
contents: string;
constructor(public dialogRef: MatDialogRef<ReleaseNotesModalComponent>) {
//this.contents = System.IO.File.ReadAllText("ReleaseNotes.html");
}
ngOnInit() {
}
close() {
this.dialogRef.close();
}
}
There are a few ways you can accomplish this. This is how I've done this in the past.
In a Controller in the c# application, you would read the html file and return it:
[HttpGet]
[Route("releasenotes")]
public async Task<IActionResult> ReadReleaseNotes()
{
var viewPath = Path.GetFullPath(Path.Combine(HostingEnvironment.WebRootPath, $#"..\Views\Home\releasenotes.html"));
var viewContents = await System.IO.File.ReadAllTextAsync(viewPath).ConfigureAwait(false);
return Content(viewContents, "text/html");
}
Then in the angular application in a service you would call this method and retrieve this file as follows:
getReleaseNotes(): Observable<string> {
return this.http
.get([INSERT_BASE_URL_HERE] + '/releasenotes', { responseType: 'text' });
}
You can then utilize that in the ReleaseNotesModalComponent through something like this:
#Component({
template: '<span [innerHTML]="contents"></span>'
})
export class ReleaseNotesModalComponent implements OnInit {
faTimesCircle = faTimesCircle;
contents: string;
constructor(public dialogRef: MatDialogRef<ReleaseNotesModalComponent>, private service: ReleaseNotesService) {
service.getReleaseNotes(html => this.contents = html);
}
ngOnInit() {
}
close() {
this.dialogRef.close();
}
}
For the Angular side of things, I created a StackBlitz example.

Render correct partial view with ajax call

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

ASP.NET MVC & JQuery Dynamic Form Content

I would like to dynamically add fields to an ASP.NET MVC form with JQuery.
Example:
<script language="javascript" type="text/javascript">
var widgets;
$(document).ready(function() {
widgets = 0;
AddWidget();
});
function AddWidget() {
$('#widgets').append("<li><input type='text' name='widget" + widgets + "'/></li>");
widgets++;
}
</script>
<ul id="widgets">
</ul>
This works, but I was going to manually iterate the form values in the controller:
[AcceptVerbs("Post")]
public ActionResult AddWidget(FormCollection form)
{
foreach (string s in form)
{
string t = form[s];
}
return RedirectToAction("ActionName");
}
But it occurred to me when I send the user back to the Get Action in the Controller I will have to set the FormData with the values entered and then iteratively add the widgets with <% scripting.
What is the est way to do this in the current release (5 I believe)?
My solution could be something like this (pseudo-code):
<script language="javascript" type="text/javascript">
var widgets;
$(document).ready(function() {
widgets = 0;
<% for each value in ViewData("WidgetValues") %>
AddWidget(<%= value %>);
<% next %>
});
function AddWidget( value ) {
$('#widgets').append("<li><input type='text' name='widget" + widgets +
"'>" + value + "</input></li>");
widgets++;
}
</script>
<ul id="widgets">
</ul>
And in the controller:
[AcceptVerbs("Post")]
public ActionResult AddWidget(FormCollection form)
{
dim collValues as new Collection;
foreach (string s in form)
{
string t = form[s];
collValues.add( t )
}
ViewData("WidgetValues") = collValues;
return RedirectToAction("ActionName");
}
You can work out the details later
(sorry for mixing VB with C#, I'm a VB guy)
i might be missing the point here, but, do you need to actually post the data back to the controller via a form action? why not make an ajax call using jquery to post the data to the controller...or better yet a web service? send the data async and no need to rebuild the view with the data values sent in.
This works fine if the values are being consumed and never used again, however, if you plan on persisting the data and surfacing it through a the view, your model should really support the data structure. maybe a Dictionary<string, string> on the model.
I'm not a ASP.net developer but I know from PHP that you can use arrays as names for input fields
Ex:
<input type="text" name="widgets[]" />
<input type="text" name="widgets[]" />
You can then iterate through the post variable widgets as if it was an array of values.
No messing around with dynamicaly named variables etc.
As far as I understand the problem is to preserve the posted values in widgets.
I thik you can just render those widgest you wont to populate on the server during the View rendering.

Categories