MVC controller view binding - c#

Am new to MVC and am trying below scenario but am struck how to proceed.
In my webpage i have several section and each section has comments below it. and for fetching the comments i have written a function inside the controller as below
public ActionResult ShowPreviousComments()
{
Comments com = new Comments();
LstComments savedComments = new LstComments();
///Entity code here
foreach (var item in comments)
{
com.comments = item.Comments;
com.dTime = item.Time;
savedComments.lstCommet.Add(com);
}
return View();
}
and model data below so i can get the list in the view
public class Comments
{
public string comments { get; set; }
public DateTime? dTime { get; set; }
public int airPortId { get; set; }
}
public class LstComments
{
public List<Comments> lstCommet { get; set; }
}
My doubt is how i can hit the controller during the pageload for each sections ?
Sorry if it sounds silly or some error. Please post if i can do it in a better way
Thanks

controller
public ActionResult ShowPreviousComments()
{
Comments com = new Comments();
LstComments savedComments = new LstComments();
///Entity code here
foreach (var item in comments)
{
com.comments = item.Comments;
com.dTime = item.Time;
savedComments.lstCommet.Add(com);
}
return PartialView(savedComments);
}
View
#Html.Action("ShowPreviousComments", "SomeController")
From My Perspective
controller
public ActionResult ShowPreviousComments()
{
// Entity code here
// var comments = entity.comments;
return PartialView(comments);
}
Index.cshtml (Main view that contains own contents and comments)
// some main view content
// main view comments...
#Html.Action("ShowPreviousComments", "SomeController")
ShowPreviousComments.chtml (partial view that hold the previous comments)
#model IEnumerable<comments>
<div class="comments_container>
foreach(var item in Model)
{
<div class="comment_body">#item.Comments</div>
<div class="comment_time">#item.Time</div>
}
</div>

Maybe U can try a script to call the controller on load of the sections of the view

Related

My blog only retrieves one post

