I have a grid filled with notes and I want to be able to add a new note. This works using two different views, but that makes the CreateNote view open a new window. I want this to open in the same window. So instead of a View I use a PartialView. This works, but the "#using (UI.koform(Model, null))" is seen as html so the knockoutjs doesn't work. How can I make this work in a partial view?
Code:
The view:
[...]
<script type="text/javascript">
(function() {
$('#load-partial').click(function() {
$('#partial').load('#Url.Action("CreateNote", "Entity", new {modelEntity = #Model.meta.entity})');
});
})();
</script>
<div id="partial"></div>
<button type="button" id="load-partial">Create Note</button>
The action:
public ActionResult CreateNote(
[ModelBinder(typeof(Models.JsonModelBinder))]
NoteModel Model, string cmd, string modelEntity)
{
[...]
return PartialView("CreateNotePartial",Model);
}
The Partial view:
<%# Control Language="C#" Inherits="test.Web.Framework.Core.ViewUserControl<test.Web.Framework.Areas.Administration.Models.NoteModel>" %>
#using (UI.koform(Model, null))
{
<div class="ui-state-highlight ui-corner-all highlight" data-bind="visible: meta.message">
<span class="ui-icon ui-icon-info"></span><strong data-bind="text: meta.message">
</strong>
</div>
Subject:
<input type="text" data-bind="value:subject" />
<span data-bind="text: subject"></span>
<br />
Text:
<input type="text" data-bind="value:text" />
<br />
set values
<div class="dialogButtons">
<button onclick="$('##Model.meta.modelname').koform('submit');">
Save</button>
</div>
}
It looks like you're mixing View engines. Your control definition is using ASPX View engine syntax (<%# %>) while your using statement is using Razor. My guess is if you changed the code to this it would work:
<% using (UI.koform(Model, null))
{ %>
<%-- HTML --%>
<% } %>
Related
Background: I work with ASPX files and webforms (as my day job) but have never really worked with Razor (.cshtml).
I am trying to create a website that logs into Steam using Owin.Security.Providers.
When i do Install-Package Install-Package Owin.Security.Providers and implement the API from https://steamcommunity.com/dev/apikey, I noticed that the corresponding button is implemented in Razor as shown below.
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) {
#Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
#foreach (AuthenticationDescription p in loginProviders) {
<button type="submit" class="btn btn-default" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</div>
Questions:
Is there a way to get the same function above in an ASPX page instead of in a CSHTML page?
Is it the following code that gives the button the link for the login?
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) {
Razor can be triviaally converted to ASPX syntax.
If you're using ASPX syntax within an ASP.NET MVC context you can use the Html helpers, just like in Razor:
<% using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) { %>
<%= Html.AntiForgeryToken %>
<div id="socialLoginList">
<p>
<% foreach( AuthenticationDescription p in loginProviders ) { %>
<button type="submit" class="btn btn-default" id="<%= p.AuthenticationType %>" name="provider" value="<%= p.AuthenticationType %>" title="Log in using your <%= p.Caption %> account"><%= p.AuthenticationType %></button>
<% } // foreach %>
</p>
</div>
<% } // using %>
(NOTE: Use <%: instead of <%= for output that should be HTML-encoded). I don't know what strings in your output should be encoded or not.
If you're using it in a non-MVC context then you'll need to replace the Html helpers with literal HTML. Do not use WebControls (like <asp:Form>) because you lose control over the markup:
I have a question concerning updating a partial view from another partial view where the first view is contained.
I have 4 dropdowns that are populated based on the previous selections, then the user may submit their selections and a database is queried and a table is populated based on their selections. I should note that I am very new to asp.net mvc and it's all still quite confusing to me.
Below is my code:
<form action="/Home/LoadRelease" method="post" style="text-align: center;">
#*Headers*#
<div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
<div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
<div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
<div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>
<br />
#*Dropdowns*#
<select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
#Html.Partial(#"Dropdowns\_BusinessArea", Model.ProjectViewModels);
</select>
<select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
<select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
<select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
<input type="submit" id="GoButton" style="visibility:hidden;" value="Go" />
</form>
<form id="ReleaseTableBody" style="text-align:center;">
#Html.Partial("_TableBody", Model.OpenCloseViewModels) //I want to update this.
</form>
<br />
and Home/LoadRelease:
[HttpPost]
public ActionResult LoadRelease(string Project, string Release)
{
var ProjectID = _ProblemReportsDB.ProjectMaps
.Where(r => r.Project == Project)
.Select(r => r.ID).FirstOrDefault();
ViewBag.Project = Project;
var Releases = from row in _ProblemReportsDB.PlannedOpenCloses
where (row.Project == ProjectID)
select row;
return PartialView("_TableBody", Releases.ToList());
}
The above loads the partial view "_TableBody", but actually directs to the page containing only the contents of _TableBody.
Ideally, I would remain on the page displaying and only update the _TableBody section of the page. I think I understand why it is currently failing, I'm telling it to run the action /Home/LoadRelease, which returns the _TableBody partial view, which it loads.
I'm having trouble figuring out how to make it only update the _TableBody partial view.
Thanks for any help you can offer.
EDIT:
Attempting Jasens method I have begun using an ajax function: Still loads to another page instead of updating the partial:
Code:
#using (Html.BeginForm("LoadRelease", "Home", FormMethod.Post, new { id = "DropDownForm", style="" }))
{
#*Headers*#
<div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
<div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
<div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
<div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>
<br />
#*Dropdowns*#
<select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
#Html.Partial(#"Dropdowns\_BusinessArea", Model.ProjectViewModels);
</select>
<select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
<select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
<select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
<button type="submit" id="GoButton" style="visibility:hidden;">Go</button>
}
#*</form>*#
<form id="ReleaseTableBody" style="text-align:center;">
#Html.Partial("_TableBody", Model.OpenCloseViewModels)
</form>
<br />
In index: (Parent of _DropDownBody):
<script src="~/Scripts/jquery-1.10.2.js">
$(document).ready(function () {
$("#DropDownForm").on("submit", function (event) {
event.preventDefault();
var form = $(this);
var Project = $('#ProjectDropDown').val();
var Release = $('#ReleaseDropDown').val();
alert(Project);
$.ajax({
url: form.attr("action"),
method: form.attr("method"),
data: form.serialize()
})
.done(function (result) {
$("#ReleaseTableBody").html(result);
});
});
});
</script>
Using A. Burak Erbora's method produces the same issue as well. Am I missing something?
Final edit: Jasen's answer worked and allowed me to update a partial view without redirecting. Still having issues getting the partial to show my content, but as far as the question goes - Jasen's answer works!
Submitting a form will cause navigation. Since you want to stay on the same page you'll need to trap the submission event and use AJAX to update your page.
Main View
#using(Html.BeginForm("LoadRelease", "Home", FormMethod.Post, new { id = "DropDownForm", style = "" })
{
<!-- your drop down inputs -->
<button type="submit">Go</button>
}
<form id="ReleaseTableBody" style="text-align:center;">
#Html.Partial("_TableBody", Model.OpenCloseViewModels) //I want to update this.
</form>
Then the page script (don't forget to load jquery.js before this). Also note if you are embedding partial views you need to move this script "up" to the parent since #section will not render in partials.
<script src="jquery.js"></script>
<script>
$(document).ready(function() {
$("#DropDownForm").on("submit", function(e) {
// prevent default submission
e.preventDefault();
// do AJAX post instead
var form = $(this);
$.ajax({
url: form.attr("action"),
method: form.attr("method"),
data: form.serialize()
})
.done(function(result) {
// replace content
$("#ReleaseTableBody").html(result);
});
});
}
</script>
Controller action unchanged
[HttpPost]
public ActionResult LoadRelease(string Project, string Release)
{
// search
return PartialView("_TableBody", results);
}
First off, I'd recommend you use html helpers. What you seem to need here is an ajax call instead of a standard form post. Instead of
<form action="/Home/LoadRelease" method="post" style="text-align: center;">
you can use
#using (Ajax.BeginForm("LoadRelease", "Home", options)){
#*Headers*#
<div id="BusinessAreaLabel" class="inline" style="width:14em;">Business Area</div>
<div id="GenericProjectLabel" class="inline" style="width:13em;">Generic Project</div>
<div id="ProjectLabel" class="inline" style="width:17em;">Project</div>
<div id="ReleaseLabel" class="inline" style="width:13em;">Release</div>
<br />
#*Dropdowns*#
<select id="BusinessAreaDropDown" name="BusinessArea" onchange="javascript: FillGenericProject(); FillProject(); FillReleases();" style="width: 13em;">
#Html.Partial(#"Dropdowns\_BusinessArea", Model.ProjectViewModels);
</select>
<select id="GenericProjectDropDown" name="GenericProject" onchange="javascript: FillProject(); FillReleases();" style="width: 13em;"></select>
<select id="ProjectDropDown" name="Project" style="width: 17em;" onchange="javascript: FillReleases();"></select>
<select id="ReleaseDropDown" name="Release" style="width: 13em;"></select>
<input type="submit" id="GoButton" style="visibility:hidden;" value="Go" />
}
and somewhere in your html you have:
<div id="ReleaseTableBody">
#Html.Partial(_TableBody", Model.OpenCloseViewModels)
</div>
you will need to define the options object for the Ajax helper like:
var options = new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "ReleaseTableBody",
OnBegin = "OnCallbackBegin",
OnFailure = "OnCallbackFailure",
OnSuccess = "OnCallbackSuccess",
LoadingElementId = "loading"
};
you can also use the event callback methods if you define their JavaScript functions like:
<script>
function OnCallbackBegin() {
$(".btn-loading-icon").show();
$(".btn-loading-text").hide();
}
function OnCallbackSuccess(data) {
//alert("onSuccess: result = " + data.result);
$(".btn-loading-icon").hide();
$(".btn-loading-text").show();
SomeOtherFunction();
}
I would also advise using the #Html.DropdownFor helper for your dropdowns.
I have an main overview.cshtml page that has the following code to run a partial:
<div>
Html.RenderAction("UserProjMetric", "Users", new {id = Model.Id});
</div>
Inside of that partial it looks like so:
#model RecruiterMetricsViewModel
<div>
<div>
<input type="text" id="dates start" />
<input type="text" id="dates end" />
<input type="submit" onclick="runAjax()" />
<table>
#foreach(var data in Model.Metrics)
{
<tr> <td>#data</td> </tr>
}
</table>
</div>
I want to make the onclick run some ajax that will reload the entire partial. The user has the ability to specify a date range. When they do that then click submit I want the entire partial to be reloaded.
How do I do that and is that possible?
This should do what you want. A bit more verbose than Jonesy's comment, perhaps they would accomplish the same thing. You could try replacing Html.BeginForm with Ajax.BeginForm I suppose.
// overview.cshtml
<div id="UserProjWrapper">
Html.RenderAction("UserProjMetric", "Users", new {id = Model.Id});
</div>
// UserProjMetric.cshtml
#model RecruiterMetricsViewModel
#using(Html.BeginForm("UserProjMetric","Users",null,FormMethod.Post, new {id = "UserProjForm" }))
{
<input type="text" name="startDate" class="dates start" />
<input type="text" name="endDate" class="dates end" />
<input type="submit" value="Submit"/>
<table>
#foreach(var data in Model.Metrics)
{
<tr> <td>#data</td> </tr>
}
</table>
}
<script>
$(function() {
$('#UserProjForm').submit(function(e) {
e.preventDefault();
$.post($(this).attr("action"),$(this).serialize(),function(response) {
$('#UserProjWrapper').html(response);
});
});
});
</script>
I have a website in Asp.Net that I am trying to port to MVC 3 and I have only worked with MVC 2 before. I stumbled across the following asp function
<div class="popup-holder">
<ul class="popups">
<asp:Repeater runat="server" ID="ourTeamRepeater" OnItemDataBound="ourTeamRepeater_ItemDataBound">
<ItemTemplate>
<asp:Panel ID="pnlTeamMember" runat="server">
<li id="TeamMember" runat="server" class="memberImage">
<asp:Image runat="server" ID="memberImg" />
</li>
<div class="popup">
<div class="img-holder">
<asp:Image runat="server" ID="memberImgBig" />
</div>
<div class="popup-text-t">
<div class="close">
close
</div>
</div>
<div class="popup-text">
</div>
<div class="popup-text-b">
</div>
<div class="holder">
<asp:Literal ID="memberDescription" runat="server" />
</div>
</div>
</asp:Panel>
</ItemTemplate>
</asp:Repeater>
</ul>
it looks like maybe this works similarly to a for loop, but I'm not quite positive how to convert it to MVC 3 architecture.
Porting an existing WebForms application to ASP.NET MVC is not only about blindly translating line by line some WebForms view code that you have. You should take into account the semantics of the target platform. For example converting this asp:Repeater into an ugly foreach loop instead of taking into account things like view models, display templates would not be very good.
So in ASP.NET MVC you start by designing view models:
public class MemberViewModel
{
public int Id { get; set; }
public string Description { get; set; }
}
then you design a controller action which populates this view model:
public ActionResult Index()
{
IEnumerable<MemberViewModel> model = ...
return View(model);
}
then you write a strongly typed view in which you invoke a display template:
#model IEnumerable<MemberViewModel>
#Html.DisplayForModel()
and then you define a display template which will be rendered for each element of the collection (~/Views/Shared/DisplayTemplates/MemberViewModel.cshtml):
#model MemberViewModel
<li id="TeamMember" class="memberImage">
<img src="Url.Action("ThumbnailImage", new { id = Model.Id })" alt=""/>
</li>
<div class="popup">
<div class="img-holder">
<img src="Url.Action("FullImage", new { id = Model.Id })" alt=""/>
</div>
<div class="popup-text-t">
<div class="close">
close
</div>
</div>
<div class="popup-text"></div>
<div class="popup-text-b"></div>
<div class="holder">
#Html.DisplayFor(x => x.Description)
</div>
</div>
Now you will notice the two additional ThumbnailImage and FullImage controller actions that will allows us to fetch the images of the members given the member id. For example:
public ActionResult ThumbnailImage(int id)
{
byte[] thumbnail = ...
return File(thumbnail, "image/jpeg");
}
Now that's more like ASP.NET MVC. As you can see it's a totally different pattern than classic WebForms.
You're quite right to suppose that the MVC equivalent of an asp:Repeater is
<% foreach( var item in Model )
{ %>
<!-- Your HTML Markup -->
<% } %>
You're right about it being similar to a for loop. A simple implementation might look like this:
<div class="popup-holder">
<ul class="popups">
<%foreach(var item in Model.Items) { %>
<div id="pnlTeamMember">
<img src="<%: item.MemberImageSrc %>" ID="memberImg" />
<div class="popup">
<div class="img-holder">
<img src="<%: item.MemberImgBigSrc %>" ID="memberImgBig" />
</div>
<div class="popup-text-t">
<div class="close">
close
</div>
</div>
<div class="popup-text">
</div>
<div class="popup-text-b">
</div>
<div class="holder">
<%: item.MemberDescription %>
</div>
</div>
</div>
<% } %>
</ul>
You'll notice that there are no longer any controls with runat="server", nor are there any events linked to handlers in the code-behind. Instead, we are assuming that the controller has populated the Model object with objects representing the data that we need to display. That is the role of the controller when using MVC.
A repeater is just a loop that provides databinding so that you can access the items in the collection that you are looping. If you look in the ourTeamRepeater_ItemDataBound method you will find the code that uses the databound items to populate the elements in the item template with data.
Usually you can just use a foreach loop in MVC to loop the items. Example:
<% foreach (var item in items) { %>
<div class="holder">
<%= item.Description %>
</div>
<% } %>
I'm not sure exactly what I'm doing wrong here.
I have an action which returns a partial view:
public class SharedController : BaseController
{
public ActionResult StudentPoll()
{
WAM.X2O.FuelUpToPlayContext db = new WAM.X2O.FuelUpToPlayContext(WAM.Utilities.Config.ConnectionString);
WAM.X2O.StudentPoll m = (from s in db.StudentPolls where s.IsActive == true select s).SingleOrDefault();
return PartialView("StudentPoll", m);
}
}
I implement the action like this:
<%Html.RenderAction("StudentPoll", "Shared");%>
The partial view looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl< Fuel_Up_To_Play.Models.Shared.StudentPollModel >" %>
<%if(ViewData.Model != null){ %>
<div class="block">
<div class="holder">
<div class="frame">
<h2 class="poll">Student Poll</h2>
<!-- TODO - Student Poll has a form screen, and results screen.
Results anim is here to demo what should happen after submision -->
<form action="/Shared/SubmitStudentPoll" method="post" class="poll-form" <%if(ViewData.Model.StartingState == Fuel_Up_To_Play.Models.Shared.StudentPollModel.PollStates.RESULTS){%>style="display:none"<%} %>>
<fieldset>
<span><%=ViewData.Model.StudentPoll.Question %></span>
<input type="hidden" id="student_poll" name="student_poll" value="<%=ViewData.Model.StudentPoll.ID %>" />
<%foreach(WAM.X2O.StudentPollAnswer answer in ViewData.Model.StudentPoll.RelatedStudentPollAnswers){ %>
<div class="row">
<input type="radio" class="radio" id="answer_<%=answer.ID %>" name="poll_answer" value="<%=answer.ID %>"/>
<label for="answer_<%=answer.ID %>"><%=answer.AnswerText%></label>
</div>
<%} %>
<input id="submitBtn" type="image" style="display:none" class="image" src="/Content/images/btn-submit-05.gif" />
</fieldset>
</form>
<div class="progress-box" <%if(ViewData.Model.StartingState == Fuel_Up_To_Play.Models.Shared.StudentPollModel.PollStates.FORM){%>style="display:none"<%} %>>
<span><%=ViewData.Model.StudentPoll.Question %></span>
<%int answerCounter = 0;
foreach(WAM.X2O.StudentPollAnswer answer in ViewData.Model.StudentPoll.RelatedStudentPollAnswers){ %>
<div class="box">
<span class="names"><%=answer.AnswerText%><strong class="quesPctTxt" rel="<%=ViewData.Model.AnswerPercentages[answerCounter] %>"></strong></span>
<div class="progress-bar"><span class="quesPctBar"></span></div>
</div>
<%
answerCounter++;
} %>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="/Scripts/studentpollscript.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("input.radio[name='poll_answer']").change(function() {
$("#submitBtn").show();
});
$("#submitBtn").click(function() {
$(".poll-form").ajaxForm(
{ dataType: 'json',
success: function(json) {
alert(json.Success);
}
}
);
});
});
</script>
<%} %>
Natually, I would expect this approach to return HTML. But no. Instead it appears that binary is being rendered in the browser. Obviously I'm doing something wrong but I don't know what.
Here's what is rendered in the browser:
http://screencast.com/t/Mjg1OWJj
Any ideas folks? I'm stumped but I'm sure it's something simple that I'm missing.
I would guess that it simply isn't setting the content type correctly. What does a network trace (fiddler, etc) show? Perhaps try using View("StudentPoll", m); instead of PartialView(...)?
Also; be careful - many of those <%= look unsafe, i.e. not html-encoded.