I really dislike page loads, I think they detract from the user experience, so I'm trying to make my web application heavily AJAX-ified.
When the user clicks on "Add new", Javascript generates a form based on a model using Razor, with the following code:
<script type="text/javascript">
var strNewCategoryForm = '<div><i class="glyphicon glyphicon-folder-open rightfolderpadding"></i>#using (Html.BeginForm("AddCategory", "Password", FormMethod.Post, new { #class="newcategoryform", role = "form", id="[1]" })) { #Html.AntiForgeryToken() #Html.PasswordFor(m => m.Category_ParentID, new { type = "hidden", value = "0" }) #Html.PasswordFor(m => m.CategoryName, new { type = "text" }) <span class="btn-group groupalign"><i class="glyphicon glyphicon-save"></i>Save</span> }</div>';
</script>
The code works great, Razor is able to generate the form within the string, so I dont have any issues with making this work.
However, for code readability and ease of development, it's not that great.
I'm still quite new to MVC and razor, so I'm just wondering, is there a better or "MVC/Razor standard" way of doing this, that I don't know about?
Edit:
In case anyone is interested, I've used both bits of Exception's answers:
In the partial view:
#model Secure_Password_Repository.Models.Category
<div><i class="glyphicon glyphicon-folder-open rightfolderpadding"></i> \
#using (Ajax.BeginForm("AddCategory", "Password", new AjaxOptions { HttpMethod="post", OnFailure="" }, new { #class="newcategoryform", role = "form", id="[1]" }))
{
#: \
#Html.AntiForgeryToken() #: \
#Html.HiddenFor(m => m.Category_ParentID, new { value = "0" }) #: \
#Html.TextBoxFor(m => m.CategoryName) #: \
#: <span class="btn-group groupalign"><i class="glyphicon glyphicon-save"></i>Save</span> \
}</div>
In the main view:
<script type="text/javascript">
var strNewCategoryForm = '#Html.Partial("_NewCategoryForm")';
</script>
The "\" at the end of each line in the partial view tell JavaScript that each line is continuation of a string value.
Answer 1 :-
If You are so keen to AJAX-ify your web app then better way is to use Ajax helper in Asp.MVC such as
#Ajax.BeginForm() or #Ajax.ActionLink() and Helpers like #Html.Partial() ,
#Html.RenderPartial() etc. are also handy for asynchronously loading data.
Their Basic Usage(I m taking hypothetical example here) :-
#Ajax.ActionLink("Show",
"Show",
null,
new AjaxOptions { HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "dialog_window_id",
OnComplete = "your_js_function();" })
#using (Ajax.BeginForm("Edit", "Cars", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
OnSuccess = "updateSuccess"
}, new { #id = "updateCarForm" })) { ..... }
Follow this link :- http://www.codeguru.com/csharp/.net/working-with-ajax-helper-in-asp.net-mvc.htm
Above link will be helpful for you to understand building Forms with Ajax Helpers.
and one thing more the way you are building forms with razor syntax in javascript is not at all a good option 'in my opinion'.
Answer 2 :-
A small demo how to build a completely ajax-ified form which donot require any page reload:
#using (Ajax.BeginForm("Index", "Home", null, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "Mydiv" }, new { #id = "frm" , #style ="width:700px" }))
{
//Your HTML here
}
Make above View a Partial View say 'Index.cshtml' and then return it this way through Controller as :
Public ActionResult Index()
{
return PartialView("Index");
}
Making Partial Views and loading Partial views through Jquery is handy to make unobtrusive forms.
This is more of an HTML-thing than MVC/Razor, as you are essentially asking on how to embed templates into your website. AFAIK html doesn't Support templating out of the box just yet, so you'd Need some JavaScript for that (in your case right now, you're probably using jquery)
Most template engines like knockoutjs, handlebars, etc. (maybe even jquery) support embedding templates similar to this:
<script type="text/html" id="my_template">
<div>
<p>
My template
</p>
</div>
</script>
The browser would not render that html, but a JavaScript library would use it (optionally doing some runtime data-binding) and display it.
Note: you can obviously put the html from that template into a partial view:
_MyTemplate.cshtml:
<div>
<p>
My template
</p>
</div>
View:
<script type="text/html" id="my_template">
#Html.Partial("MyTemplate")
</script>
Most template engines also support loading templates asynchronously, in which case you just render them the partial view alone.
Hope this helps a little.
Related
I have an ASP.NET MVC C# web application.
In my layout file I have the following ajax call
<li class="PriceModal">
#Ajax.ImageActionLink("/InternalDB/Content/downloadCSV.png", "Pareto", "35px", "35px", "PartialViewPriceCalculator",
"PriceCalculator", new {#Pareto = "active" }, new AjaxOptions
{ UpdateTargetId = "PriceCalculator", InsertionMode = InsertionMode.Replace, HttpMethod = "GET"}, new { #style = "padding-top:30px" })
</li>
When this Ajax link is clicked a bootstrap Modal is loaded and a partial view is load inside the Bootstrap Modal. At this point the Bootstrap modal and the partial view inside are sitting on top of my existing page.
My problems start when I place jQuery code inside the partial View. I have found a way to append the jQuery code (partial) to the DOM with the following code
$('body').on('jQuery event to be placed here(focus,change,mouseenter)', 'class/ID to be Placed here', function (e) {
// jQuery events
});
This will allow me to append to the DOM loaded by my page on the background.
However this has a lot of issues as well. How do I append a jQuery plug in called Chosen to a dropdownlist which is sitting inside my partial view.
I have experimented by putting the jQuery inside the Ajax Options and using the Onsuccess property but this seems to have problems as well.
<li class="PriceModal">
#Ajax.ImageActionLink("/InternalDB/Content/downloadCSV.png", "Pareto", "35px", "35px", "PartialViewPriceCalculator",
"PriceCalculator", new {#Pareto = "active" }, new AjaxOptions
{ UpdateTargetId = "PriceCalculator", InsertionMode = InsertionMode.Replace, HttpMethod = "GET", OnSuccess = "$('.chosen2').chosen({ allow_single_deselect : true })"}, new { #style = "padding-top:30px" })
</li>
Is there a general approach to loading all of the needed jQuery inside a popup partial view or i just need to find hacks for each specific situation?
You can just use the document.ready event to execute JavaScript when your PartialView finishes loading:
<div id="partialViewHere">
</div>
<script type="application/javascript">
$(function() {
// load whatever you need here
};
</script>
I use this on an application to add events to buttons loaded through different PartialViews and it works perfectly.
I'm trying to load the scripts for my application. Do not work so I checked a simple alert ('test11');. It also does not work, the script is not loaded at all?
It uses standard _Layout of MVC4 generated by Visual Studio 2013.
View Details.cshtml
#model xzy.Models.Album
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
#using (Html.BeginForm("Details", "Store", FormMethod.Post,
new
{
id = "someID",
data_oneAction = #Url.Action("one"),
data_twoAction = #Url.Action("two")
}))
{
<fieldset
<label for="Formats">Select format</label>
#Html.DropDownList("Formats", ViewBag.Formats as SelectList,
new { id = "FormatsID", #class = "xxx" })
<input type="submit" value="Submit" id="SubmitID" } />
</fieldset>
}
<script src="#Url.Content("~/Scripts/first_file.js")"></script>
<script src="#Url.Content("~/Scripts/second_file.js")"></script>
Script first_file.js
$(function () {
$('#FormatsID').change(function () {
alert('Test11');
});
});
UPDATE
Yaakov Ellis, it works. But when I have a script with the update, then it not works.
$(function () {
$('#FormatsID').change(function () {
alert('Test11');
var URL = $('#someID').data('oneAction');
$.getJSON(URL + '/' + $('#FormatsID').val(), function (data) {
alert('Test22');
});
});
});
The content of first_file.js is dependent on the presence of jQuery. I suspect that if you check your console log, you will see errors on the first line of this file.
This is confirmed by the fact that when you add in a jQuery reference prior to this file (as you wrote in your comment), it works.
So if you want to use this script as-is, be sure to include jQuery first.
If you just want a better test of the first_file.js file, then remove all of the jQuery stuff from it. Its sole contents should be one line with alert('Test11'); on it.
I have the following code in a MVC c# Razor view:
#{string url = "/Projects/_MonthRangesScriptsPartial";}
#using (Ajax.BeginForm(
"_MonthRanges",
"Projects",
new { id = ViewBag.InputResourceID },
new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "MonthRanges",
InsertionMode = InsertionMode.Replace,
OnComplete = "updategraphArrayScript(#url,#ViewBag.InputResourceID)"
}))
The code works almost perfectly, but I would like to resolve the c# variables to their values in the OnComplete line.
That is to say, the Razor engin should resolve the code to (for example):
<form action="/Projects/_MonthRanges/55"
data-ajax="true" data-ajax-complete="updategraphArrayScript(/Projects/assets,55)"
...>
Rather than:
<form action="/Projects/_MonthRanges/55"
data-ajax="true" data-ajax-complete="updategraphArrayScript(#url,#ViewBag.InputResourceID)"
...>
Simply create another variable string:
#{
string url = "/Projects/_MonthRangesScriptsPartial";
string onComplete = String.Format("updategraphArrayScript({0}, {1})", url, ViewBag.InputResourceID);
}
#using (Ajax.BeginForm(
"_MonthRanges",
"Projects",
new { id = ViewBag.InputResourceID },
new AjaxOptions {
HttpMethod = "POST",
UpdateTargetId = "MonthRanges",
InsertionMode = InsertionMode.Replace,
OnComplete = #onComplete
}))
Take off the # when you assign your OnComplete value. You don't need it since you're already in the context of server side code.
# is used to switch html rendering context to server side code, and <text></text> is used to switch from server side context to html rendering. In example given above, you're already in the server side code context of the BeginForm() method so the # is not needed. You're not writing the value of onComplete to the page, BeginForm() will handle all that.
I have a page with a video at the top and a list of videos you can choose from. Currently, clicking a link in the video list will reload the entire page. I need it to only refresh the partial view I have containing the video at the top of the page.
I saw several posts here on SO showing how to reload partial views with JQuery, but couldn't get it to work correctly in my situation. I'm unsure how to pass the correct id of the video along.
Controller:
public ActionResult Videos(int topVideo = 0)
{
VideosModel model = new VideosModel();
model.Videos = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).ToList();
if (topVideo == 0)
model.TopVideo = model.Videos.First();
else
{
model.TopVideo = model.Videos.Where(x => x.StatsVideoId == topVideo).FirstOrDefault();
if (model.TopVideo == null)
model.TopVideo = model.Videos.First();
}
return View(model);
}
View:
#model Project.Models.VideosModel
<section class="videos">
<div id="top_video">
#{Html.RenderPartial("StatsVideo", Model.TopVideo);}
</div>
<ul>
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
</ul>
</section>
If there's any more information needed, please let me know.
After several hours of bashing my head against the wall, I got it to work! Just as a reference to anyone else in the future who's viewing this article, here's how I got it to work:
I set the onclick of the link to point to a javascript method, passing in the id of the video as a parameter:
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#" onclick="updateTopVideo(#item.StatsVideoId)">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
And then I included this script in the view at the bottom:
<script>
var updateTopVideo = function (itemId) {
var url = '#Url.Content("~/Home/StatsVideo/")';
url = url + itemId;
$.get(url, "", callBack, "html");
};
var callBack = function (response) {
$('#top_video').html(response);
};
</script>
Finally, I added a method to my controller that would return the partial view needed for the video at the top of the screen:
public ActionResult StatsVideo(int Id)
{
IStatsVideo vid = StatsVideoService.GetEntity(new Lookup(TableStatsVideo.StatsVideoId, Id));
if (vid == null)
vid = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).FirstOrDefault();
return PartialView(vid);
}
This code should be fairly easy to understand. Basically, the onclick calls the first javascript method, which then calls the controller. The controller builds the partial view and returns it. The first javascript method passes it to the second javascript method which sets the html of the div "top_video" to be the returned partial view.
If anything doesn't make sense, or anyone's having trouble with this in the future, let me know and I'll do my best to offer some help.
I think there may be several confusing and inconsistent elements here.
First, you are returning a full view instead of a partial view. This reloads all containing elements, not just the part that is relevant to your partial view.
Second, you are using Url.Action, which only generates the url. I would recommend using Ajax.ActionLink, which allows you to do fully ajax calls, refreshing the content of your partial div and updating a target div element.
instead of:
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
try the more modern solution
<div class="videoList">
#Ajax.ActionLink(
"Videos",
"Home",
"new { topVideo = item.StatsVideoId },
new AjaxOptions {
HttpMethod = "GET",
OnSuccess = "handleSuccess"
}
)
</div>
This way you can be very specific on what you want each link to do, and you can pass along multiple parameters as well as define a callback function. You can also use "UpdateTargetId" in your ajax options to load your newly refreshed partial view into a DOM element.
You can remove the around the image and just store the url generated by the Url.Action in a data-href attribute.
Then you can use the jquery load method to load the data:
$(".videolist>img").click(function () {
$("#content").load($(this).data("href"));
});
I created a fiddle that loads content dynamically here, so you can play with it if you want: http://jsfiddle.net/bTsLV/1/
I'm trying to use json for my web page's globalization options.. id like to change the labels of my form just by using a little dropdownbox and without refreshing the whole page and more interesting part is i got more than two form in my view.
so far i have done this:
My Json:
public JsonResult Globalx(String incoming)
{
System.Globalization.CultureInfo Cult = new System.Globalization.CultureInfo(incoming, true);
System.Threading.Thread.CurrentThread.CurrentCulture = Cult;
System.Threading.Thread.CurrentThread.CurrentUICulture = Cult;
Resources.Global.Culture = System.Threading.Thread.CurrentThread.CurrentCulture;
Global.ResourceManager.GetResourceSet(Cult, false, true);
ViewData["Name"] = Global.Name;
ViewData["Surname"] = Global.Surname;
ViewData["Birth"] = Global.Birth;
String lnginfo = Resources.Global.Culture.TwoLetterISOLanguageName.ToString();
ViewData["Languages"] = new SelectList(myList, "Value", "Text", lnginfo);
return Json(ViewData, JsonRequestBehavior.AllowGet);
}
My View:
#model MyCustomers.Models.Customers
#{
ViewBag.Title = ViewData["NewCustomer"];
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script type="text/javascript" language="javascript">
$(document).ready(function () {
function changeLang() {
var lang = $("#LanguageBox").val();
$.getJSON('#Url.Content("~/Home/People/")', { incoming: lang }, function (data) {
// what should i do here to get my label's language changed?
})
}
}
</script>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", id = "LanguageForm" }))
{
<fieldset>
<legend>#ViewData["LanguagesTitle"]</legend>
#Html.DropDownListFor(x => x.SelectedLanguage, (SelectList)ViewData["Languages"], new { onchange = "changeLang()", id = "LanguageBox" })
</fieldset>
}
#using (Html.BeginForm("PeopleForm", "Home", FormMethod.Post, new { enctype = "multipart/form-data", id = "PeopleForm" }))
{
<fieldset>
<legend>#ViewData["SalesContract"]</legend>
<div>
<div class="Name">
#Html.Label(ViewData["Name"].ToString()) <!--> HERE </!-->
#Html.EditorFor(x => x.People.Name)
</div>
<div class="Surname">
#Html.Label(ViewData["Surname"].ToString()) <!--> HERE </!-->
#Html.EditorFor(x => x.People.Surname)
</div>
<div class="Birth">
#Html.Label(ViewData["Birth"].ToString()) <!--> AND HERE </!-->
#Html.EditorFor(x => x.People.Birth)
</div>
</div>
</fieldset>
}
No im not actually using this method im refreshing the whole page each time to change the language of my labels but some friend of mine told me it could be done without refreshing and the first thing that came in my mind was Json.. I dont know if its possible or not im just trying. Any other ideas are wellcome.
I think the title is a little confusing and im asuming my problem here is understood so if anyone can find a better title please attempt to fix it.
In your Json result you would need to identify each of the labels that you have provided the text for, say each label has a Json object:
Id: 'label1',
Text: 'Enter your first name'
You provide one of these objects for each label on your page in an array,
{Id: 'label1', Text: 'Enter your first name'},
{Id: 'label2', Text: 'Enter your second name'},
{Id: 'label3', Text: 'Enter your telephone number'},
Then you deal with each of these on the requesting end,
$.getJSON('#Url.Content("~/Home/People/")', { incoming: lang }, function (data) {
for(i = 0; i < data.length; i++){
$('#'+data[i].Id).Html(data[i].Text);
}
})
I'm not 100% sure that Html will be the best thing to use - there may be sub DOM elements created by MVC that would need to be taken into account in your selector.
If you wanted to stick to the method you're using now you'll need to hard code each of the assigned values one at a time.
$.getJSON('#Url.Content("~/Home/People/")', { incoming: lang }, function (data) {
$('#Name').Html(data['Name']);
$('#Birth').Html(data['Birth']);
})