Setting up ngRoute throughout my application creates a problem. The controllers used in many views are dependant on script taken from the database in the ASP.NET controllers. Here's an example:
Angular App
var app = angular.module("app", ["ngRoute"])
.config(["$routeProvider", "$locationProvider", function($routeProvider, $locationProvider) {
$routeProvider
.when("/Template/:id", {
templateUrl: function(arg) { return "/User/Template/Details/" + arg.id },
controller: "FormController"
});
$locationProvider.html5Mode(false).hashPrefix("!");
}]);
Razor view Details.cshtml:
#{
Layout = null;
}
<head>
<script type="text/javascript">
alert('triggered');
angular.module("app").controller("FormController", ["$scope", function ($scope) {
alert("not triggered");
$scope.xyz = 1;
}]);
</script>
</head>
<div id="template-details" class="col-xs-12" ng-controller="FormController" ng-cloak>
<span>{{xyz}}</span>
</div>
It loads the view just fine, except it doesn't initiate the controller (with the ViewBag code). The script block executes just fine (tested with the first alert()).
Couple of things to keep in mind, when you get controller is undefined:
Make sure your Angular App is loaded before you call the FormController script
Make sure your FormController div is inside ng-app="app" div.
Make sure you are loading ng-route after angular.
I have created a JsFiddle from what you have provided and it works fine.
Related
I would like to render the "scripts" section of the .cshtml view inside the controller as a string. Is this possible?
What I actually want to do is get the scripts with a separate ajax call and then run eval on the script after loading the html of the view also with ajax.
I've tried looking for related topics but haven't come up with anything relevant. Some related answers fool around with HtmlHelper.ViewContext.HttpContext.Items[""] but I'm unsure how to use it.
What I want is something like this: string scripts = GetScriptsForView(action, controller); which returns the section used in the view like this: #section Scripts {
To clarify (edit):
I'm trying to replace the "RenderBody()" of the layout page with ajax calls, so that I don't have to load the layout containing the static header every time.
I have managed to replace all <a>-tags with ajax calls replacing the <div> containing the view, but am unable to get the javascripts working.
I could remove the #section scripts { from the cshtml-files and let the script tag get loaded with the html view. The problem with this is that if I reload the page it calls the scripts for that view before calling the scripts of the layout page, resulting in errors. Therefore I wish to load the scripts separately.
TL;DR
While you can load sections and contents in separate requests (see example 1), but you don't need to do that. Instead:
You can have different Layout pages for different purposes and dynamically decide which layout to use for the view. For example a full layout page for normal requests and a simple layout page without headers and footers for ajax requests (see example 2).
You can decide dynamically about the layout page in _ViewStart.cstml or in the controller, based on different request parameters, like Request.Headers, Request.QueryString, Request.IsAjaxRequest, etc.
Long Answer
Here are some useful notes about the layout, section, view and partial view which may help you to handle the case:
Specify Layout in return View() - When returning a View you can specify the layout for the view. For example return View("Index", masterName: "_SomeLaypout"). You can have some logic to dynamically decide about the layout name.
Specify Layout in _LayoutStart.cshtml - When returning a View, you can specify the Layout in _ViewStart.cshtml file, for example #{ Layout = "_Layout"; }. You can have some logic to dynamically decide about the layout name.
Render content without Layout - When return a view using PartialView() method, it will render the whole content of the view without any layout or without rendering any section. It's equivalent to using return View and setting Layout to null, in _ViewStart.cshtml)
Render Section without any Content - You can have a layout having just a RenderSection method. Then if you return a view using that layout, it just renders the specified section of that view and will ignore the contents.
Scripts with/without Section - Considering above options, when you put scripts in the script tag without putting in a Section, you guarantee they will be returned as part of the result, regardless of calling return View or return PartialView or having or not having Layout. So in some cases you may want to put scripts without sections. In this case, when you load the partial view using ajax call, it contains the content as well as scripts of the partial view.
In some cases you may want to have some common content/scripts even for partial views, in such cases you can use Section, also have a Layout page which renders the section and contains common things that you want to have in all partial view. In this case you need to return the partial view using return View and specify that specific layout page.
Example 1 - Return just scripts or just content in an ajax request
The following example shows how you can send an ajax request to get content and send an ajax request to get scripts section.
Please note, I don't recommend using different requests for getting
content or getting scripts separately and this example is just for
learning purpose to show you what you can do with Layout pages and
_ViewStart.
To do so, follow these steps:
Change the _ViewStart.cshtml content to:
#{
if(Request.QueryString["Layout"]=="_Body")
{
//Just content of the page, without layout or any section
Layout = null;
}
else if (Request.QueryString["Layout"] == "_Script")
{
//Just script section
Layout = "~/Views/Shared/_Script.cshtml";
}
else
{
//Everything, layout, content, section
Layout = "~/Views/Shared/_Layout.cshtml";
}
}
Note: Instead of a Request.QueryString, you can easily rely on a Request.Header or Request.IsAjaxRequest() or ... base on your requirement.
Then just for _Script, add a new layout page named _script.cshtml having the following content:
#RenderSection("scripts", required: false)
Then:
To get the script section, send a normal ajax request and add the following query string: Layout=_Script.
To get the content without script section, send a normal ajax request and add the following query string: Layout=_Body.
To get the whole content, send a normal ajax request without specifying any Layout query string.
For example, assuming you have a Sample action in Home controller:
[HttpGet]
public ActionResult Sample()
{
return View();
}
Which returns the following Sample.cshtml view:
<div id="div1" style="border:1px solid #000000;">
some content
</div>
#section scripts {
<script>
$(function () {
alert('Hi');
});
</script>
}
In client side, to get the script section:
<button id="button1">Get Sample Content</button>
<script>
$(function () {
$('#button1').click(function () {
$.ajax({
url: '/home/sample',
data: { Layout: '_Script' },
type: 'GET',
cache: false,
success: function (data) { alert(data); },
error: function () { alert('error'); }
});
});
});
<script>
Example 2 - Return Content Without Layout Using Ajax requests
In this example, you can see how easily you can return content and scripts of the views without layout for ajax requests.
To do so, follow these steps:
Change the _ViewStart.cshtml content to:
#{
if(Request.IsAjaxRequest())
{
Layout = "~/Views/Shared/_Partial.cshtml";
}
else
{
Layout = "~/Views/Shared/_Layout.cshtml";
}
}
Add a new layout page named _PartialView.cshtml having the following content:
#RenderBody()
#RenderSection("scripts", required: false)
Get the view using ajax. For example, assuming you have a Sample action in Home controller:
[HttpGet]
public ActionResult Sample()
{
return View();
}
Which returns the following Sample.cshtml view:
<div id="div1" style="border:1px solid #000000;">
some content
</div>
#section scripts {
<script>
$(function () {
alert('Hi');
});
</script>
}
Get the result using ajax:
<button id="button1">Get Sample Content</button>
<script>
$(function () {
$('#button1').click(function () {
$.ajax({
url: '/home/sample',
type: 'GET',
cache: false,
success: function (data) { alert(data); },
error: function () { alert('error'); }
});
});
});
<script>
As per my understanding I guess you are looking for load the page via Ajax without reloading the entire page.
Did you check the Kool Swap Jquery plugin? Hope this will match your requirement.
I'll cut right to the chase.
After I call RedirectToAction to any of my Action-methods, my JQuery libraries are not loaded until after the page has rendered. (Note: a direct route to the Action Method loads the libraries correctly). I'm not using renderSection for my scripts at the moment, but load the bundles in the header, in the _Layout.cshtml document:
<head>
#Scripts.Render("~/bundles/jquery")
</head>
This goes for all the libraries I try to load in the header, by the way.
I do see any reason why it wouldn't work when I'm redirection. Maybe I don't understand the RedirectToAction method correctly, in which case I would be very happy with an explanation.
An example of the redirect I use, from my controllers:
return RedirectToAction("Confirmation", new{orderId = order.Id});
(Keep in mind, that this problem persists trough all my controllers, and all actions)
Thanks a lot.
I found the error, after studying my Network tab in firebug. Here was the problem:
On redirects after form-posts, I load a jquery script via TempData. I accidentally made an external reference to jquery in that partial view's script.
Controller:
TempData["Message"] = new GenericMessage()
{
Message = "You need to select a date",
MessageType = GenericMessages.danger
};
return RedirectToAction("Index", "Home");
Partial View Script:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript"> $(function () { $('div.alert-generic').delay(3500).fadeOut(); }); </script>
<div class="alert alert-block alert-#genericMessage.MessageType.ToString() alert-generic">
×
#Html.Raw(genericMessage.Message)
</div>
This caused JQuery to be loaded twice (in different versions), on redirects.
After I removed the reference from the script, it all worked perfectly again. Thanks to those of you who tried to help me out, and I apologise for not seeing this error my self, before now.
As the title states, I want to define a section in a partial view.
My code that I've tested with are as follows:
Controller:
public ActionResult Test()
{
return View();
}
public ActionResult PartialTest()
{
return PartialView("_PartialTest");
}
Test.cshtml:
#{
ViewBag.Title = "Test";
}
<h2>Test</h2>
#Html.Action("PartialTest")
_PartialTest.cshtml:
<p>partial Test</p>
#section scripts {
<script type="text/javascript">
$(document).ready(function() {
alert("Test");
});
</script>
}
Placing the section scripts in the Test.cshtml works fine so the problem isn't in the layout.
Anyone know how to do this?
Partial views don't support #section tag. You should add them in the view which references the partial view. See this question for more information: Injecting content into specific sections from a partial view ASP.NET MVC 3 with Razor View Engine.
It basically comes down to the fact that the main view referencing a partial should be responsible for including Javascript, not the partial view itself.
I know this question is super out-dated, but for anyone out there who might still wondering (as I was):
You can actually get around this problem by changing the #section scripts { } portion in your partial views to this:
#using (Html.BeginScriptContext())
{
Html.AddScriptBlock(
#<script type="text/javascript">
//rest of script
);
}
This will allow you to have a script in your partial view and achieve that widget-like structure. Hope this helps!
So the design ideal is to have one page with a couple different 'widgets' in this MVC app. Each 'widget' should be able to submit information back to itself and reload only itself. Simple in web forms, not so much with MVC it seems
First, we have our main page controller, nothing special
public ActionResult Index()
{
return View();
}
Next, we have our main page View. Note that I have tried both Action and RenderAction and there is no change in behavior
#Html.Action("Index", "Monitor", new { area = "Statistics" });
<div id="messages">
#Html.Action("Index", "Messages", new { area = "Data"});
</div>
#section Script {
<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$('#filter').click(function () {
$.ajax({
method: 'POST',
url: '#Url.Action("Index", "Messages", new { area = "Data"})',
success: function(data){
$('#messages').html(data);
}
});
return false;
});
</script>
The Index ActionResult in the Messages Controller:
public ActionResult Index()
{
var model = GetMessages();
return PartialView(model);
}
For the sake of brevity, going to skip the whole of Monitor Index View, and only give a brief version of Messages Index View
#using (Html.BeginForm("Index", "Messages", FormMethod.Post))
{
//Some fields to limit results are here, with style classes
<button type="submit" id="filter">Filter</button>
}
#foreach(var item in Model)
{
//Display results
}
Upon loading the main page, all looks good. The fields to limit results are displayed, as well as messages. I can enter something into the fields, click Filter, and am returned to the main page but! ...the fields have lost their style classes and the messages are unfiltered.
Strange, but more strange is if I again enter information in the fields and click Filter, this time I am not taken to the main page, but get only the Partial View of the Messages Index displayed and the messages are not filtered.
I can't say that the filtering not working is related to this issue or not, but the non-consistent behavior of clicking Filter is the part that bothers me. Anyone like to point out what I am doing wrong in here?
You probably should be using Ajax.BeginForm rather than Html.BeginForm in your widgets. That will let the widgets manage their own posts:
<div id="messages">
#using (Ajax.BeginForm("Index", "Messages", new AjaxOptions { UpdateTargetId = "messages" }))
{
// fields content
}
// other widget content
</div>
The "bizarre" behavior you're seeing is happening because on page load the submit event for your #filter button is being hijacked using jQuery, but after the first replacement of the widget content the #filter submit event is no longer being hijacked so when you click it the whole page is submitted. If you don't want to use Ajax.BeginForm you'll need to use $.on rather than $.click to sign up events, as it will handle events for matching elements which are created after the event sign up script has run.
For example I have some view with action link:
#Html.ActionLink("Action", "Controller")
Action action returns some view:
public ActionResult Action()
{
string someModelForView = "some url i need to redirect after view was fully loaded";
return View("SomeView", someModelForView);
}
What I need is to redirect user to url, defined in someModelForView model after view was fully loaded, and all javascripts on this page were executed. This view might be empty, without any content, I just need to execute some javascript, and after that redirect user to external page. How can accomplish that?
Once the view has been rendered and the JavaScript loaded, you (the server) have already sent your response (encapsulated in the returned ActionResult) to the client (the browser). Thus, you cannot let ASP.NET MVC redirect you – the server has finished handling the request.
You can use JavaScript redirecting instead, though:
// Here goes your JavaScript code that needs to be executed
// ...
// ... and here comes the redirect:
window.location.href = "http://newurl.com";
You could do a redirect directly, as #achristov has suggested. But if you must return SomeView to execute the javascript you can use this:
#model string
<html>
<head>
</head>
<body>
<script type="text/javescript">
$(document).ready( function() {
// all your javascript code...
// ...and then:
window.location = "#Model";
});
</script>
</body>
</html>