How to refresh a page in MVC - c#

I found out that my page actually reload/refresh, but require additional reload to see the content I just added. However I've to reload again to see the data, OR add another data to see the previous data..
I added my controller code below:
(CreateComment and Comment(display comments) are inside Details(details about book) View )
CreateComment:
public ActionResult CreateComment(Guid id) {
return View(new CommentToBook { BookId = id });
}
[HttpPost]
public ActionResult CreateComment(CommentToBookVm model) {
if (ModelState.IsValid) {
var m = new CommentToBook { Comment = model.Comment, BookId = model.BookId };
m.UserId = new Guid(Session["UserID"].ToString());
m.CreatedDate = DateTime.Now;
db.CommentToBooks.Add(m);
db.SaveChanges();
}
return View(model);
}
View for createComment
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(s => s.BookId)
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Comment, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Comment, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Comment, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
Details (have Comment and CreateComment inside)
public ActionResult Details(Guid? id) {
Book book = db.Books.Find(id);
return View(book);
}
View
<h2>Details</h2>
#Html.Action("Rating", new { id = Model.Id })
<div>
<h4>Books</h4>
<hr/>
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.Title)
</dt>
<dd>
#Html.DisplayFor(model => model.Title)
</dd>
#*and so on...*#
</dl>
</div>
#Html.Action("Comment", new { id = Model.Id })
#Html.Action("CreateComment", new { id = Model.Id })
And the Comment to list all comments.
public ActionResult Comment(Guid? id) {
var comment = db.CommentToBooks.Where(c => c.BookId == id);
return View(comment.ToList());
}
view:
<table class="table">
<tr>
<th>
User
</th>
<th>
#Html.DisplayNameFor(model => model.Comment)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.ActionLink(item.User.UserName, "VisitUser", new { id = item.UserId })
</td>
<td>
#Html.DisplayFor(modelItem => item.Comment)
</td>
</tr>
}
</table>
I think it's about what I am returning in controller, I tried some different alternative but I just end up hitting the error:
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:
Line 9: #Html.Action("Comment", new { id = Model.Id })
OR
child actions are not allowed to perform redirect actions.
If I try to RedirectToAction for CreateComment etc.
I would appreciate an example of code as I find it hard to understand new concepts by just words.