I'm creating a blog in C# MVC, but I'm having a problem with the blog posts. From a list of posts, only one post is retrieved, and it's the last one. I wanna retrieve all blog posts with their associated images...
BLOG MODELS
public class Blog
{
[Key]
public int BlogID { get; set; }
public string Title { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public int PostID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogID { get; set; }
public bool hasImages { get; set; }
public virtual Blog Blog { get; set; }
public virtual ICollection<ImageFile> ImageFiles { get; set; }
}
public class ImageFile
{
[Key]
public int ImageID { get; set; }
public string ImageName { get; set; }
public string ImageType { get; set; }
public byte[] ImageBytes { get; set; }
public int PostID { get; set; }
public virtual Post Post { get; set; }
}
METHOD TO RETRIEVE POSTS + IMAGES
public Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> getAllBlogData()
{
var AllPosts = _entities.Posts.ToList();
Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> model = null;
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
if (posts != null)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, images);
}
else
{
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, null);
}
}
}
return model;
}
CONTROLLER
public ActionResult Posts()
{
return View(_blogrepository.getAllBlogData());
}
VIEW
#model Tuple<IEnumerable<Winter.Domain.Entities.Post>, IEnumerable<Winter.Domain.Entities.ImageFile>>
#{
ViewBag.Title = "Posts";
}
<hgroup>
<h3>BLOG</h3>
</hgroup>
#foreach (var post in Model.Item1)
{
<p class="container images">
#if (Model.Item2 != null)
{
foreach (var image in Model.Item2)
{
<span>
<img src="data:image/png;base64,#Convert.ToBase64String(image.ImageBytes)" />
</span>
}
<div class="clearfix"></div>
}
<hgroup>
<h2>#Html.DisplayFor(modelItem => post.Title)</h2>
</hgroup>
<span>
#Html.DisplayFor(modelItem => post.Content)
</span>
</p>
}
<hr />
Thanks in advance
Below, I give you 2 ways of making your code work.
The first being the "correct" way to implement what you're trying to do and the second being a fix of the current code you're using.
Get a beverage, and have a good read. note: this is all off the top of my head (so please forgive in advance - just comment anything that needs correcting)
1) The "correct" way to do it:
It looks like you're using Entity Framework. (If not, then why not?)
That said, I would look into Entity Framework Relationships & Navigation Properties.
This is exactly what Database Relationships are for. Your code is currently making heavy work of what is an in-built feature, and you're using Tuples unnecessarily. EF is there to allow you to use real objects you've already created.
Here's a short(ish) way you should update your structure (this example is using EF Code-First - I find it way easier to create the DB from already-made classes that I use around the program):
Classes
public class Post
{
public int Id { get;set; }
public string Title { get; set; }
// Other properties etc.
public virtual ICollection<ImageFile> Images { get; set; }
}
public class ImageFile
{
public int Id { get; set; }
public byte[] ImageBytes { get; set; }
[ForeignKey(nameof(Post))]
public int PostId { get; set; }
[ForeignKey(nameof(PostId))]
public Post Post { get; set; }
}
This gives you a one-to-many relationship of "one post" has "many image files". The virtual property of Images in the Post class means it is a "navigation property" - this means that EF can figure out the relationship between the ImageFile that have the matching PostID to that post. This is down to FluentAPI's convention (TL;DR)
See how Code First Relationships work in Entity Framework for more information.
Controller
Your controller can then return a List<Post> from your context.
public ActionResult Posts()
{
var posts = _yourDbContext.Posts.Include(x => x.Images).ToList();
return View(posts);
}
Note: You will need to include using System.Data.Entity at the top of your file's using statements, in order to use the Include() method with a lambda expression.
Further note: If you don't "flatten" the results to a List<>, then you might not (off the top of my head) need to use the Include() method - TL;DR: when the page is rendered, it will still have the context in scope, and will be able access the navigation properties using the query (IQueryable<>) it still uses (lazy loading). Include() simply uses eager loading to pull the images out and place them in the List<>.
View
Then, in your view you can simply use the #model as an IEnumerable<Post>, iterating over the List/IEnumerable you passed in; referencing the Images property that way.
Like so:
#model IEnumerable<Your.Namespace.Post>
// Other fluff - page title etc.
#foreach (var post in Model)
{
foreach (var image in post.Images)
{
// Display each image - #image.ImageBytes
}
<h1>#post.Title</h1>
// Other #post.Property printing, here
}
Using this way will enable you to (use a lot less code), and use your classes for what they would usually be designed for in OOP.
I have also included a "dirty" answer - using the current method you're using, below:
2) The "dirty" fix of your code:
Similar to #Jauch's answer, but a little more condensed for both the code, and your view:
Your getAllBlogPosts() method is simply returning the last post in the iteration, every time - you're overwriting model each time.
You could simply return a list of blog posts like this:
private List<Tuple<Post, IEnumerable<ImageFile>>> GetAllBlogPosts()
{
var allPosts =
_entities.Posts.Select(
post =>
new Tuple<Post, IEnumerable<ImageFile>>(
post,
_entities.ImageFiles.Where(x => x.PostID == post.PostID)));
return allPosts.ToList();
}
Then, in your view you can simply iterate the .Item2 property (the images), without having to do a null check beforehand.
This is because, even if there are no results, the IEnumerable<ImageFile> will result as simply an empty collection.
In the view, your model will be an IEnumerable<Tuple<Post, IEnumerable<ImageFile>>> ( whereFQNS is the fully qualified namespace of your classes):
#model IEnumerable<Tuple<FQNS.Post, IEnumerable<FQNS.ImageFile>>>
Your view can then iterate over each Tuple in the Model - let's say item.
Then inside the item, you can reference Value1 (being the Post) and Value2 (being the Post's images).
Like so:
#foreach (var item in Model)
{
<p class="container images">
foreach (var image in item.Item2)
{
<span>
<img src="data:image/png;base64,#Convert.ToBase64String(image.ImageBytes)" />
</span>
}
<div class="clearfix"></div>
<hgroup>
<h2>#Html.DisplayFor(x => item.Value1.Title)</h2>
</hgroup>
<span>
#Html.DisplayFor(x => item.Value1.Content)
</span>
</p>
}
I hope that this helps you along the way with your project, whichever method you take.
I would heavily advise to research into EF's relational capabilities and using navigation properties in order to make your code much cleaner (and your life much easier).
Hope this helps! :)
I would say that the problem is in this part of the code:
public Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> getAllBlogData()
{
var AllPosts = _entities.Posts.ToList();
Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> model = null;
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
if (posts != null)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, images);
}
else
{
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, null);
}
}
}
return model;
}
First, you load all posts:
var AllPosts = _entities.Posts.ToList();
Then you iterate on each post on AllPosts, and look for all posts that have the same ID than this one:
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
Assuming that PostID is a "unique" KEY (like you showed in the code), you will find just the post itself.
Than you create the "model". And do this for each post. At the end, "model" will contain only the last post from the first foreach.
I think you need to return a "list" of posts (and associated images), so, I would try to change the code in this way:
public List<Tuple<Post, IEnumerable<ImageFile>>> getAllBlogData()
{
var list = new List<Tuple<Post, IEnumerable<ImageFile>>();
Tuple<Post, IEnumerable<ImageFile>> tuple = null;
var AllPosts = _entities.Posts.ToList();
foreach (var post in AllPosts)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
tuple = new Tuple<Post, IEnumerable<ImageFile>>(post, images);
}
else
{
tuple = new Tuple<Post, IEnumerable<ImageFile>>(post, null);
}
list.Add(tuple);
}
return list;
}
And, of course, adapt you file to deal with the list instead of the "model".
The code above should return all the posts in your blog, through a list that has tuples where on data is the POST, and the other is the list of images associated with that post.

