MVC 4 persist search on next page - asp.net

I have created a View Form which include SearchModel and result as well.
Here below is my search model.
public class UserViewModel
{
public string Status { get; set; }
public string Type { get; set; }
public string Search { get; set; }
public string SortBy { get; set; }
public string SortOrder { get; set; }
public IPagedList<Users> Users{ get; set; }
}
public ActionResult Index(UserViewModel filter, int? page)
{
filter.Users=GetUsersFromDatabase().ToList();
}
public ActionResult ToggleActive(bool IsActive, Guid Id)
{
// Set update operation to user
return RedirectToAction("Index", new { page = Request["page"] });
}
Now I want to redirect user on ToggleActive based on UserViewModel filter values on listing page. I hope you get my point.
How to pass the search only model. Please let me know the easy way.
In my view I have created the view.
<a href="#Url.Action("ToggleActive", "User", new { IsActive = item.IsActive, Id = item.UserID, page = ViewData["CurrentPage"]})">
Active/Deactive
</a>

Make sure your view has defined the model:
#model UserViewModel
Send your current model back to the controller's ToggleActive action:
public ActionResult ToggleActive(bool IsActive, Guid Id, int page, UserViewModel filter)
{
// Do stuff with the filter
// Set update operation to user
return RedirectToAction("Index", new { page = Request["page"] });
}
And change your action url to include the model:
<a href="#Url.Action("ToggleActive", "User", new { IsActive = item.IsActive, Id = item.UserID, page = ViewData["CurrentPage"], filter = Model})">
Active/Deactive
</a>

Related

ASP.NET Razor - How to create a POST form for List of objects?

I need to create a POST form to add new objects to database. I have to create a Razor page where I can add new lesson form on click of a button. And after it on click of another button all the lessons should be added to DB context. I still don't know how to do it so I want you to help me
public class Course
{
public Guid Id { get; set; }
public string Category { get; set; }
public string Title{ get; set; }
public List<Lesson> Lessons { get; set; } = new List<Lesson>();
}
public class Lesson
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
}
Here is some image of what I mean:
DB has a Course table and a Lesson table. Please tell me how I can create a page to create new 'Course' with dynamic amount of 'Lessons'
Please try with this in your controller method you need to pass from the page.
public ActionResult PostMethod(Course course, FormCollection formCollection)
{
string title = course.Title; // Title value
string category = course.Category; // Category value
//add course into database
if (course.Lessons != null && course.Lessons.Count > 0)
{
foreach (var item in course.Lessons)
{
//add Lessons into database
}
}
}
Please note in this example you are going to perform many operations in databases
you have to manage transaction as well for this.
Using Transactions or SaveChanges(false) and AcceptAllChanges()?

Redirecting after deleting an item is always returning null