Your code is returning the view for CreateComment method. Looks like you marked this action method as ChildActions only. You should not be using ChildAction for a use case like this. ChildActions should be used for rendering something to the view. Ex : A Menu bar in your app.
Even if you remove the [ChildAction] from the CreateComment action method, when you return the model back to the form, it is going to render the markup generated by the CreateComment view. That means you will loose the list of comments (which was loaded when you called the Details view).
Ideally, for all data insertion usecases, you should follow P-R-G pattern.
PRG stands for POST - REDIRECT- GET. That means, you submit a form and after successfully saving data to db, you do return a redirect result to the client and client(browser) will issue a totally new http request for the GET action method in which you wiil query the db table and return the results.
But since your form is loaded to the main view through a call to the Html.Action method, you won't get the desired results (list of comments and the validation messages in same view). One thing to make it to work is, by enabling unobtrusive client side validation. In that case, the form won't be actually submitted to the server. Instead client side validation will be invoked and validation messages will be shown to the user in the same page(no page reload!).
You can enable it by adding reference to these 2 scripts in your view(or layout)
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
Redirect to the GET action to show all comments after successful save of the posted comment.
[HttpPost]
public ActionResult CreateComment(CommentToBookVm model)
{
if (ModelState.IsValid)
{
//your existing code to save data to the table
return RedirectToAction("Details","Book", new { id=model.BookId} );
}
// Hoping that the below code might not execute as client side validation
// must have prevented the form submission if there was a validation error.
return View(model);
}
Another option which does not rely on just client side validation is to create a flat view model which has the list of existing comments and properties for the new comment form. When you submit the form, if validation fails, reload the Comments property again and return the view model back to the form.
public class ListAndCreateVm
{
[Required]
public string NewComment { set;get;}
public Guid BookId { set;get;}
public List<CommentVm> Comments { set;get;}
}
public class CommentVm
{
public string Comment { set;get;}
public string Author { set;get;}
}
and in your Details action, load Comments and send this to the view
public ActionResult Details(Guid id)
{
var vm = new ListAndCreateVm { BookId= id};
vm.Comments = GetComments(id);
return View(vm);
}
private List<CommentVm> GetComments(Guid bookId)
{
return db.CommentToBooks.Where(c => c.BookId == bookId)
.Select(x=> new CommentVm { Comment = x.Comment})
.ToList();
}
and in your view
#model ListAndCreateVm
#foreach(var c in Model.Comments)
{
<p>#c.Comment</p>
}
<h4>Create new comment</h4>
#using(Html.BeginForm())
{
#Html.ValidationSummary(false, "", new {#class = "text-danger"})
#Html.TextBoxFor(s=>s.NewComment)
#Html.HiddenFor(f=>f.BookId)
<input type="submit" />
}
Now along with using PRG pattern, make sure to reload Comments property of the view model when Model validation fails
[HttpPost]
public ActionResult Details(ListAndCreateVm model)
{
if(ModelState.IsValid)
{
// to do : Save
return RedirectToAction("Details,"Book",new { id=model.BookId});
}
//lets reload comments because Http is stateless :)
model.Comments = GetComments(model.BookId);
return View(model);
}

Related

Model passed to Partial to View

Been searching around but couldn't find a direct solution to what I'm trying to achieve.
I've tried to include as much as needed but it's a very large project so hopefully you'll get the gist.
Overview:
I have a view model that has several lists of objects within it. I am using two partial views for control over each of the list of objects, one for gathering the list of objects (which is held in a session), and the other for adding a list of said object into the list.
Update:
As per comment - what I am looking to do is as follows - in the Index, fill out the existingIp model info, which is displayed through the addToListPartialView, then it will post to the ListPartialView to update the list through the session, handled backend in the controller, which will in turn display on the Index - the question ultimately is, how do I achieve this?
Problem:
The issue I'm having is once I've added an object, through a partial view, to the object list, another partial view, how do I then pass this back to the main view?
Code:
Controller
public ActionResult AddExistingIp([Bind(Include = "Subnet, Cidr, Mask")]ExistingIp existingIp)
{
if(Session["pa_ipv4Session"] != null)
{
pa_ipv4 pa_ipv4 = (pa_ipv4)Session["pa_ipv4Session"];
if(pa_ipv4.ExistingIps == null)
{
pa_ipv4.ExistingIps = new List<ExistingIp>();
}
pa_ipv4.ExistingIps.Add(existingIp);
ViewBag.pa_ipv4 = pa_ipv4.ExistingIps;
return View("ExistingIpView", ViewBag.pa_ipv4);
}
else
{
pa_ipv4 pa_ipv4 = new pa_ipv4();
Session["pa_ipv4Session"] = pa_ipv4;
pa_ipv4.ExistingIps = new List<ExistingIp>();
pa_ipv4.ExistingIps.Add(existingIp);
ViewBag.pa_ipv4 = pa_ipv4.ExistingIps;
return View("ExistingIpView", ViewBag.pa_ipv4);
}
Index:
#model ViewModel
<div id="ExistingIpList">
#{Html.RenderPartial("ExistingIpView");}
</div>
<div id="addExisting">
#{Html.RenderPartial("AddExistingIp");}
</div>
List Partial
#model IEnumerable<ExistingIp>
#if (Model != null)
{
foreach (var ei in Model)
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>#ei.Subnet</span>
</div>
<div class="ui-block-b">
<span>#ei.Cidr</span>
</div>
<div class="ui-block-c">
<span>#ei.Mask</span>
</div>
<div class="ui-block-d">
#ei.Id
Delete
</div>
</div>
}
}
Add to list partial:
#using (Html.BeginForm("AddExistingIp", "PA_IPV4"))
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>
#Html.EditorFor(m => m.Subnet)
#Html.ValidationMessageFor(m => m.Subnet)
</span>
</div>
<div class="ui-block-b">
<span>
#Html.EditorFor(m => m.Cidr)
#Html.ValidationMessageFor(m => m.Cidr)
</span>
</div>
<div class="ui-block-c">
<span>
#Html.EditorFor(m => m.Mask)
#Html.ValidationMessageFor(m => m.Mask)
</span>
</div>
<div class="ui-block-d">
<span>
#Html.EditorFor(m => m.Id)
#Html.ValidationMessageFor(m => m.Id)
</span>
</div>
</div>
<div data-role="main" class="ui-content">
<div data-role="controlgroup" data-type="horizontal">
<input type="submit" id="addExistingIp" cssclass="ui-btn ui-corner-all ui-shadow" value="Add" />
</div>
</div>
}
ViewModel:
public Contact ContactDetails { get; set; }
[Required]
public bool ExistingAddress { get; set; }
public List<ExistingIp> ExistingIps { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string ExistingNotes { get; set; }
You can modify the AddExistingIp to just store the data. And to make a RedirectToAction Index. There you will take the data from Session and pass it to the Model.
[HttpPost]
public ActionResult AddExistingIp([Bind(Include = "Subnet, Cidr, Mask")]ExistingIp existingIp)
{
if(Session["pa_ipv4Session"] != null)
{
pa_ipv4 pa_ipv4 = (pa_ipv4)Session["pa_ipv4Session"];
if(pa_ipv4.ExistingIps == null)
{
pa_ipv4.ExistingIps = new List<ExistingIp>();
}
pa_ipv4.ExistingIps.Add(existingIp);
}
else
{
pa_ipv4 pa_ipv4 = new pa_ipv4();
Session["pa_ipv4Session"] = pa_ipv4;
pa_ipv4.ExistingIps = new List<ExistingIp>();
pa_ipv4.ExistingIps.Add(existingIp);
}
return RedirectToAction("Index");
}
The Index Action will look similar with this, where you take data from Session and use it in your Model
public ActionResult Index()
{
var viewModel = new ViewModel();
// take data from Session
pa_ipv4 pa_ipv4 = Session["pa_ipv4Session"] as (pa_ipv4);
// some verification
// add the list from Session to model
viewModel.ExistingIps = pa_ipv4.ExistingIps;
return View(viewModel);
}
Also, I think your Index View you should at ExistingIpView you should pass the Model to display.
#model ViewModel
<div id="ExistingIpList">
#{Html.RenderPartial("ExistingIpView", Model.ExistingIps);}
</div>
<div id="addExisting">
#{Html.RenderPartial("AddExistingIp");}
</div>

IEnumerable<Model> returning null

I am having difficulty passing an IEnumerable as a model. The data is populating a form on one page - and doing so correctly. Upon submission the model returns as null.
I've seen various posts on this and they mostly reference naming-conventions so I have attempted different methods of naming the parameters to try to avoid any confusion in the model binding.
I have also tried various models and helpers to try and pass the data and all have the same result.
Current implementation:
Models:
public class UserProfileListModel
{
public IEnumerable<UserProfileViewModel> UserProfileViewModels { get; set; }
}
public class UserProfileViewModel
{
public UserProfile UserProfile { get; set; }
public Role UserRole { get; set; }
public Team UserTeam { get; set; }
public Scope UserScope { get; set; }
}
View:
#model Project.WebUI.Models.UserPRofileListModel
SNIP
<fieldset>
<legend>Administrate Users:</legend>
<table class="adminTbl">
<thead>
<tr>
<th>UserName:</th>
<th>Role:</th>
<th>Team:</th>
<th>Scope:</th>
<th>Update:</th>
<th>Delete:</th>
</tr>
</thead>
<tbody>
#{foreach (var user in Model.UserProfileViewModels)
{
<tr>
<td>
<p>#user.UserProfile.UserName
#{if (!user.UserProfile.Membership.IsConfirmed)
{
using (Html.BeginForm("Confirm", "Account", FormMethod.Post, null)){
#Html.AntiForgeryToken()
#Html.Hidden("Token", user.UserProfile.Membership.ConfirmationToken)
#Html.Hidden("Name", user.UserProfile.UserName)
}
<input type="submit" value="Confirm" />}
}
</p>
</td>
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(u => user.UserProfile)
if (user.UserProfile.UserName != User.Identity.Name && user.UserProfile.Membership.IsConfirmed)
{
<td>
#Html.DropDownListFor(u => user.UserRole, Project.WebUI.Controllers.AccountController.RoleList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserTeam, Project.WebUI.Controllers.AccountController.TeamList, new { #class = "formdrop" })
</td>
<td>
#Html.DropDownListFor(u => user.UserScope, Project.WebUI.Controllers.AccountController.ScopeList, new { #class = "formdrop" })
</td>
<td>
<input type="submit" value="Save Changes" onclick="return confirm('Are you sure you wish to update this user? ')" />
</td>
}
else
{
/*If user is self or not yet confirmed these are here to buffer the delete button into the last cell*/
<td></td>
<td></td>
<td></td>
<td></td>
}
}
}
<td>
#Html.ActionLink("Delete", "Delete", new { user.UserProfile.UserId }, new
{
onclick = "return confirm('Warning: Action cannot be undone. Are you sure you wish to permanently delete this entry?')"
})
</td>
</tr>
}
}
</tbody>
</table>
</fieldset>
Controller:
Populate View:
public ActionResult AdministrateUsers()
{
populateLists();
var query = repository.UserProfiles.OrderBy(e => e.UserName);
List<UserProfileViewModel> list = new List<UserProfileViewModel>();
foreach(UserProfile up in query)
{
UserProfileViewModel vm = new UserProfileViewModel() { UserProfile = up };
list.Add(vm);
}
UserProfileListModel models = new UserProfileListModel()
{
UserProfileViewModels = list.OrderBy(up => up.UserProfile.UserName)
};
return View(models);
}
Accept Post:
public ActionResult SaveUserChanges(UserProfileListModel model)
{
foreach (UserProfileViewModel upvm in model.UserProfileViewModels)
{
UserProfile up = new UserProfile()
{
UserId = upvm.UserProfile.UserId,
UserEmail = upvm.UserProfile.UserName,
UserName = upvm.UserProfile.UserName
};
if (ModelState.IsValid)
{
repository.SaveUserProfile(up);
}
else
{
return View(model);
}
}
return RedirectToAction("Index", "Admin");
}
The code does still need a lot of work but I can't get past getting the model back to the controller on post. I have also tried returning the UserProfileViewModel instead of the entire list.
Can anyone tell what I am doing wrong?
Thanks!
You have a lot of invalid html including form elements as child elements of tr elements and duplicate id attributes. If you want to post back UserProfileListModel then you need a single form element and use an EditorTemplate or a for loop (not foreach) to render the controls so they are correctly named with indexers.
You are also trying to bind your dropdown lists to complex objects (for example UserProfile, Role etc.). <select> elements (and all form controls) only post back key/value pairs so you need to bind to a value type (for example UserProfile.UserId).
Your SaveUserChanges() post method is also trying access properties of UserProfile but you don't even have controls for properties of UserProfile in the form that post back to this method (for example UserId = upvm.UserProfile.UserId, UserEmail = upvm.UserProfile.UserName, ...) so they will always be null.
You probalby need to bind properties in POST method like here:
public ActionResult Create([Bind(Include = "Id,Subject,Text,IsImportant")] Announcment announcment) {... }
So it will be:
public ActionResult SaveUserChanges([Bind(Include = "UserProfile,Role,UserTeam,UserScope")]UserProfileListModel model)
Have you specified your action method is for HTTP Post? And change your action method to accept UserProfileViewModels instead.
[HttpPost]
public ActionResult SaveUserChanges(UserProfileViewModels model)
{
You are also only posting back one model: UserProfileViewModels.
You have your form in your foreach loop, so each UserProfileViewModels has its own form. If you want to change it to post back your UserProfileListModel, move
#{using (Html.BeginForm("SaveUserChanges", "Account", FormMethod.Post, null))
outside of your foreach.

Passing multiple Collections of data from View to Controller (ASP.NET MVC 5)

In a View i have the next structure ( control of Subject*s for each *Group):
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#for (int i = 0; i < ViewBag.AllGroups.Count; i++)
{
<h4>#ViewBag.AllGroups[i].Code</h4>
<select id="e-#i" multiple="multiple">
#foreach (Subject subject in ViewBag.AllSubjects)
{
<option value="#subject.Name">#subject.Name</option>
}
</select>
}
<input type="submit" value="Generate" class="btn btn-default" />
</div>
}
The question is that how can I retreive this data (I want to receive (1)list of Groups and and I want to get a (2)list of all selected Subjects for each group in my list(1)) in my Controller?
Thank you in advance.
Recommended way is to use strongly typed View Model Object
public class GroupViewModel
{
public string Code { get;set; }
public List<Subject> AllSubjects { get; set; }
}
Pass List as the Model to the Razor view in the controller.
return new View(new List<GroupViewModel>()); // populated one.
Use this list in the View.
#model IList<GroupViewModel>
#for (int i = 0; i < Model.Count; i++)
{
<h4>Model[i].Code</h4>
<select id="e-#i" multiple="multiple">
#foreach (Subject subject in Model[i].AllSubjects)
{
<option value="#subject.Name">#subject.Name</option>
}
</select>
}
There is nothing especial to deal with this situation, except that you have missed the tag name of the select element.
To be exact, all html elements such as select you have used here, should have a name not id (id="e-#i") and all elements are serialized based their names and sent to server. On the other side, at server-side, you should get the posted values which are in a csv formatted (due to multiple ability added you have added to select element)
Solved my problem by simplifying the task. What i had to to: I created new ViewModel for this thing. I replaced tag <select></select> with #Html.ListBoxFor(m => m.Subjects, Model.SubjectItems). I had to create a SubjectItems list in my ViewModel.
Here's the code (Sorry for tons of code: I just want to make everything clear):
My View:
#using System
#using System.Linq
#using TimeTable.GenericRepository
#model TimeTable.Models.GroupViewModel
#{
//it's better to move the next line to a Controller later
ViewBag.GroupId = new SelectList(new GroupRepository().Get().ToList(), "Id", "Code", Model.GroupId);
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_LayoutBootstrap.cshtml";
}
<h2>Index</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.GroupId, "Group is: ", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("GroupId", String.Empty)
#Html.ValidationMessageFor(model => model.GroupId)
</div>
</div>
#Html.ListBoxFor(m => m.Subjects, Model.SubjectItems, new { #id = "e", #style = "width:80%; " })
<br /><br />
<input type="submit" value="Generate" class="btn btn-default" />
</div>
}
#* ReSharper disable once Razor.SectionNotResolved *#
#section Scripts {
#Styles.Render("~/Content/select2")
#Scripts.Render("~/bundles/select2")
<script type="text/javascript">
$(function () { $("#e").select2(); });
</script>
}
My Controller:
public ActionResult Generate()
{
return View(new GroupViewModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Generate(GroupViewModel gvm)
{
var subjects= gvm.Subjects; // <== selected subjects are here
return View();
}
My Model:
public class GroupViewModel
{
public int GroupId{ get; set; }
public Group Group {
get { return new GroupRepository().GetById(GroupId); }
}
public object Subjects { get; set; }
public IEnumerable<SelectListItem> SubjectItems
{
get
{
var items = new SelectList(new SubjectRepository().Get().ToList(), "Id", "Name");
return items;
}
}
}
P.S. Select2 is a custom replacement for selectBoxes, it's not necessary, but I like it: http://ivaynberg.github.io/select2/

Partial View to Add Data

So here, I need to add comment for video in one view.
I have the main view code like this that displaying video and comment for that video.
<!-- language: C# -->
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Watch";
}
<div id="videoPlayer">
</div>
<h2>#Html.DisplayFor(model => model.media.Title)</h2>
<h3> By #Html.DisplayFor(model => model.media.UserId) at #Html.DisplayFor(model => model.media.UploadDate) </h3>
#Html.HiddenFor(model => model.media.Id)
#Html.HiddenFor(model => model.media.AssetId)
#Html.HiddenFor(model => model.media.FileUrl, new { id = "fileUrl" })
<div class="display-label" style="font-weight:bold">
#Html.DisplayNameFor(model => model.media.Description)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Description)
</div>
<br />
<div class="display-label" style="font-weight:bold">
#Html.DisplayName("Category")
</div>
<div class="display-field">
#Html.DisplayFor(model => model.media.Category.CategoryName)
</div>
<h3>Comments</h3>
#foreach (var item in Model.comment)
{
<div class="display-label" style="font-weight:bold">
#item.UserId
</div>
<div class="display-field">
#item.Content
</div>
}
#Html.Partial("Post",new AzureMediaPortal.ViewModels.ViewModelWatch())
#section Scripts {
<script src="~/Scripts/playerframework.min.js"></script>
<script src="~/Scripts/media-player.js"></script>
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
mediaPlayer.initFunction("videoPlayer", $("#fileUrl").val());
</script>
}
and this the partial view
#model AzureMediaPortal.ViewModels.ViewModelWatch
#{
ViewBag.Title = "Post";
}
<h2>Add Comment</h2>
#Html.HiddenFor(model => model.cmnt.MediaElement.Id)
#using (Html.BeginForm("Post","Home",FormMethod.Post)) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Add Comment</legend>
<div class="editor-label" style="font-weight:bold">
#Context.User.Identity.Name
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.cmnt.Content)
#Html.ValidationMessageFor(model => model.cmnt.Content)
</div>
<p>
<input type="submit" value="Post" />
</p>
</fieldset>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
this is my ViewModel
public class ViewModelWatch
{
public MediaElement media { get; set; }
public List<Comment> comment { get; set; }
public Comment cmnt { get; set; }
}
and this is my controller
public ActionResult Watch(int id)
{
ViewModelWatch vm = new ViewModelWatch();
vm.media = _repository.GetMedia(id);
vm.comment = _repository.GetMediaComment(id);
return View(vm);
}
public ActionResult Post()
{
return View();
}
[HttpPost]
public ActionResult Post(Comment comment, int id)
{
if (ModelState.IsValid)
{
comment.UserId = User.Identity.Name;
comment.MediaElement.Id = id;
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Watch");
}
return View();
}
I need to pass data from partial view and save it to database include the media.Id to know that comment inserted for the video.
Thanks so muchhh
putting scripts on a partial view is generally bad practice. Script gets inserted into the middle of the view. For saving information from a partial view I use ajax calls. To do that add a class to your post button
<input type="button" class="btnSubmit" value="Post" />
then in your script on the main page
$(document).on('click', '.btnSubmit', function(){
$.ajax({
url: '#Url.Action("Action", "Controller")',
cache: false,
async: true,
data: {
//put the data that you want to save from the partial here
id: $('#hiddenID').val(),
content: $('#Content').val()
},
success: function (_result) {
//can add something here like an alert, close a popup something along those lines
}
});
});
just make sure the inputs on your controller match exactly the names that you have defined here
[HttpPost]
public ActionResult Action(int id, string content){
//database call to save the fields
//see here for an example of returning json http://stackoverflow.com/questions/1482034/extjs-how-to-return-json-success-w-data-using-asp-net-mvc
return Json(json);
}
I would suggest using #Html.Action and get the create form from there. Also, once the comments are stored you can redirect to the parent action. That should automatically update the comments list.
But that would be a bad idea.
Rather you can put get your comments by calling action from ajax ($.ajax) and use pure.js to replace the older ones with the new comment list.