Access Generics List in ASP.NET MVC 5 View

I am getting started with MVC and I have the following Model
public class FormControls
{
//Properties of the FormControls object
public string formCName { get; set; }
public string formCType { get; set; }
public string formCCss { get; set; }
public string formCEnabled { get; set; }
public string formCDefaultVal { get; set; }
}
I also created the following control and I am querying a database using linq to select records. Each record will then have to be added to a list.
public ActionResult Index()
{
var DataContext = new EditProfileFormFieldsDataContext();
var controls = from c in DataContext.Controls
select c;
List<FormControls> Fields = new List<FormControls>();
foreach(var fc in controls)
{
//Create Object for Generic List
FormControls epc = new FormControls();
epc.formCName = fc.Control_Name;
epc.formCType = fc.Control_Type;
epc.formCCss = fc.Control_CSS;
epc.formCEnabled = fc.Control_Enabled;
epc.formCDefaultVal = fc.Control_DefaultVal;
//Add Object to FormControls Generic List
Fields.Add(epc);
}
return View("EditProfile");
}
My question is how would I access the list using RAZOR in the view? I am trying to loop through the list I created in the view. I am fairly new to MVC and I think I am over thinking a lot of this :) Thanks!
You can make the model of your view a List. Put this at the top of your view:
#model List<FormControls>
Change the return of your Index() method:
return View("EditProfile", Fields);
Then you can access it from the view by using #Model. For example, to iterate through it:
#foreach (var field in Model)
{
<p>#field.formCName</p>
}
Btw, there is a more direct way to implement the controller.
public ActionResult Index()
{
var DataContext = new EditProfileFormFieldsDataContext();
return View("EditProfile", DataContext.Controls.ToList());
}
or if you rename the view to "index.cshtml", you can do it like this:
public ActionResult Index()
{
var DataContext = new EditProfileFormFieldsDataContext();
return View(DataContext.Controls.ToList());
}
Suppose you do not have the index.cshtml yet, right click the "View(" and select "Add View", in the pop-up wizard, select "list view" and FormControls, there will be an auto-generated view with #model defined and a well done table demoing how to use it.