I'm using ASP.NET MVC to build an application for Forums. I have an entity named Posts and an entity named PostReplies.
On a particular Post, there will be a list of replies which are linked by a FK Post_Id in my PostReplies entity.
When I delete a reply on a post and call:
RedirectToAction(GetPost, Post, new { id = post.id});
(gets the individual post, with list of replies on it)
I get an error relating to this bit of code:
var replies = post.Replies;
(the post, always returns null)
I'm not sure why this is, it always redirects fine when I add a reply and then redirect back to the post.
I feel like I'm doing something fundamentally wrong when I'm calling delete method. I'll expand the logic I have below:
Post entity:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime Created { get; set; }
public virtual Discussion Discussion { get; set; }
public virtual ICollection<PostReply> Replies { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
PostReply entity:
public class PostReply
{
public int Id { get; set; }
public string Content { get; set; }
public DateTime Created { get; set; }
public virtual Post Post { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
ReplyController - delete a reply:
[HttpGet]
public ActionResult DeleteReply(int id)
{
return View(_replyService.GetReply(id));
}
[HttpPost]
public ActionResult DeleteReply(int id, PostReply reply, Post posts)
{
var replies = _replyService.GetReply(id);
_replyService.DeleteReply(id, reply, posts);
return RedirectToAction("GetPost", "Post", new { id = posts.Id });
}
_replyService logic (called in the controller above):
public void DeleteReply(int id, PostReply reply, Post posts)
{
var replytoDelete = _context.Replies
.FirstOrDefault(r => r.Id == id);
if (replytoDelete != null)
{
_context.Replies.Remove(replytoDelete);
_context.SaveChanges();
}
}
PostController - get individual post:
public ActionResult GetPost(int id)
{
Post post = _postService.GetPost(id);
var replies = post.Replies;
var listofReplies = replies.Select(reply => new NewPostReplyModel
{
Id = reply.Id,
ReplyPosted = reply.Created,
ReplyContent = reply.Content,
ReplyUserId = reply.ApplicationUser.Id,
ReplyUserName = reply.ApplicationUser.UserName
});
var model = new GetPostViewModel
{
Replies = listofReplies,
Posts = BuildNewPost(post)
};
return View(model);
// return View("GetPost", post);
}
private NewPostModel BuildNewPost(Post post)
{
return new NewPostModel
{
PostId = post.Id,
PostContent = post.Content,
PostTitle = post.Title,
DatePosted = post.Created.ToString(),
DiscussionName = post.Discussion.Title,
DiscussionId = post.Discussion.Id,
UserId = post.ApplicationUser.Id,
UserName = post.ApplicationUser.UserName,
};
}
GetReply logic in service:
public PostReply GetReply(int id)
{
return _context.Replies.Find(id);
}
I think you are not including replies in your GetPost() methode,
So check if your code is like this:
public Post GetPost(int id)
{
return _context.Posts.Include(p=>p.Replies).FirstOrDefault(p=>p.Id == id);
}

How to use eager loading for two controllers in mvc?

I have two models and their tables. Restaurant:
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Image { get; set; }
[InverseProperty("Restaurant")]
[InverseProperty("Restaurant")]
public ICollection<Menu> Menus { get; set; }
And Menu:
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Image { get; set; }
public int RestaurantId { get; set; }
public virtual Restaurant Restaurant { get; set; }
And I created controller by using EntityFramework with views. I have Restaurant Index view which shows list of restaurants and it has to have menu button in every restaurant info. Also I have Menu Index view which shows menu items. And now my problem is to show menu items which relates to specific restaurant.
RestaurantIndex Actionresult:
public ActionResult Index()
{
var restaurants = db.Restaurants;
return View(restaurants.ToList());
}
MenuIndex ActionResult:
public ActionResult Index()
{
var menus = db.Menus.Include(m => m.Restaurant);
return View(menus.ToList());
}
That my restaurant's list:
enter image description here
And it is menu:
but it shows all items
I searched about eager loading, but couldn't find similar examples. Can someone tell me how to realize it?
From looking at the code an the 'images' you have. I would simply pass the Id of the restaurant as nullable int? into the Index of the MenuController such as:
public ActionResult Index(int? restaurantId = null)
{
List<Menu> menus = new List<Menu>();
if(menuId != null){
menus = db.Menus.Include(m => m.Restaurant).Where(x => RestaurantId == restaurantId);
}
else{
menus = db.Menus.Include(m => m.Restaurant)
}
return View(menus.ToList());
}
Then above we filter against the restaurant ID in the controller instead of the view.
In the menu view you should not have to change anything you have done so far.
By having the ID as a nullable parameter, this Index method can be called using an ID or not.

Loading related data in ASP.net MVC

Something very simple but I am looking for the best way to do it. I have a Movie entity, each Movie can be in one Language only(a lookup table with English, French,etc...). Now I'm trying to load all the available languages in the lookup in the Movie Create Page, the Movie View Model:
namespace Project.ViewModels {
public class Movie {
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string Rating { get; set; }
public string Director { get; set; }
public string Plot { get; set; }
public string Link { get; set; }
public string Starring { get; set; }
public int DateCreated { get; set; }
public string Genre { get; set; }
[Display(Name = "Language")]
public int LanguageID { get; set; }
// Navigational Properties
public virtual MovieLanguage Language { get; set; }
}
}
The MovieLanguage View model:
namespace MAKANI.ViewModels {
public class MovieLanguage {
[Key]
public int ID { get; set; }
public string Language { get; set; }
public virtual ICollection<Movie> Movies { get; set; }
}
}
The controller action:
public ActionResult MovieCreate() {
using (MAKANI.Models.Entities db = new MAKANI.Models.Entities()) {
List<Models.MoviesLanguages> enLanguages = db.MoviesLanguages.ToList();
IEnumerable<SelectListItem> selectList =
from m in enLanguages
select new SelectListItem {
Text = m.Language,
Value = m.ID.ToString()
};
ViewBag.SelectLanguage = selectList.ToList();
return View();
}
}
And in the View page i have
<div class="editor-field">
#Html.DropDownList("Language", ViewBag.SelectLanguage);
</div>
Howver I am getting this error in the View:
'System.Web.Mvc.HtmlHelper' has no applicable method named 'DropDownList' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax
Not sure what the problem might be?
Another questions regarding this approach:
Should a create a view model for the MovieLanguage entity in the first place, knowing that it servers only as a lookup table(so it doesnt require any Create/Edit/Delete action, Only List/Read maybe), should I be depending on the EF entities directly in that case?
Have a Languages Collection Property in your Movie ViewModel and a SelectedLanguage Property to get the selected Language ID when the form submits. It is not necessary that your ViewModel should have all the properties like your domain model. Have only those properties which the View needs.
public class Movie
{
public int ID { set;get;}
public string Title { set;get;}
//Other Relevant Properties also.
public IEnumerable<SelectListItem> Languages { set;get;}
public int SelectedLanguage { set;get;}
public Movie()
{
Languages =new List<SelectListItem>();
}
}
Now in your GET Action, Create an object of your Movie ViewModel and set the Languages Collection property and send that to the View. Try to avoid using ViewBag for passing data like this. ViewBag makes our code dirty.Why not use the strongly typed ViewModels to its full extent ?
public ActionResult CreateMovie()
{
var vm=new Movie();
// TO DO : I recommend you to abstract code to get the languages from DB
// to a different method so that your action methods will be
// skinny and that method can be called in different places.
var enLanguages = db.MoviesLanguages.ToList();
vm.Languages= = from m in enLanguages
select new SelectListItem {
Text = m.Language,
Value = m.ID.ToString()
};
return View(vm);
}
And in your view which is strongly typed to our Movie ViewModel, use the DropDownListFor Hemml helper method
#model Movie
#using(Html.Beginform())
{
#Html.DropDownListFor(x => x.SelectedLanguage,
new SelectList(Model.Languages, "Value", "Text"), "Select Language")
<input type="submit" />
}
Now when you post the form, you will get the selected languageId in the SelectedLanguage Property of your ViewModel
[HttpPost]
public ActionResult CreateMovie(Movie model)
{
If(ModelState.IsValid)
{
//check model.SelectedLanguage property here.
//Save and Redirect (PRG pattern)
}
//you need to reload the languages here again because HTTP is stateless.
return View(model);
}

How to Preserve/Protect Certain Fields in Edit in ASP.NET MVC

In an Edit action in ASP.NET MVC, certain fields can be hidden from user with HiddenFieldFor. However this doesn't protect the fields (such as ID, data creation date) from being edited.
For example, a model Student has fields Id, Name and Birthday. I like to allow users to update the Name, but not Id nor Birthday.
For an Edit action like this
public ActionResult Edit(Student student)
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(student);
}
How can I prevent Id and Birthday from being edited? Thanks!
You should use a view model which contains only the properties that you want to be edited:
public class EditStudentViewModel
{
public string Name { get; set; }
}
and then:
public ActionResult Edit(StudentViewModel student)
{
...
}
Another technique which I don't recommend is to exclude certain properties from binding:
public ActionResult Edit([Bind(Exclude = "Id,Birthday")]Student student)
{
...
}
or include:
public ActionResult Edit([Bind(Include = "Name")]Student student)
{
...
}
I assume you have to have the properties in your Model so that in View you can use them to render useful information e.g. an ActionLink with ID or some readonly Text.
In this case you can define your model with an explicit binding:
[Bind(Include = "Name")]
public class Student
{
int Id { get; set; }
int Name { get; set; }
DateTime Birthday { get; set; }
}
This way when updating your model, if the user submits an extra Id it will not be bound.
Another idea I like is having your model know its bindings per scenario and have them compiler validated:
public class ModelExpression<T>
{
public string GetExpressionText<TResult>(Expression<Func<T, TResult>> expression)
{
return ExpressionHelper.GetExpressionText(expression);
}
}
public class Student
{
public static string[] EditBinding = GetEditBinding().ToArray();
int Id { get; set; }
int Name { get; set; }
DateTime Birthday { get; set; }
static IEnumerable<string> GetEditBinding()
{
ModelExpression<Student> modelExpression = new ModelExpression<Student>();
yield return modelExpression.GetExpressionText(s => s.Name);
}
}
This way in your Action when calling TryUpdateModel you can pass this information.

Resources