How can I pass values from a Linq select from multiple tables to a view? I have a model that shows the contents of a page. A page can have multiple contents and which contente can have multiple files.
I have a ViewModel to assembly the information of the contents:
namespace WeChange.ViewModels
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class FicheirosInformationViewModel
{
public int id_filec { get; set; }
public string filenamec { get; set; }
public string fileurlc { get; set; }
public string fileimg { get; set; }
public string fileextc { get; set; }
}
public class FicheirosConteudosViewModel
{
public int id_conteudo { get; set; }
public string conttitle { get; set; }
public string conttext { get; set; }
public DateTime contdate { get; set; }
public ICollection<FicheirosInformationViewModel> FicheirosInformation { get; set; }
}
}
In the controller I have:
public ActionResult Index(FicheirosInformationViewModel FichInfoVM, FicheirosConteudosViewModel FichConteVM)
{
ViewBag.Message = "Academia Page";
var cwc_academia = db.CWC_CONTEUDOS.Include(c => c.CWC_PAGINAS).Where(c => c.CWC_PAGINAS.id_page == 1);
foreach (var itemfile in cwc_academia)
{
var FichesContes = new FicheirosConteudosViewModel();
FichesContes.id_conteudo = itemfile.id_conteudo;
FichesContes.conttitle = itemfile.conttitle;
FichesContes.conttext = itemfile.conttext;
FichesContes.contdate = itemfile.contdate;
var ficheirosconteudos = from c in db.CWC_FILESCONTEUDOS
join d in db.CWC_FICHEIROS on c.idfile equals d.id_file
join e in db.CWC_TIPOSFICHEIROS on d.idfiletype equals e.id_tpfile
join f in db.CWC_EXTENSOESFILES on e.id_tpfile equals f.idtpdoc
where c.idconte == itemfile.id_conteudo
select new FicheirosInformationViewModel()
{
id_filec = d.id_file,
filenamec = d.filename,
fileurlc = d.fileurl,
fileimg = e.tipoimg,
fileextc = f.extensao
};
}
return View(FichConteVM);
}
}
And in the View:
#model IEnumerable<WeChange.ViewModels.FicheirosConteudosViewModel>
#foreach (var item in Model)
{
<div class="divider"><div class="circle"><img src="/Images/orange.png" alt="" /></div></div>
<div id="acad" class="container">
<div class="jumbotron">
<h2>#Html.Raw(item.conttitle)</h2>
#Html.Raw(item.conttext)
</div>
</div>
foreach (var fich in item.FicheirosInformation)
{
#fich.id_filec
<br />
#fich.filenamec
<br />
#fich.fileurlc
}
}
I´m always getting an error:
The model item passed into the dictionary is of type 'WeChange.ViewModels.FicheirosConteudosViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[WeChange.ViewModels.FicheirosConteudosViewModel]'.
What I´m going wrong?
The view is expecting a collection of FicheirosConteudosViewModel and you have passed a single object to it.
Try this:
return View(ficheirosconteudos);
Which will use the FicheirosInformationViewModel collection from your LINQ statement and then pass it to the View.
Related
I'm trying to present a table with a list of rows from the database, as well as a small form option for the user to add an additional db row. I made two Models in order to both hold the table rows and capture the form. I'm not having problems with the table model, just the form model.
So I'm confident the problem is in my Controller; I am new to MVC and this is the first time I've seen two models in the same VM... I've been going back and forth making small changes to things, but I keep getting a Null Reference Error in my Controller.
VM:
namespace User.ViewModel
{
public class UploadDocumentTemplatesViewModel
{
public string TemplateName { get; set; }
public string Comments { get; set; }
}
public class TemplateModel
{
public UploadDocumentTemplatesViewModel NewTemplate { get; set; }
public List<UploadDocumentTemplatesViewModel> Templates { get; set; }
}
}
View (UploadDocumentTemplates.cshtml):
#using (Html.BeginForm("AddNewTemplate", "User", FormMethod.Post, new { enctype = "multipart/form-data", id = "NewTemplateForm"}))
{
#Html.AntiForgeryToken()
<div>
<input type="text" class="form-control" rows="5" id="TemplateName" value="#Model.NewTemplate.TemplateName">
<label for="comments">Comments:</label>
<textarea class="form-control" rows="5" id="comments" name="#Model.NewTemplate.Comments"></textarea>
<button id="AddTemplate" onclick="AddNewTemplate()" name="Add Template">Add Template</button>
</div>
}
<table>
<tbody>
#{ foreach (var template in Model.Templates)
{
<tr id="tr_#template.DocumentTemplateId">
<td>#template.TemplateName</td>
<td>#template.Comments </td>
<td>#template.IsActive </td>
}
<script>
function AddNewTemplate() {
$("#NewTemplateForm").submit();
}
</script>
Controller:
public class UserController : Controller
{
public ActionResult UploadDocumentTemplates()
{
var model = entity.DocumentTemplates.Select(x => new UploadDocumentTemplatesViewModel()
{
DocumentTemplateId = x.DocumentTemplateId,
TemplateName = x.TemplateName,
Comments = x.Comments,
});
var _obj = new TemplateModel();
var templatelist = _entities.GetTemplates();
_obj.Templates = new List<UploadDocumentTemplatesViewModel>();
foreach (var template in templatelist)
{
_obj.Templates.Add(new UploadDocumentTemplatesViewModel()
{
TemplateName = template.TemplateName,
Comments = template.Comments,
});
}
_obj.NewTemplate = new UploadDocumentTemplatesViewModel();
return View(_obj);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddNewTemplate(TemplateModel vModel)
{
var template = new DocumentTemplates();
template.TemplateName = vModel.NewTemplate.TemplateName;
template.Comments = vModel.NewTemplate.Comments;
entity.DocumentTemplates.Add(template);
entity.SaveChanges();
return View("~/User/UploadDocumentTemplates.cshtml", vModel);
}
}
I hit a Null Reference at this line in my Post method in the Controller:
template.TemplateName = vModel.NewTemplate.TemplateName;
The vModel.NewTemplate.TemplateName is null. Can anyone help me with this?
Thank you so much!
SOLUTION:
The NRE did get fixed with Andy's help (see answer). My further issue was fixed after I populated the new VM with the fields I was using in my form.
namespace Example.Areas.ViewModel
{
public class UploadDocumentTemplatesViewModel
{
public long DocumentTemplateId { get; set; }
public string TemplateName { get; set; }
public string Comments { get; set; }
}
public class TemplateModel
{
public string TemplateName { get; set; }
public string Comments { get; set; }
public HttpPostedFileBase TemplateFile { get; set; }
public List<UploadDocumentTemplatesViewModel> Templates { get; set; }
}
}
In the controller I populated the TemplateModel and sent that to the View:
public ActionResult UploadDocumentTemplates()
{
var templateList = Repository.GetTemplates();
var _obj = new TemplateModel
{
Templates = templateList.Select(x => new UploadDocumentTemplatesViewModel()
{
DocumentTemplateId = x.DocumentTemplateId,
TemplateName = x.TemplateName,
Comments = x.Comments
}).ToList()
};
return View(_obj);
}
Then I put #using Example.Areas.ViewModel.TemplateModel on my View and used Model.Templates for the table, and Model.TemplateName and Model.Comments in the form.
Add a breakpoint in the line where you hit the Null Reference and check if vModel.NewTemplate has a valid instance.
You can add a constructor in TemplateModel class to create a new instance of UploadDocumentTemplatesViewModel()
namespace User.ViewModel
{
public class UploadDocumentTemplatesViewModel
{
public string TemplateName { get; set; }
public string Comments { get; set; }
}
public class TemplateModel
{
public TemplateModel()
{
NewTemplate = new UploadDocumentTemplatesViewModel();
}
public UploadDocumentTemplatesViewModel NewTemplate { get; set; }
public List<UploadDocumentTemplatesViewModel> Templates { get; set; }
}
}
OR
create an instance after instantiating template:
var template = new DocumentTemplates();
template.NewTemplate = new UploadDocumentTemplatesViewModel();
So im trying to insert into my database the values from check boxes that the user checked.
In my ViewModel:
[Display(Name = "Title")]
public string Title { get; set; }
public IEnumerable<SelectListItem> UserTitlelist { get; set; }
public IEnumerable<SelectListItem> Titles { get; set; }
In my View:
#foreach (var item in Model.Titles)
{
<label class="managelabel" style="padding: 0 5px 0 5px;"><input name="Title" type="checkbox" value="#item.Value" #checkedcheckbox> #item.Text</label>
}
In my Controller:
var titleToInsert = new UserTitle
{
UserId = currentUserId,
TitleId = model.Title[];
};
UserManagerService.UpdateUserTitles(titleToInsert);
In UserManagerService:
public static int UpdateUserTitles(UserTitle userTitle)
{
using (ITransaction transaction = Context.BeginTransaction())
{
foreach (var x in userTitle)
{
Context.Save(userTitle);
}
transaction.Commit();
}
return 0;
}
You view model is incorrect and has no relationship at all to what you are editing. And SelectListItem is a class for use in #Html.DropDownListFor(), not for a collection of checkboxes.
You view models should be
public class TitleVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class UserTitleVM
{
.... // other properties
public List<TitleVM> Titles { get; set; }
}
And in the view
#model UserTitleVM
#using (Html.BeginForm())
{
....
for(int i = 0; i < Model.Titles.Count; i++)
{
#Html.HiddenFor(m => m.Titles[i].ID)
#Html.CheckBoxFor(m =>m.Titles[i].IsSelected)
#Html.LabelFor(m => m.Titles[i].IsSelected, Model.Titles[i].Name)
}
and in the controller
public ActionResult Edit(UserTitleVM model)
{
// Get the ID's of the selected titles
List<int> selectedTitles = model.Titles.Where(t => t.IsSelected).Select(t => t.ID);
....
I found the answer is quite simple:
in the controller:
var myList = Request.Form["Title"];
foreach (var item in myList.Split(','))
{
var titleToInsert = new UserTitle
{
UserId = currentUserId,
TitleId = Convert.ToInt32(item)
};
UserManagerService.UpdateUserTitles(titleToInsert);
}
then in UserManagerService:
public static int UpdateUserTitles(UserTitle userTitle)
{
using (ITransaction transaction = Context.BeginTransaction())
{
Context.Save(userTitle);
transaction.Commit();
}
return 0;
}
This way each record gets saved individually
I'm trying to pass few ViewModels to the same View via ViewData. Unfortunately I'm new to MVC and I do not have idea what's wrong with that.
Here is first DataViewModel:
public class TagsViewModel
{
public string TagName { get; set; }
public int TagId { get; set; }
}
And another one:
public class ShortPostViewModel
{
public int PostId { get; set; }
public string PostSubject { get; set; }
public DateTime? PostCreated { get; set; }
public string PostImage { get; set; }
public string PostAuthor { get; set; }
public byte? PostRating { get; set; }
public List<PostTagsViewModel> PostedTags { get; set; }
}
Here is repository:
public IEnumerable<BlogPostViewModel.ShortPostViewModel> GetLast20()
{
var last = from a in _db.blog_post
orderby a.Posted descending
select new BlogPostViewModel.ShortPostViewModel
{
PostId = a.ID,
PostAuthor = (from u in _db.users where u.ID == a.Author
select u.Login).FirstOrDefault(),
PostCreated = a.Posted,
PostImage = a.PostAvatar,
PostRating = a.Rating,
PostSubject = a.Subject,
PostedTags = (from b in _db.tags
join c in _db.posted_tags on b.ID equals c.TagID
where c.PostID == a.ID
select new PostTagsViewModel
{
TagId = b.ID,
TagName = b.TagName
}).ToList()
};
return last.Take(20);
}
And one more:
public IEnumerable<TagsViewModel> GetAll()
{
var t = from a in _db.tags
select new TagsViewModel
{
TagId = a.ID,
TagName = a.TagName
};
return t;
}
So here is Controller:
public ActionResult Index()
{
ViewData["ShortPost"] = _postRepository.GetLast20().AsEnumerable();
ViewData["Tags"] = _tagsRepository.GetAll().AsEnumerable();
return View();
}
So on the View:
<ul class="list-group">
#foreach (var item in (IEnumerable<ShortPostViewModel>)ViewData["ShortPost"])
{
<li class="list-group-item">
<img src="#item.PostImage" alt=""/>
<h3>#Html.ActionLink(#item.PostSubject, "Details", "BlogPost", new { id = item.PostId }, null)</h3>
Создано: #item.PostCreated. Автор: #item.PostAuthor. Оценка: #item.PostRating.
<p>
Темы статьи:
#foreach (var tag in #item.PostedTags)
{
<i class="glyphicon glyphicon-tag"></i> #Html.ActionLink(#tag.TagName, "Tag", "Search", new { id = tag.TagId }, null)
}
</p>
</li>
}
</ul>
</div>
<div class="col-md-4">
#foreach (var tag in (IEnumerable<TagsViewModel>)ViewData["Tags"])
{
<span class="label label-info"><i class="glyphicon glyphicon-tag"></i> #Html.ActionLink(#tag.TagName, "Tag", "Search", new { id = tag.TagId }, null)</span>
}
</div>
This all look just fine for me. Could you advise how should I fix that?
Instead of using several ViewData, I would recommend using a new ViewModel class that have a List<TagsViewModel> property and a List<ShortPostViewModel> property so you don't have to do the conversions in the view. Let's say the ViewModel is named CustomViewModel
public class CustomViewModel
{
public CustomViewModel()
{
this.ShortPosts = new List<ShortPostViewModel>();
this.Tags = new List<TagsViewModel>();
}
public List<ShortPostViewModel> ShortPosts { get; set; }
public List<TagsViewModel> Tags { get; set; }
}
then in your controller
public ActionResult Index()
{
CustomViewModel model = new CustomViewModel();
model.ShortPosts = _postRepository.GetLast20().ToList();
model.Tags = _tagsRepository.GetAll().ToList();
return View(model);
}
Make sure you have this at the top of your view code
#model CustomViewModel
You can enumerate the items of ShortPosts in your view as below
#foreach (var item in Model.ShortPosts)
and enumerate the items of Tags as below
#foreach (var tag in Model.Tags)
I have the following form:
#model Teesa.Models.SearchModel
#using (Html.BeginForm("Index", "Search", FormMethod.Get, new { id = "SearchForm" }))
{
<div class="top-menu-search-buttons-div">
#if (!MvcHtmlString.IsNullOrEmpty(Html.ValidationMessageFor(m => m.SearchText)))
{
<style type="text/css">
.top-menu-search-text
{
border: solid 1px red;
}
</style>
}
#Html.TextBoxFor(q => q.SearchText, new { #class = "top-menu-search-text", id = "SearchText", name = "SearchText" })
#Html.HiddenFor(q=>q.Page)
<input type="submit" value="search" class="top-menu-search-submit-button" />
</div>
<div id="top-menu-search-info" class="top-menu-search-info-div">
Please Select one :
<hr style="background-color: #ccc; height: 1px;" />
<div class="top-menu-search-info-checkbox-div">
#Html.CheckBoxFor(q => q.SearchInBooks, new { id = "SearchInBooks", name = "SearchInBooks" })
<label for="SearchInBooks">Books</label>
</div>
<div class="top-menu-search-info-checkbox-div">
#Html.CheckBoxFor(q => q.SearchInAuthors, new { id = "SearchInAuthors" })
<label for="SearchInAuthors">Authors</label>
</div>
<div class="top-menu-search-info-checkbox-div">
#Html.CheckBoxFor(q => q.SearchInTags, new { id = "SearchInTags" })
<label for="SearchInTags">Tags</label>
</div>
}
and the following Controller and Models :
namespace Teesa.Models
{
public class SearchModel
{
[Required(ErrorMessage = "*")]
public string SearchText { get; set; }
public bool SearchInTags { get; set; }
public bool SearchInAuthors { get; set; }
public bool SearchInBooks { get; set; }
public int Page { get; set; }
public List<SearchBookModel> Result { get; set; }
public List<SimilarBookModel> LatestBooks { get; set; }
}
public class SearchBookModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Summary { get; set; }
public List<Tags> Tags { get; set; }
public string StatusName { get; set; }
public string SubjectName { get; set; }
public string ThumbnailImagePath { get; set; }
public string BookRate { get; set; }
public string RegistrationDate { get; set; }
public int NumberOfVisit { get; set; }
}
}
[HttpGet]
public ActionResult Index(SearchModel model)
{
FillSearchModel(model);
if (ModelState.IsValid)
{
string page = model.Page;
DatabaseInteract databaseInteract = new DatabaseInteract();
model.Result = new List<SearchBookModel>();
List<Book> allBooks = databaseInteract.GetAllBooks();
List<Book> result = new List<Book>();
#region
if (model.SearchInTags)
{
var temp = (from item in allBooks
from tagItem in item.Tags
where tagItem.Name.Contains(model.SearchText)
select item).ToList();
result.AddRange(temp);
}
if (model.SearchInBooks)
{
var temp = (from item in allBooks
where item.عنوان.Contains(model.SearchText)
select item).ToList();
result.AddRange(temp);
}
if (model.SearchInAuthors)
{
var temp = (from item in allBooks
where item.Author.Contains(model.SearchText)
select item).ToList();
result.AddRange(temp);
}
#endregion
#region Paging
string itemsPerPage = databaseInteract.GetItemsPerPage();
int ItemInPage = int.Parse(itemsPerPage);
var pagingParams = Helpers.SetPagerParameters(page, ItemInPage, result);
ViewBag.AllPagesCount = pagingParams.AllPagesCount;
ViewBag.CurrentPageNumber = pagingParams.CurrentPageNumber;
ViewBag.CountOfAllItems = pagingParams.CountOfAllItems.ToMoneyFormat().ToPersianNumber();
result = pagingParams.ListData as List<Book> ?? result;
#endregion
foreach (var item in result)
{
var bookRate = (item.BookRate == null || item.BookRate.Count == 0)
? 0.0
: item.BookRate.Average(q => q.Rate);
model.Result.Add(new SearchBookModel
{
Author = item.Author,
Id = item.Id,
.
.
.
});
}
}
else
{
model.Result = new List<SearchBookModel>();
}
return View(model);
}
When I submit the form I see the following query strings(Notice the duplicate names) :
http://localhost:2817/Search?SearchText=book&Page=2&SearchInBooks=true&SearchInBooks=false&SearchInAuthors=true&SearchInAuthors=false&SearchInTags=true&SearchInTags=false
But it has to be something like this :
http://localhost:2817/Search?SearchText=book&Page=2&SearchInBooks=true&SearchInAuthors=true&SearchInTags=true
How can I fix it ?
Thanks
Html.Checkbox (and the related For... methods) generate a hidden input for false, and the checkbox for true. This is to ensure that model binding works consistently when binding.
If you must get rid of "false" items resulting from the hidden inputs, you'll need to construct the checkbox inputs yourself (using HTML and not the helper).
<input type="checkbox" id="SearchInBooks" name="SearchInBooks">
Why dont your create a Post Method with a matching name to the Get method. This will ensure that the code is much easier to debug. As you will not have a huge function to go through trying to find problems like this.
I cannot find a where your getting the duplicate url query strings from though.
This will also allow you to bind your results back to the model.
If you want the model binding to happen successfully then you have to go with this way because that is the nature of the Html.CheckBox/Html.CheckBoxFor methods they will render a hidden field as well.
I would suggest rather go with POST to make your life easy. If you still want to use GET then you have to use checkbox elements directly but you have to take care of the model binding issues. Not all the browsers returns "true" when the checkbox is checked for ex. firefox passes "on" so the default model binder throws an error.
Other alternate options is you can go for custom model binder or you can submit the form using jquery by listening to the submit event.
I hope I explain this correctly..
What I am trying to do is build up a session array with a list of products in.
Then display these on a form in text boxes with quantiles next to them and be able to submit them. I think I need to use template editor. But I don't know how to put data into the list of items.
This is how my session variable is currently being populated..
IList<EnqProduct> items2 = Session["enquiry"] as IList<EnqProduct>;
desc = desc.Replace(",", "");
EnqProduct item = new EnqProduct();
item.Id = (items2.Count + 1).ToString();
item.Product = desc;
item.Quantity = "0";
items2.Add(item);
So desc, can be productone, product two etc.
Enquiry Product model:
namespace MvcEditorTemplates.Models
{
public class EnqProduct
{
public string Id { get; set; }
public string Product { get; set; }
public string Quantity { get; set; }
}
}
Normal Enquiry Model:
public class Enquiry
{
public List<EnqProduct> EnqProduct { get; set; }
}
How i am trying to populate the model, but this is static. I need it to be populated from the array items:
var EnquiryModel = new Enquiry {
EnqProduct = items2.Select(c => new EnqProduct()
{
Quantity = c.Quantity,
Product = c.Product
})
};
Enquiry product template view:
#model MvcEditorTemplates.Models.EnqProduct
<div class="fl">
<p>
#Html.LabelFor(x => x.Product)
#Html.TextBoxFor(x => x.Product)
</p>
<p>
#Html.LabelFor(x => x.Quantity)
#Html.TextBoxFor(x => x.Quantity)
</p>
</div>
This is how im trying to get it to be displayed din the view:
#Html.EditorFor(model => model.EnqProduct)
EDIT:
at items2.Select(c => new EnqProduct()
i get a IEnumerbale error something about cast?
Try something like this:
public class ErrorMessage
{
public DateTime ErrorDate { get; set; }
public string ErrorText { get; set; }
public int DexRowId { get; set; }
}
public class Transaction
{
public string TransactionType { get; set; }
public string Processed { get; set; }
public DateTime UpdateDate { get; set; }
public int DexRowID { get; set; }
public string Text { get; set; }
}
public class Result
{
public List<ErrorMessage> errorMessageList { get; set; }
public List<Transaction> transactionList { get; set; }
}
In your controller:
List<Transaction> transactionList = ...;//query to populate your list;
List<ErrorMessage> errorMessageList = ...;//query to populate your list;
Result result = new Result();
result.ErrorMessageList = errorMessageList;
result.TransactionList = transactionList;
return View(result);
and in your view:
#model Models.Result
#{
ViewBag.Title = "Result";
Layout = "~/Views/Shared/_ResultLayout.cshtml";
}
EDIT:
#model IENumerable<MvcEditorTemplates.Models.EnqProduct>
#{
foreach( EnqProduct ep in #model)
{
.... your code comes here.........
}
}