I'm my Project Browser page I have 2 sub-elements that warrant their own controller (and not simply a user-control) - Upload and FileBrowser, and so I've added them using Html.RenderAction(Action, Controller, param).
The problem however, is that the Browse page requires ProjectViewModel, where Upload uses UploadViewModel, etc. etc. So by having these Html.RenderAction elements, the Browse page seems to immediately stop receiving the ProjectViewModel - i'm guessing it switches to the VM of the last RenderAction.
Is there something I have to set up in routing to ensure these already strongly typed Views keep their contexts?
Update with code:
Also, maybe I have to explicitly state that the model going TO "Upload" is a different one? I dunno.
Browser (containing Upload and FileBrowser):
<%#
Page Title=""
Language="C#"
Inherits="System.Web.Mvc.ViewPage<CKD.Web.Files.ViewModels.ProjectViewModel>"
MasterPageFile="~/Views/Project/Project.Master"
%>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<table>
<tr>
<td id="upload" style="width: 180px" class="ui-widget ui-widget-content ui-corner-all">
<% Html.RenderAction("Index", "Upload", new {id = Model.Project.Id}); %>
</td>
<td id="fileBrowser" style="width: auto" class="ui-widget ui-widget-content ui-corner-all">
<% Html.RenderAction("Index", "FileBrowser", new {id = Model.Project.Id}); %>
</td>
</tr>
</table>
</asp:Content>
Upload View:
<%#
Page Title=""
Language="C#"
Inherits="System.Web.Mvc.ViewPage<CKD.Web.Files.ViewModels.UploadViewModel>"
MasterPageFile="~/Views/Shared/Control.master"
%>
<%# Import Namespace="System.IO" %>
<asp:Content runat="server" ID="Scripts" ContentPlaceHolderID="Scripts">
</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<div class="uploadControl" style="Margin: 8px">
<h2 style="Margin-Bottom: 0px">Upload</h2>
<hr />
<div id="accordion" style="display: block;">
<h3>Files</h3>
<div>
<div class="ui-widget-content ui-corner-all" style="min-height: 80px; margin: 4px">
<% if(Model.Files != null) %>
<% foreach(FileInfo f in Model.Files) {%>
<p><%= f.Name %></p>
<hr />
<% } %>
</div>
<ul style="width: 10px; list-style-type:none">
<li class="ui-widget ui-widget-button ui-corners-all">Clear</li>
<li class="ui-widget ui-widget-button ui-corners-all">Add</li>
</ul>
</div>
<h3>Transmittal</h3>
<div>
<p>File here</p>
<p style="width: auto; margin: 8px" class="ui-widget-button">Pick File...</p>
</div>
<h3>Notification</h3>
<div>
<p>
Stuff
</p>
</div>
</div>
<div>
<div class="ui-widget ui-corner-all ui-widget-active">Upload Files</div>
</div>
</div>
</asp:Content>
Upload Controller:
using System.Web.Mvc;
namespace CKD.Web.Files.Controllers
{
using System.Linq;
using Models;
using ViewModels;
public class UploadController : Controller
{
private ICKDClientAreaRepository Repository { get; set; }
private UploadViewModel _viewModel;
private UploadViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = ViewModel = UploadViewModel.Default(Repository)); }
set { _viewModel = value; }
}
public UploadController(ICKDClientAreaRepository repository)
{
Repository = repository;
}
// GET
public ActionResult Index(int id)
{
var project = Repository.Projects.Single(x => x.Id == id);
ViewModel = UploadViewModel.ForProject(project, Repository);
return View(ViewModel);
}
}
}
Upload VM:
namespace CKD.Web.Files.ViewModels
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Security;
using Models;
public class UploadViewModel
{
public Project Project { get; set; }
public DirectoryInfo Directory { get; set; }
public User Uploader { get; set; }
public DateTime Time { get; set; }
public List<FileInfo> Files { get; set; }
public FileInfo Transmittal { get; set; }
public List<User> NotificationList { get; set; }
public static UploadViewModel Default(ICKDClientAreaRepository fromRepository)
{
var project = fromRepository.Projects.First();
return ForProject(project, fromRepository);
}
public static UploadViewModel ForProject(Project project, ICKDClientAreaRepository fromRepository)
{
var dir = project.DirectoryName;
var uploader = fromRepository.Users.Single(x => x.Username == Membership.GetUser().UserName);
var time = DateTime.Now;
var notification = project.Users.ToList();
return new UploadViewModel
{
Project = project,
Directory = new DirectoryInfo(dir),
Uploader = uploader,
Time = time,
NotificationList = notification
};
}
}
}
Try making the view rendered by RenderAction() a partial view.
You should also decorate the action with [ChildActionOnly] attribute to prevent it from executing by it's own (when somebody would request http://xxx.com/Upload/Index).
Related
I have a form in which the user is supposed to enter Name and Wage and click a button Add. When the button "Add" is clicked, that user is supposed to be displayed on a list.
This is how I tried it.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TimeIsMoney.Models;
namespace TimeIsMoney.Controllers
{
public class HomeController : Controller
{
List<UserModel> users = new List<UserModel>();
public ActionResult Index(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
public ActionResult AddUser(UserModel user)
{
users.Add(user);
return View(users);
}
}
}
View:
#model TimeIsMoney.Models.LoginModel
#{
}
#functions{
public string GetAntiForgeryToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
<div id="main-content" class="col-md-8 col-md-offset-2">
<div class="col-md-12 row">
<h1>Time is money my friend!</h1>
</div>
<div class="col-md-12 row">
<h2>1000kr</h2>
</div>
<div class="col-md-12 row">
<button class="btn" onclick="start()">Start</button>
<button class="btn" onclick="reset()">Reset</button>
</div>
<div class="col-md-12 row">
<form >
<input type="text" placeholder="Name" />
<input type="number" placeholder="Hourly wage" />
<input type="submit" value="Add" onclick="AddUser()" />
</form>
</div>
<div class="col-md-12 row">
<div class="col-md-3 col-md-offset-1">
<label>Name:</label>
<ul>
<li>Dave</li>
<li>Pete</li>
</ul>
</div>
<div class="col-md-4">
<label>Wage:</label>
<ul>
<li>500kr/h</li>
<li>500kr/h</li>
</ul>
</div>
</div>
<br />
<br />
</div>
Model:
namespace TimeIsMoney.Models
{
public class UserModel
{
[Required]
[DataType(DataType.Text)]
[DisplayName("Username")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Text)]
[DisplayName("Wage")]
public string Wage { get; set; }
}
}
Am I on the right path?
How can I move on from here?
UPDATE:
public ActionResult AddUser(UserModel user)
{
var list = Session["myUsers"] as List<UserModel>;
list.Add(user);
return View(list);
}
You're mostly on the right path excluding way you're trying to store your users list.
Since ASP.NET MVC controller instance is created for every request and disposed after view is rendered and passed to the browser - it will be new controller holding new List<UserModel> created on every request.
So you have to store it somewhere else (session variables, file on server's disk, database and so on). Usually database is best choice for this.
In the case you want to store it in session variable, you should add something like this into Global.asax:
protected void Session_Start(object sender, EventArgs e)
{
Session["myUsers"] = new List<UserModel>();
}
and then in your controller's methods you will be able to access this list as
var list = Session["myUsers"] as List<UserModel>;
I have two model classes,
public class Claims //Goes into main view
{
public int Id { get; set; }
public string ClaimName { get; set; }
public List<ClaimDetails> ClaimList { get; set; }
}
public class ClaimDetails //Class I want in my partial view
{
public int ClaimNumber { get; set; }
public string Client { get; set; }
public int Amount { get; set; }
public string Type { get; set; }
}
My controller,
public class ClaimsController : Controller
{
public ActionResult Index()
{
Claims claims = new Claims();
claims.Id = 1;
claims.ClaimName = "Ashton";
return View(claims);
}
[HttpPost]
public ActionResult SearchList(string enterdNumber)//On click of button I come here using ajax call
{
ClaimDetails cD = new ClaimDetails();
Claims cms = new Claims();
cms.ClaimList = new List<ClaimDetails>();
cD.ClaimNumber = 10;
cD.Client = "Ashton";
cD.Amount = 2900;
cD.Type = "Vendor";
cms.ClaimList.Add(cD);
ClaimDetails cDD = new ClaimDetails();
cDD.ClaimNumber = 10;
cDD.Client = "Ashton";
cDD.Amount = 2900;
cDD.Type = "Vendor";
cms.ClaimList.Add(cDD);
return PartialView("SearchList",cms);
}
My main view in which I want my partial view to be rendered,
#using BusinessLayer
#model BusinessLayer.Claims
#{
ViewBag.Title = "Index";
}
<script src="~/Scripts/jquery-1.10.2.min.js" type="text/javascript"></script>
<script src="~/Scripts/bootstrap.min.js" type="text/javascript"></script>
<div class="row">
<div class="col-md-6">
#Html.LabelFor(m => m.Id):#Model.Id
</div>
<div class="col-md-6">
#Html.LabelFor(m => m.ClaimName):#Model.ClaimName
</div>
</div>
<div class="row">
<div class="col-md-6">
<input id="searchNumber" placeholder="Enter the number" type="text" />
</div>
<div class="row">
<button id="searchBtn" type="button">Search</button>
</div>
</div>
<div class="row">
<div class="col-md-12">
#Html.Partial("SearchList",Model.ClaimList)
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#searchBtn").on("click", function () {
var enteredNum = $("#searchNumber").val();
$.ajax({
type: "POST",
url: "/Claims/SearchList",
data: { enterdNumber: enteredNum }
});
});
});
</script>
My partial view,
#model BusinessLayer.Claims
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>Claim Number</th>
<th>Client</th>
<th>Amount</th>
<th>Type</th>
</tr>
<tbody>
#if (Model.ClaimList != null)
{
foreach(var item in Model.ClaimList)
{
<tr>
<td>#item.ClaimNumber</td>
<td>#item.Client</td>
<td>#item.Amount</td>
<td>#item.Type</td>
</tr>
}
}
</tbody>
</table>
My control comes to my partial view page, which I confirmed using breakpoint, it also iterates through the foreach loop but still does not put rows into my table..Help to know where I am going wrong is appreciated, or may be my approach to partial view is itself wrong?
You're returning a partial view but you're not doing anything with it. You need to include the success callback in the ajax function and add the partial view to the DOM
$.ajax({
type: "POST",
url: '#Url.Action("SearchList", "Claims")', // use this
data: { enterdNumber: enteredNum },
dataType: 'html', // add this
success: function(response) {
$('#someElement').html(response); // add this (adjust id to suit)
}
});
and assuming you want to update the existing partial, add an id attribute to the existing container
<div class="row">
<div class="col-md-12" id="someElement"> // add id attribute
#Html.Partial("SearchList",Model.ClaimList)
</div>
</div>
Side notes:
You may want to consider including the <table> and <thead>
element in the main view and have the partial only return the
<tbody> elements to minimize the data transfered in each ajax
call.
Your method appears to be just getting data based on a filter, so
the method could be a GET rather than a POST
I looked at all of the postings with this error but none helps. I am using MVC with the views in ASPX (C#).
I am updating records in the database after editing the data in simple views. When I process one record at a time (Controller EDIT methods and EDIT view) all works fine.
When I process multiple records (Controller UpdateTest and UPDATETEST view) I get an error in my HTTPPOST method in Controller after clicking the Submit button in a view.
here is an error with the sample codes one which works and one which does not:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error: (CONTROLLER – SEE BELOW HttpPost UpdateTest(ICollection<Mysurvey> mysurveys)
enter code here
Line 48: public ActionResult UpdateTest(ICollection<Mysurvey> mysurveys)
Line 49: {
Line 50: foreach (var survey in mysurveys)
Line 51: {
Line 52: db.Entry(survey).State = EntityState.Modified;
Source File: C:\Users\rsc_vok\Documents\Visual Studio 2010 \Projects\MvcMysurvey\MvcMysurvey\Controllers\MysurveyController.cs Line: 50
CONTROLLER
namespace MvcMysurvey.Controllers
{
THE CODE WHICH WORKS – SINGLE EDIT
public ActionResult Edit(int id)
{
Mysurvey mysurvey = db.Mysurveys.Find(id);
return View(mysurvey);
}
// POST: /Mysurvey/Edit/5
[HttpPost]
public ActionResult Edit(Mysurvey mysurvey)
{
System.Diagnostics.Debug.WriteLine("iam in edit post");
if (ModelState.IsValid)
{
db.Entry(mysurvey).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mysurvey);
}
THE CODE WITH THE ERROR IN httppost – MULTIPLE RECORDS SAVE
public ActionResult UpdateTest(int id = 0)
{
List<Mysurvey> mysurveys = db.Mysurveys.ToList();
return View(mysurveys.ToList());
}
[HttpPost]
public ActionResult UpdateTest(ICollection<Mysurvey> mysurveys)
{
foreach (var survey in mysurveys)
{
db.Entry(survey).State = EntityState.Modified;
}
db.SaveChanges();
return View(mysurveys);
}
VIEW
UPDATETEST
THE CODE WHICH WORKS HERE BUT THROWS AN ERROR IN CONTROLLER AFTER THE SAVE BUTTON IS HIT
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MvcMysurvey.Models.Mysurvey>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Updatetest
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>
<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Mysurvey</legend>
<% foreach (var item in Model) { %>
<%: Html.HiddenFor(model => item.ID) %>
<%: Html.HiddenFor(model => item.SurveyID) %>
<div class="editor-label">
<%: Html.LabelFor(model => item.Comment) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => item.Comment) %>
<%: Html.ValidationMessageFor(model => item.Comment) %>
</div>
<%} %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
MODEL
namespace MvcMysurvey.Models
{
public class Mysurvey
{
public int ID { get; set; }
[DisplayFormat(DataFormatString = "{0:c}")]
public int SurveyID { get; set; }
public string Comment { get; set; }
}
public class MysurveyDBContext : DbContext
{
public DbSet<Mysurvey> Mysurveys { get; set; }
}
}
I wrote something like this in the controller.
public ActionResult Giris()
{
ViewData["Tarif"] = (from t in _entities.Tarif
join k in _entities.Kullanici on t.KID equals k.KID
select new {KAdi = k.KAdi, TAdi = t.TAdi})
.Take(4);
return View();
}
I am using it as below in the view page.
<% foreach (var item in (IEnumerable<dynamic>)ViewData["Tarif"]) { %>
<div class="begenilen-video" style="float:left">
<img class="video-resmi" alt="reklam" src="../../Uygulama/Resimler/Reklam/1.jpg" />
<span class="benzer-yemek-tarifi-adi"></span><%=item.TAdi %><br />
<span class="benzer-yemek-tarifi-ekleyen">Ekleyen: </span><br />
<span class="benzer-yemek-tarifi-izlenme">İzlenme: </span>
</div>
<% } %>
However,I am receive the error in the select statement.How do I invoke the items in the view page?
Thanks in advance.
As a guess because you haven't posted the error:
The object being stored in ViewData["Tarif"] will be of the type IQueryable<T> where T is an anonymous object and in your view you are casting to IEnumerable<dynamic>. IQueryable is also lazily loaded so you will be trying to execute your query once the object has been disposed.
You should really create a strongly typed view model
public class ViewModelType {
public IEnumerable<TarifType> Tarif { get; set; }
}
Tarif type
public class TarifType {
public string KAdi { get; set; }
public string TAdi { get; set; }
}
controller
public ActionResult Giris() {
var viewModel = new ViewModelType();
viewModel.Tarif = (from t in _entities.Tarif
join k in _entities.Kullanici on t.KID equals k.KID
select new TraifType { KAdi = k.KAdi, TAdi = t.TAdi }
).Take(4)
.ToList();
return View(viewModel);
}
view
<% foreach (var item in viewModel.Tarif) { %>
<div class="begenilen-video" style="float:left">
<img class="video-resmi" alt="reklam" src="../../Uygulama/Resimler/Reklam/1.jpg" />
<span class="benzer-yemek-tarifi-adi"></span><%=item.TAdi %><br />
<span class="benzer-yemek-tarifi-ekleyen">Ekleyen: </span><br />
<span class="benzer-yemek-tarifi-izlenme">İzlenme: </span>
</div>
<% } %>
Ok, here is my problem. I am trying to pass a view model of mine, which has multiple list in it, to my view. Then in my view i need to edit the different list. Then on my post i need to save the edits. Although, when i pass my viewmodel back to my post, it is empty! Can somebody explain what i am doing wrong? i am not worried about saving the information right now, i am just worried about getting the data back to the controller. The ViewModel is null when i hit that portion.
Controller
public ActionResult ManageNewsArticles()
{
NewsViewModel newsViewModel = new NewsViewModel();
newsViewModel.ListBreakingNews = db.NewsArticles.Where(n => n.PageSetupID == 1).ToList<NewsArticle>();
newsViewModel.ListMainArticle = db.NewsArticles.Where(n => n.PageSetupID == 2).ToList<NewsArticle>();
newsViewModel.ListSubNews1 = db.NewsArticles.Where(n => n.PageSetupID == 3).ToList<NewsArticle>();
newsViewModel.ListSubNews2 = db.NewsArticles.Where(n => n.PageSetupID == 4).ToList<NewsArticle>();
newsViewModel.ListSubNews3 = db.NewsArticles.Where(n => n.PageSetupID == 5).ToList<NewsArticle>();
return View(newsViewModel);
}
[HttpPost]
public ActionResult ManageNewsArticles(NewsViewModel newsViewModel)
{
if (ModelState.IsValid)
{
db.SaveChanges();
return RedirectToAction("Admin");
}
return View(newsViewModel);
}
here is my View
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TrueNews.ViewModels.NewsViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Manage News Articles
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Manage News Articles</h2>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm(Model)) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<%: Html.EditorForModel(Model) %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to Admin Controls", "Admin") %>
</div>
</asp:Content>
NewsViewModel
public class NewsViewModel
{
public List<NewsArticle> ListBreakingNews { get; set; }
public List<NewsArticle> ListMainArticle { get; set; }
public List<NewsArticle> ListSubNews1 { get; set; }
public List<NewsArticle> ListSubNews2 { get; set; }
public List<NewsArticle> ListSubNews3 { get; set; }
} // End of Class
I couldn't get your view to display correctly using the EditorForModel syntax, however I did replicate your problem and found this article which provides one way of solving it:
http://weblogs.asp.net/nmarun/archive/2010/03/13/asp-net-mvc-2-model-binding-for-a-collection.aspx
I created a quick table NewsArticle containing Id and Stuff columns and then had the following form in the view:
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<% for (int i = 0; i < Model.ListBreakingNews.Count; i++)
{ %>
<div>
Id</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Id", i), Model.ListBreakingNews[i].Id) %>
</div>
<div>
Name</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Stuff", i), Model.ListBreakingNews[i].Stuff) %>
</div>
<% } %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
and the items in the list were passed when posted. I also found that previously you had Html.BeginForm(Model) which when I added it caused nothing to be passed through in the view model - I am only a newbie to MVC so I don't know why this happens but my code below worked only with Html.BeginForm() and not Html.BeginForm(Model).
I hope this helps.