I'm looking at outsourcing some in-page help on a large web application I am creating and would like to make it really easy for this content to added to our pages when we're ready for it.
So I was thinking I could create a system where I can add text files to the same folder where an action expects it's view to be and read out the content of that file the content in that file can be passed back to the view to display. Either that or create a helper that would do the same.
Example
Controllers
HomeController.cs
Views
Home
Index.aspx
Index.txt
Index.aspx would then have access to the content in Index.txt.
How would I start going about creating this system. Are there any built in classes in .NET MVC that I could take advantage of?
A similar question was asked yesterday: Including static html file from ~/Content into ASP.NET MVC view.
Basically you can read the text from the file and include it inside your view by using File.ReadAllText so you would have something like this inside your index.aspx file
<%= File.ReadAllText(Server.MapPath("~/Views/Home/index.txt")) %>
I'd create a parallel hierarchy in the Content folder and put the files there, probably as HTML. Then you can simply load them via AJAX in the view using the parallel hierarchy convention.
Content
Help
Home
index-help.html
about-help.html
Foo
index-help.html
bar-help.html
Then in your views
<div class="help">
<noscript>
<a href='#Url.Content( "~/content/help/home/index-help.html" )'>Click for Help</a>
</noscript>
</div>
<script type="text/javascript">
$(function() {
$('.help').load( '#Url.Content( "~/content/help/home/index-help.html" )' );
});
</script>
You may also be able to extract the controller/action from RouteData in the view if your routes are consistent and move this to your _Layout.cshtml file with the path being provided by route data.
#{
var controller = ViewContext.RouteData["controller"] as string;
var action = ViewContext.RouteData["action"] as string;
var url = Url.Content( string.Format( "~/content/help/{0}/{1}-help.html", controller, action ) );
<div class="help">
<noscript>
<a href="#url>Click for Help</a>
</noscript>
</div>
<script type="text/javascript">
$(function() {
$('.help').load( "#url" );
});
</script>
}
One possible solution would be to store them as xml file instead, that are serialized from the model the view is expecting. You could then create an Action Filter populate the model being returned with the data from the XML file. I hope that helps.
Related
I have an area in my ASP.NET MVC application that needs to make use of partial views stored in the main application.
The code in question is being migrated from the main application into an area for organization, so I need to update the helper tags for the partial views.
Currently my tags look like this:
#await Html.PartialAsync("../Shared/Partials/_details.cshtml")
Of course, this fails in an area, since this helper only begins searching at the Areas/MyArea/ folder. I've tried adding additional ../ to the beginning of the address, but that doesn't change anything. How can I reconnect my partial views to this area?
You need to reference the app root.
The following example references a partial view from the app root. Paths that start with a tilde-slash (~/) or a slash (/) refer to the app root:
Partial Tag Helper:
<partial name="~/Pages/Folder/_PartialName.cshtml" />
<partial name="/Pages/Folder/_PartialName.cshtml" />
Asynchronous HTML Helper:
#await Html.PartialAsync("~/Pages/Folder/_PartialName.cshtml")
#await Html.PartialAsync("/Pages/Folder/_PartialName.cshtml")
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-6.0
See also: significance of tilde sign
I am converting css template into MVC application. Needless to say, I am just a beginner into webdesign. Template contains reference to one js script, which needs to be included on all pages except the landing page.
<script type="text/javascript" src="js/slider_func_innerpage.js"></script>
How to achieve it in MVC without creation of another master page? I tried to put this line in all views except of default view, but nothing happened. Ideally, I would like to handle this in my _Layout.cshtml file. Is it possible to set there some "if" condition, which would, if true, call partial view containing that single line I posted above? Or is there a more elegant way to load that js file into all views with the exception of the default one?
If you do not prefer to create another layout for landing page (Why not ? This seems to be the right way of doing it) , you might consider conditionally loading this js file in your layout.
So in the view for landing page, set a flag to the ViewBag dictionary
#{
ViewBag.IsLandingPage = true;
}
Now in the layout, check this flag and if it is not null and true, Exclude the inclusion of your js file
#{
if (ViewBag.IsLandingPage == null)
{
<script type="text/javascript" src="js/slider_func_innerpage.js"></script>
}
}
I have downloaded a chat example from the Microsoft website. I have been following several tutorials but I have never seen the #section script{} before I have done scripts without this block of c# code (#section script{}) and it seem to work fine but in this instance of the chat application using signal R when I do take the scripts outside the block it does not work.
#section scripts {
<!--Script references. -->
<!--The jQuery library is required and is referenced by default in _Layout.cshtml. -->
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>
<!--SignalR script to update the chat page and send messages.-->
<script>
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call back to display messages.
chat.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
// This optional function html-encodes messages for display in the page.
function htmlEncode(value) {
var encodedValue = $('<div />').text(value).html();
return encodedValue;
}
</script>
}
A section allows you to add something in a view which will be added in the layout. ie:-
view
#section scripts {
<script>
alert('foo');
</script>
}
layout
#RenderSection("scripts", false)
now this named section scripts will be rendered where you have specified in the layout.
#RenderSection also has 2 signatures:-
public HelperResult RenderSection(string name) // section required in the view
public HelperResult RenderSection(string name, bool required)
When you define an #section somewhere, lets say the _Layout.cshmtl file, it allows all of your Views to dynamically insert script files or CSS files or what ever into places in the defining page.
This is very nice when, for example, you are using the jQuery UI Datepicker control only on a couple views in your site. So you may not want to globally include the jQuery UI Datepicker script file in your _Layout.cshtml since you are only going to need it on 2 or 3 pages.
#section allows you to include those files only for certain views. It is needed since, a view cannot easily change the contents of the _Layout.cshtml otherwise.
You can also position the #section at the bottom of the layout, for JavaScript files for example, or at the top of the layout, for CSS files. You could also use it to include a sidebar, made in HTML, only in certain views.
Just be aware that Partial Views are not able to use the #section element by default.
There is also one thing that should be added to the answers above that makes the use of "scripts" section crucial in most cases which is also the only reason for me to use this section.
That is, it guarantees that scripts will load after all page contents which is essential. By doing this, you actually make sure necessary elements for your JavaScript code have loaded already and also it is a matter of performance.
To elaborate how it works, I should mention that:
That is a common practice to put the commonly used scripts inside the "_Layout" page to make them accessible among all pages and also prevent their repetition.
All contents of child views are loaded into the _Layout view where #RenderBody() method is called. Except the contents inside #sections of each view.
When we define "scripts" section inside the footer of the layout for common scripts and then add our scripts of child views inside the "scripts" section of each child view we make sure that these scripts will load after the script of the layout that makes the functions in the _Layout available to the scripts of the child views.
Otherwise, the scripts of child views would be loaded where RenderBody() method is called, before the common scripts of the _Layout page.
For Example:
Inside _Layout:
#RenderBody()
<footer>
<script>
$(function CommonlyUsedFunction() {
console.log("text");
});
</script>
#RenderSection("Scripts", required: false)
</footer>
Inside MyView:
<script>
CommonlyUsedFunction(); //Function is not Accessible Here
//It will not be accessible because RenderBody() will load the function before its declaration.
</script>
#section Scripts
{
<script>
CommonlyUsedFunction(); //Function is Accessible Here
</script>
}
I'd just like to add another form of answer here, because it took me a combo of reading all three current answers and some experimenting before I understood it.
I copied some ajax code for a modal popup that was enclosed in #section scripts { } and put it in a view. It worked fine, but I took away the section bit, because it was encapsulated in <script> html - which is normally fine;
My view before:
#Section scripts {
<script> ... my function ... </script>
}
<h1>The rest of the page</h1>
How I normally include a one-off script on a view:
<script> ... my function ... </script>
<h1>The rest of the page</h1>
By doing this, the script is rendered inside the html of the view itself and the popup stopped working. This is because the script accesses elements outside of that view - it needs site-wide access so to be able to stop using #section scripts I would have to be put in the layout file.
But I only really want this script rendered when this view is being used. I don't want to put it in the _layout file and have it loading on every single page.
Lucky me! At the bottom of a default _layout file there's a line (I normally remove, to be honest):
#RenderSection("scripts", required: false)
So by enclosing my <script>s in #section scripts { } on any view, it becomes part of the _layout, and is loaded after all other content (it's right at the bottom of the layout page), but only when that particular view is used.
It basically allows a dynamic layout page which is very clever, I wonder if it's possible to do with stylesheets too.
I am unable to show the CKEditor UI, for reasons unknown at some places. In one view, it works great.. in another it does not, but uses the exact same code as the one that works. Allow me to clarify:
I am going database first, and connecting an ADO.NET Entity Data Model to a database I have running. Then I made a controller, lets call it "news" with the MVC CRUD template, linking it to my model (database). From here the views are compiled for me, and under the "Create" view I would introduce:
<div class="editor-field">
#Html.TextAreaFor(model => model.content, new { #class="ckeditor", #id="ckeditorbox"})
#Html.ValidationMessageFor(model => model.content)
</div>
where model.content is the column name in the database (read/write to DB works, so this isn't really the issue anyway). This appears as a blank space in my browser. Inspecting the page with Google Chrome I see that it gets a visibility:hidden attribute for some reason.
Now
If I were to make a new controller with no template (Empty MVC controller) and post the exact same code from the Create.cshtml view into my newly created Index.cshtml then CKEditor would display just fine. Anyone got an idea as to why this is?
I was thinking this was an issue with the ckeditor.js script not being initialized, because the initializing is placed in _Layout.cshtml as:
<script src="#Url.Content("addons/ckeditor/ckeditor.js")" type="text/javascript"></script>
However placing this in the Create.cshtml view does not work either.
Suggestions are greatly appreciated
As to why CKEditor did not appear on particular pages, I am not sure. The fix for me however was to set:
<script type="text/javascript">
CKEDITOR.replace('x');
CKEDITOR.editorConfig = function (config) {
ignoreEmptyParagraph = true;
};
in the view
I still find myself having to declare global variables in my .aspx files (the project was started before razor). For example I have to declare global javascript variables like this in my .aspx files:
var getDistributionListUrl = '<%= Url.Action("GetDistributionList", "PublicDocument") %>';
I can then reference this variable in my .js files.
Is there a better way?
Is there a better way?
There is nothing wrong with this.
Personally I use HTML5 data-* attributes on some DOM elements that I am manipulating later in my scripts. For example:
<div id="foo" data-url="<%= Url.Action("GetDistributionList", "PublicDocument") %>">
Hello
</div>
and then in my js:
$('#foo').click(function() {
var url = $(this).data('url');
...
});
But in 99.99% of the cases those urls are associated with either <form> elements or link hrefs so in my javascript I simply retrieve and use this value when I need to do some progressive enhancement on the given DOM element (like unobtrusively AJAXifying forms or anchors).