Get Relative Url in ASP.NET MVC View - asp.net-mvc

I have an ASP.NET MVC app. I am using Razor in my views. Due to my IT dept. the app sits at a relative root like http://ourdomain.com/appRoot instead of at the top of the domain like http://ourdomain.com. The other challenge here is, appRoot is not static. It actually changes. For that reason, I need to get the name of the relative root via Razor. Currently, I have something like the following:
<input id="hiddenElem" type="hidden" value="12345" />
<script type='text/javascript'>
function doThis() {
var id = $('#hiddenElem').val();
var nextUrl = '#(Request.?)' + '/path/' + id;
alert(nextUrl);
}
</script>
When doThis is called, I want to see the alert window to display /appRoot/path/12345. What do I put into the Razor block associated with nextUrl to get the relative root?

Generally you would use the mvc routing function Url.Action().
function doThis() {
var id = $('#hiddenElem').val();
var nextUrl = '#Url.Action("MyAction","MyController")' + '/path/' + id;
alert(nextUrl);
}
The Html.ResolveUrl() function is fine for resolving relative paths, however, it does not take into consideration routing.
Also, using the Url.Content() function may produce a result you are expecting. For example, in the snippet below:
.Template("<img src='"+ #Url.Content("~/Images/Icons/16/STATE.png")+"'/> <mark>${data.StateName }</mark>");
The image will always resolve no matter where you are calling it from or how you accessed the view withing the root.

Related

knockout + partials + mvc + correct way to bind

Dear fellow programmers
I got myself into a pickle. Ive been in to knockout for like 2 weeks now and im afraid i dont understand the basic idea of it. So be gentle with me.
Situation:
I got a master view layout page /Master to make it simple. Here i got 2 column layout.
On the left a listbox with patients, after clicking one, you will see prescriptions added to the listbox below it. pretty simple...
Dont mind the renderbody here, this is faulty. Just use this image to see 2 big parts. The red en and the yellow.
Now i got the Master working pretty well, when i click on a patient i want to load a specific partial view on the yellow part of the screen. The same with clicking a prescription , then i want to load the prescription partial on the yellow part.
I got these 2 editscreens working but without the master included. these pages look like this:
#model FysioNotes.WebMVC.Models.ViewModels.EditPatientViewModel
form + bindings here
#section scripts {
<script src="~/MyScripts/patientVm.js" />
<script>
$(function () {
ko.applyBindings(new editPatientVm(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model))));
});
</script>
}
You see that i use razor here to fill in the data in the editPatientVm. the js file looks like this :
var patientVm = function (data) ...
var editPatientVm = function (data) ...
var createPatientVm = function (data) ...
This is the same with the prescriptions .
Now when i try to load the prescription partial into the yellow part of the screen, i try to do it like this. And this is probably very faulty....
-- this is at the bottom of the master view --
<script type="text/javascript">
$(document).ready(function () {
$('#tablePrescriptions').on('click', 'tr', function (event) {
var selectedId = $(this).data("prescriptionid");
// this data gets filled without troubles
$(this).addClass('selectedrow').siblings().removeClass('selectedrow');
openDetail("prescription", selectedId);
});
and then this function
function openDetail(type, selectedId) {
if (debug)
alert(type + " : " + selectedId);
var url = baseurl + "/Prescription/Edit?prescriptionId=" + selectedId;
$("#mainContent").load(url);
//CHECK THIS
ko.cleanNode($("#mainContent")[0]);
$("#mainContent").load(url, function () {
//ko.applyBindings(new viewModel(), $("#mainContent")[0]);
ko.applyBindings(new editPrescriptionVm(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(new FysioNotes.WebMVC.Models.ViewModels.EditPrescriptionViewModel(12836)))), document.getElementById("mainContent"));
});
}
like you see , the fixed number 12836 is totally wrong to do it like this. But i wanted to just try if this would work and it did. But apparantly i cant send a js var to razor , because this is impossible.... so this let me to the idea that im doing something completely wrong
the master view has this at the bottom:
<script src="~/MyScripts/patientVm.js"></script>
<script src="~/MyScripts/prescriptionVm.js"></script>
<script>
$(function () {
ko.applyBindings(new masterVm(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model))));
})
</script>
I think i need to put the viewmodels that i need in the partial into my viewmodel of the master maybe ? and then send it trough but this will mean that EVERY viewmodel needs to be on the master. This cant be good for initial loading ! And this seems wrong to me to do it like this , but ive been wrong before....
please help :(
#section scripts cannot work in partials views that are retrieved through ajax... you'll have to execute your code between those tags after the code that retrieves the view in the first place. #section scripts is kept into the request items and rendered when the page is rendered. But since this is ajax, the server has long passed the point where it would keep into account scripts defined between this section

Ember: How to bind action to code generated with JS

I have a simple view which renders set of images depending on given items array (simplified code), using this as I need to collect some other data to 'build' required class name(s):
App.MyView = Ember.View.extend({
buildTemplate: function () {
var itemz = this.get('items');
var classname = 'classNameDependingOnSomeCalculations...';
var out = '<div>';
$.each(itemz, function (index, obj) {
out += '<img {{action myActionHere}} src="' + obj.href + '" alt="" class="'+classname+'"/>';
});
out += '</div>';
return out;
}.property('view'),
defaultTemplate: Ember.Handlebars.compile(
"<div>{{{view.buildTemplate}}}</div>"
)
});
And in template I'm using it as
{{#each myObj in myCollection}}
{{view App.MyView itemsBinding="myObj.items" otherBinding="otherProps" }}
{{/view}}
Unfortunately this way Ember instead of binding the action puts {{action myActionHere}} directly into code...
How can I bind an action instead while building dynamic template?
I'm using Ember 1.1.2
P.S. Or maybe I should use quite other approach for building this view?
There is a workaround to make this work with the view as you've laid it out here... But this is really not the ember way of doing it. If for some reason you need this kind of an approach, I'll append an answer for that, but I'm going to aim to fix the underlying issue.
Instead of doing this as shown here, you should instead have code that looks like the following directly in your JSP:
{{#each myObj in myCollection}}
<div>
{{#each item in myObj.items}}
<img {{action myActionHere}} src={{item.href}} alt='' class={{classNameFunction}}/>
{{/each}}
</div>
{{/each}}
If your reason for wanting to do this as a view is so that you can reuse this functionality without rewriting the code, take a look at partials which are specifically designed for that purpose.

ASP.NET MVC 3 - Add/Remove from Collection Before Posting

I have a model that contains a collection, such as this:
class MyModel
{
public List<MySubModel> SubModels { get; set; }
}
In the view, I want to dynamically add/remove from this list using Javascript before submitting. Right now I have this:
$("#new-submodel").click(function () {
var i = $("#submodels").children().size();
var html = '<div>\
<label for="SubModels[' + i + '].SomeProperty">SomeProperty</label>\
<input name="SubModels[' + i + '].SomeProperty" type="textbox" />\
</div>'
$("#submodels").append(html);
});
This works, but it's ugly. And, if I want to show those labels/textboxes for the existing items, there's no clean way to do that either (without duplicating).
I feel like I should be able to use Razor helpers or something to do this. Any ideas? Help me stay DRY.
You approach may lead to unexpected errors if you when you are removing or adding the divs. For example you have 4 items, you remove the first item, then $('#submodels').children().size() will return 3, but your last inserted div has the name attribute value set SubModels[3].SomeProperty which results in a conflict. And if your posted values contain SubModels[1] but not SubModels[0] the default model binder will fail to bind the list (it will bind it as null). I had to learn this the hard way...
To eliminate the aforementioned problem (and your's) I suggest you do something like this:
$("#addBtn").click(function() {
var html = '<div class="submodel">\
<label>SomeProperty</label>\
<input type="textbox" />\
</div>'; // you can convert this to a html helper!
$("#submodels").append(html);
refreshNames(); // trigger after html is inserted
});
$(refreshNames); // trigger on document ready, so the submodels generated by the server get inserted!
function refreshNames() {
$("#submodels").find(".submodel").each(function(i) {
$(this).find("label").attr('for', 'SubModels[' + i + '].SomeProperty');
$(this).find("label").attr('input', 'SubModels[' + i + '].SomeProperty');
});
}
Then your view (or even better an EditorTemplate for the SubModel type) can also generate code like:
<div class="submodel">
#Html.LabelFor(x => x.SomeProperty);
#Html.EditorFor(x => x.SomeProperty);
</div>
It would also be possible to convert the code generation to a html helper class, and use it in the EditorTemplate and in the JavaScript code
I would recommend you going through the following blog post.

What is the equivalent to Page.ResolveUrl in ASP.NET MVC?

What is the equivalent to Page.ResolveUrl in ASP.NET MVC available in the Controller?
It is Url.Content:
ASPX:
<link rel="stylesheet" href="<%= Url.Content("~/Content/style.css") %>" type="text/css" />
Razor:
<link rel="stylesheet" href="#Url.Content("~/Content/style.css")" type="text/css" />
This should do what you're looking for...
System.Web.VirtualPathUtility.ToAbsolute("~/")
Here are a whole bunch of ways to resolve a path that uses that application root operator (~)
UrlHelper.Content
HttpServerUtility.MapPath
WebPageExecutingBase.Href
VirtualPathUtility.ToAbsolute
Control.ResolveUrl
To call any method with inline code on an asp.net page, the method either needs to be exposed as an instance variable on the current object, or available as a static/shared method.
A typical MVC page gives us access to quite a few of these as properties via the WebViewPage. Ever wonder when you type #ViewData, you get magically wired up to the ViewData? That's because you have hit a property exposed by the MVC page you're on.
So to call these methods, we don't necessarily refer to the type they represent, but the instance property that exposes them.
We can call the above instance methods like this (respectively):
href="#Url.Content("~/index.html")"
href="#Server.MapPath("~/index.html")"
href="#Href("~/index.html")"
We can do this to call a shared method that doesn't need an instance:
href="#VirtualPathUtility.ToAbsolute("~/index.html")"
AFAIK, an MVC page doesn't automatically create an instance of anything from the System.Web.UI namespace, from which ResolveUrl inherits. If, for some reason, you really wanted to use that particular method, you could just new up a control and use the methods it exposes, but I would highly recommend against it.
#Code
Dim newControl As New System.Web.UI.Control
Dim resolvedUrl = newControl.ResolveUrl("~/index.html")
End Code
href="#resolvedUrl"
That all said, I would recommend using #Url.Content as it fits best with MVC paradigms
UrlHelper.Content() does the same thing as Control.ResolveUrl().
For Further References:
http://stephenwalther.com/archive/2009/02/18/asp-net-mvc-tip-47-ndash-using-resolveurl-in-an-html.aspx
You don't need to do this anymore in Razor v2.0/ASP.NET MVC 4.
Just use the "~" in a razor page and it will resolve it for you.
<link rel="stylesheet" href="~/Content/style.css" type="text/css" />
Source
In my case, I find #Href not being enough in the way it deals with query strings in a URL. I prefer to wrap it inside the Raw method:
<script>
var isKendoWindow = false;
var myTimeOut;
clearTimeout(myTimeOut);
var sessionTimeout = (#Session.Timeout * 60000) - 5;
function doRedirect() {
if (!isKendoWindow)
window.location.href = '#Html.Raw(Href("~/Logon.aspx?brandid=" + SessionController.LandingBrandId + "&errCode=5055"))';
}
myTimeOut = setTimeout('doRedirect()', sessionTimeout);
</script>
Or you can create your own version like this:
public static IHtmlString ResolveUrl(this HtmlHelper htmlHelper, string url)
{
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
return htmlHelper.Raw(urlHelper.Content(url));
}
Another way to solve this issue:
Resolve the url in a code block at the top of the page or in code behind.
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
Layout = "~/Pages/Shared/_IndexLayout.cshtml";
String img1 = Url.Content("~/img/people11.jpg");
}
Then use the variable in the html.
<div class="col-12 col-lg-8" style="background-image: url('#img1');"> </div>
Server.MapPath() //returna full path
or
url.content()
try using Server.MapPath().

Escaping JavaScript string literals in views

Is there a utility function for escaping JavaScript in ASP.NET MVC views? I often need to init a little snippet of JavaScript using some values from the view; for instance I may have something like:
<script type="text/javascript">
var page = new Page({ currentUser: "<%= Model.UserName %>" });
page.init();
</script>
I would expect something like:
<script type="text/javascript">
var page = new Page({ currentUser: "<%= Html.JavaScriptEscape(Model.UserName) %>" });
page.init();
</script>
I could, of course, write the function myself. But since there are already built-in utilities form HTML encoding, and since one of the selling points of ASP.NET MVC is that the <% %> is the default rendering mode, and since what I'm trying to achieve is quite common, it makes me wonder why I cannot find anything like that already built-in. Is there, for instance, an easy and elegant way to serialize an object to JSON in views?
Or am doing something against ASP.NET MVC principles? When I hit a problem like this, it usually makes it think that either I’m doing something wrong since I assume that the framework designers spent some time thinking about real world scenarios.
In .NET 4, The HttpUtility class has a variety of static encoding methods for various contexts, including a JavaScriptStringEncode method for this particular purpose.
It's often simpler to just use JSON deserialization, though.
In MVC 5 using Razor templates, the following is possible:
<script type="text/javascript">
var page = new Page({ currentUser: #Html.Raw(Json.Encode(Model.UserName)) });
page.init();
</script>
After some time working in ASP.NET MVC, I concluded that (most likely) there is no build-in helper for it. Of course, it's trivial to write your own. Here is it for the sake of completeness:
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace MyProject.Helpers
{
public static class JsonExtensions
{
public static string Json(this HtmlHelper html, object obj)
{
JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
return jsonSerializer.Serialize(obj);
}
}
}
In a view, it can be used as follows:
<script type="text/javascript">
var page = new Page(<%= Html.Json(new { currentUser: Model.UserName } ) %>);
page.init();
</script>
In my case I needed a string not a json object and this is for Asp.Net Core:
#functions{
public Microsoft.AspNetCore.Html.IHtmlContent ToJS(string value)
{
return Html.Raw("'" + value.Replace("'", "\\'").Replace("\r", "\\r").Replace("\n", "\\n") + "'");
}
public Microsoft.AspNetCore.Html.IHtmlContent ToJS(int value)
{
return Html.Raw("" + value);
}
}
This will escape the ' and end of line characters. Also it leaves numbers (int) as a number. This could be overloaded to include float, decimal, etc. as needed.
So, I don't have to think about it or do anything different for each type:
var serverName = #ToJS(m.ServerName);
var appSiteUrl = #ToJS(m.SiteUrl);
var facebookId = #ToJS(m.FacebookAppId);

Resources