Issue with editing data and model binding

I started working with asp.net and I have encountered a problem when I try to edit multiple values from a table. I have a bookmark tables which is connected to another tag table, with an 1 : N relationship. My problem is when I want to edit already existing tags associated with an existing url. I can display them on the page but when I try to post the edited data I don't know how to pick it up in the controller. So far I have managed to send them back as a string but I doubt that is the solution since I have to edit all the data again later. I want to replace the existing values in the Tag table with the edited data. Here are my model and controller code snippets.
Bookmark model:
public int id { get; set; }
public string url { get; set; }
public virtual ICollection<Tag> tags { get; set; }
Tag model:
public int id { get; set; }
public string name { get; set; }
public virtual Bookmark bookmark { get; set; }
public string user { get; set; }
Controller:
public ActionResult Edit(int id)
{
var editBookmark = adc.Bookmarks.Single(x => x.id == id);
var query_where2 = from a in adc.Tags
where a.bookmark.id == id
select a;
BookmarkTag bkTag = new BookmarkTag();
bkTag.bookmark = new List<Bookmark>();
bkTag.bookmark.Add(editBookmark);
bkTag.tag = query_where2.ToList();
return View(bkTag.tag);
}
//
// POST: /SavedBookmark/Edit/5
[HttpPost]
public ActionResult Edit(int id, ICollection<FormCollection> tag)
{
try
{
return View();
}
catch
{
return View();
}
Html code:
#using (Html.BeginForm("edit", "SavedBookmark"))
{
#Html.AntiForgeryToken()
if (Model != null) {
var aa= Model.First();
#Html.TextBox("test2", aa.bookmark.url);
List<BookIT2.Models.Tag> allTags = new List<BookIT2.Models.Tag>();
allTags = Model.ToList();
for (int i = 0; i < allTags.Count; i++)
{
if (!allTags[i].name.IsEmpty())
{
#Html.TextBox(allTags[i].name, allTags[i].name);
#Html.Hidden(allTags[i].id.ToString(), allTags[i].id);
#Html.Hidden(allTags[i].user, allTags[i].user)
#Html.Hidden(allTags[i].bookmark.id.ToString(), allTags[i].bookmark.id.ToString())
}
}
#Html.Label("Additional tag")
#Html.TextBox("additionalTag")
<input type="submit" value="edit" />
}
In short: I can't get any values in the http post ICollection, it's always null.
Here is the updated code:
#using (Html.BeginForm("edit", "SavedBookmark"))
{
#Html.AntiForgeryToken()
if (Model != null)
{
for (int i = 0; i < Model.tag.Count; i++)
{
if (!Model.tag[i].name.IsEmpty()) {
#Html.Hidden(Model.tag[i].id.ToString(), Model.tag[i].id);
#Html.Label("name");
#Html.TextBox(Model.tag[i].name, Model.tag[i].name);
#Html.Hidden(Model.tag[i].bookmark.id.ToString(), Model.tag[i].bookmark.id);
#Html.Hidden(Model.tag[i].user, Model.tag[i].user);
}
}
#Html.TextBox(Model.bookmark.id.ToString(), Model.bookmark.url);
<input type="submit" value="edit" />
}
}
Model class:
public class TestBookmark
{
public Bookmark bookmark{get; set;}
public List<Tag> tag {get; set;}
}
[HttpPost]
public ActionResult Edit(TestBookmark edit)
{}
Don't really understand why you're doing it this way. I would like to suggest you totally different approach.
First:
Create a class with all the fields you want in your view.
Second:
Use this class as the MODEL in your View
Third:
In the controller, in the POST function user your class as the only one parameter of that function.

Pass a viewModel to the layout

I have this viewmodel that has some properties and stuff that i would like to apply
to the layoutpage:
public class BasicViewModel
{
public Page Page { get; set; }
public List<Settings> Settings { get; set; }
}
From other threads here have i understood that this is possible but i dont really understand how.
From what I understand I somehow need to modify a controller and this is where I get confused. How do I know what controller that has to be modified and how?
Any help appreciated.
In controller, Prepare an action like
public ActionResult BasicViewModelDemo
{
BasicViewModel obj=new BasicViewModel()
// assign properties
return View(obj);
}
and view write some jquery. (Here i am using knockout to make view model)
<script>
var model='#Html.Raw(Json.Encode(Model))';
var viewmodel = ko.mapping.fromJSON(model);
</script>
Here goes my solution -
Lets say you have a model of this way -
public class BasicViewModel
{
public Page Page { get; set; }
public List<Settings> Settings { get; set; }
}
public class Page
{
public string PageName { get; set; }
}
public class Settings
{
public string SettingName { get; set; }
}
Then in the controller you should initiate the model in this way -
public class HomeController : Controller
{
BasicViewModel model;
public HomeController()
{
model = new BasicViewModel();
model.Page = new Page();
model.Settings = new List<Settings>();
}
public ActionResult Index()
{
model.Page.PageName = "My Page";
ViewBag.LayoutModel = model;
return View();
}
}
So basically we used Constructor to initiate the model and then we assign proper values in the controller action.
Then in the Layout, we can use the Model property as shown below -
<div> #ViewBag.LayoutModel.Page.PageName </div>

Grab post back collection and show it on list over webpage

I have an MVC webpage with a DropDownList full of items.
Every item is an object from my Database that represent a file on disk.
My object class:
namespace CapturesMVC.Models
public class Capture : IEquatable<Capture>
{
public int id { get; set; }
[Display(Name = "File Name")]
public string fileName { get; set; }
[Display(Name = "Browser")]
public string browser { get; set; }
[Display(Name = "Mobile")]
public string mobile { get; set; }
[Display(Name = "Protocol")]
public string protocol_site { get; set; }
public string family { get; set; }
public sealed override bool Equals(object other)
{
return Equals(other as Capture);
}
public bool Equals(Capture other)
{
if (other == null)
{
return false;
}
else
{
return this.protocol_site == other.protocol_site;
}
}
public override int GetHashCode()
{
return protocol_site.GetHashCode();
}
}
CaptureDBContext class:
namespace CapturesMVC.Models
public class CaptureDBContext : DbContext
{
public DbSet<Capture> Captures { get; set; }
}
This is my controller:
[HttpPost]
public ActionResult Index(string File)
{
var list = db.Captures.Where(x => x.protocol== File).ToArray();
ViewBag.Files = list;
return View();
}
Index.cshtml:
#using (Html.BeginForm())
{
<div>
#Html.DropDownList("File", new SelectList(ViewBag.Files, "protocol_site", "protocol_site"), "Select webmail site", new { style = "vertical-align:middle;" })
<button type="submit">Select</button>
</div>
}
</body>
After choosing an item from my DropDownList and hitting the button, the Index action is executed and returns list of objects that match one of my object properties and this list I want to show over my webpage inside a list, but the current situation is that this list is inserted into my DropDownList.
You want to implement Cascading DropDownList
check this example 'Cascading DropDownList in ASP.Net MVC' on msdn code or this on c-sharpcorner
The problem is that you put the objects in the same ViewBag property that your Dropdownlist gets its values from.
You could make a List and put that in your ViewBag:
List<Capture> list = db.Captures.Where(x => x.protocol== File).ToList();
ViewBag.data = list;
And enumerate over these and display some html in your view (within an unordered list for example). But you have to cast it back to a list first:
#using Namespace.Capture
...
<ul>
foreach (var item in (ViewBag.data as List<Capture>))
{
<li>#item.Property</li>
}
</ul>
ViewBag is a C# 4 dynamic type. You need to cast the entities from it to use them in a type-safe way.
But I would recommend using a view model with the list as a property and sending that to the view from your controller action.

Categories