ASP.net MVC 3 Edit/Delete issue

So I just finished working the project at http://msdn.microsoft.com/en-us/data/gg685489. I am trying to create a CRUD for use with an external database that already exists and I am just using the project as a guide for my own work. I have been able to change certain variable names and connections to connect with the database that I am trying to work with and I have even displayed the data I want using the index method. I have also been able to get the create method working and I am able to create new data in the DB I am working with. Unfortunatlty I have not been able to pull data in when using the Edit or Delete methods or save/delete after I have made the changes I want.
For example when I press the edit button it should pull the record that I clicked on so that I could edit a particular records information but instead it threw an error saying that this variable cannot accept a null value which is why I changed the int to a string in the edit delete methods. This part is solved
I think the issue has something to do with the Edit and Delete methods not pulling the recordset when I tell it to. Does anyone have any idea why it's not pulling the record sets when I edit/delete or saving my changes after I tell it to save?
I have posted my PaController class which has my CRUD methods in it for diagnosis as well as my iamp_mapping class file which has the 3 fields I need to work with. I am 90% sure I am doing something wrong with the controller, but if you need anymore code/information about the issue please leave me a note I will be checking back A LOT because I am soo stuck! Thanks so much for your help!
PaController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DBFirstMVC.Models;
using System.Data;
namespace DBFirstMVC.Controllers
{
public class PaController : Controller
{
PaEntities db = new PaEntities();
//
// GET: /Pa/
public ActionResult Index()
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.ToList());
}
}
//
// GET: /Pa/Details/5
public ActionResult Details(int id)
{
return View();
}
//
// GET: /Pa/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Pa/Create
[HttpPost]
public ActionResult Create(iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.iamp_mapping.Add(IAMP);
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /Pa/Edit/5
public ActionResult Edit(string id)
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.Find(id));
}
}
//
// POST: /Pa/Edit/5
[HttpPost]
public ActionResult Edit(string id, iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.Entry(IAMP).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View();
}
}
//
// GET: /Pa/Delete/5
public ActionResult Delete(string id)
{
using (var db = new PaEntities())
{
return View(db.iamp_mapping.Find(id));
}
}
//
// POST: /Pa/Delete/5
[HttpPost]
public ActionResult Delete(string id, iamp_mapping IAMP)
{
try
{
using (var db = new PaEntities())
{
db.Entry(IAMP).State = EntityState.Deleted;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
return View();
}
}
}
}
iamp_mapping
using System;
using System.Collections.Generic;
namespace DBFirstMVC.Models
{
public partial class iamp_mapping
{
public string PA { get; set; }
public string MAJOR_PROGRAM { get; set; }
public string INVESTMENT_AREA { get; set; }
}
}
Edit View Code
#model DBFirstMVC.Models.iamp_mapping
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<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>iamp_mapping</legend>
<div class="editor-label">
#Html.LabelFor(model => model.PA)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PA)
#Html.ValidationMessageFor(model => model.PA)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MAJOR_PROGRAM)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MAJOR_PROGRAM)
#Html.ValidationMessageFor(model => model.MAJOR_PROGRAM)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.INVESTMENT_AREA)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.INVESTMENT_AREA)
#Html.ValidationMessageFor(model => model.INVESTMENT_AREA)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Index.cshtml
#model IEnumerable<DBFirstMVC.Models.iamp_mapping>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
PA
</th>
<th>
MAJOR PROGRAM
</th>
<th>
INVESTMENT AREA
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PA)
</td>
<td>
#Html.DisplayFor(modelItem => item.MAJOR_PROGRAM)
</td>
<td>
#Html.DisplayFor(modelItem => item.INVESTMENT_AREA)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PA }) |
#Html.ActionLink("Details", "Details", new { id=item.PA }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PA })
</td>
</tr>
}
The ActionLink is what I had to fix, when I first posted the question it looked like this
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ })
I changed it to the actual primary key PA and deleted the comments. This fixed the problem of data not filling in when trying to edit a row!
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PA }) |
#Html.ActionLink("Details", "Details", new { id=item.PA }) |
#Html.ActionLink("Delete", "Delete", new { id=item.PA })
</td>
</tr>
}
Delete.cshtml
#model DBFirstMVC.Models.iamp_mapping
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>iamp_mapping</legend>
<div class="display-label">PA</div>
<div class="display-field">
#Html.DisplayFor(model => model.PA)
</div>
<div class="display-label">MAJOR_PROGRAM</div>
<div class="display-field">
#Html.DisplayFor(model => model.MAJOR_PROGRAM)
</div>
<div class="display-label">INVESTMENT_AREA</div>
<div class="display-field">
#Html.DisplayFor(model => model.INVESTMENT_AREA)
</div>
</fieldset>
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace DBFirstMVC
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Pa", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
}
Your problem here appears that when you click on the edit button/link the Edit action will expect an id of type int (if you use int), however this can be switched as optional if you use int?...which is how the MVC behaves...your best bet here is to try using a url say something like http://urwebsite.com/Pa/Edit?id=[use a valid id]...with this you will have a id in your Edit action...
Update
I think the issue you are having here is that the Id value in the Edit action method is null...and the MVC engine is unable to recognize an id value from your Url pattern
So, the page where you have the edit link/button you can use something like
Edit
If you have a for loop where you are displaying a collection of records...
#foreach(var item in Model)
{
Edit
}
Also make sure that you have a routing configured for url pattern /Pa/Edit/{id}
You may wanna read this Article if you want to know more about routing in mvc.
This will ensure that when a Get request is made to the edit action method, the id value will be recognized
Hope this helps...
My link wouldnt pick the id up for some reason, so I found a solution from one course.
He added the fifth argument "null" to the function like this
<td>#Html.ActionLink("Edit", "Edit", "Account", new {id = account.Id }, null)</td>
(text, action, controller, route value, null or HTML atribute)
this fixed it for me.

Categories

Resources