I have the following problem: I am making an MVC intranet website for the corporation I'm working for. One part of the job is to make a phonebook - I need a tree like structure of the departments (with depth).
I have a view with two div elements - left (containing the departments, the structure follows below), and a right div which should show all the employees that are working in the selected (clicked) department.
#helper GetTree(List<PhonesClasses.Department> department, int parentID){
foreach(var i in department.Where(a=>a.headDepartmentID.Equals(parentID)))
{
{var childDepartments = department.Where(a => a.headDepartmentID.Equals(i.departmentID)).Count();
if(childDepartments > 0)
{
<li class="haschlid" id="#i.departmentID">
#i.departmentName
<ul class="sub-dep">
#GetTree(department, #i.departmentID)
</ul>
</li>
}
else
{
<li id="#i.departmentID">
#i.departmentName
</li>
}
}
}
The following is the above-mentioned view. As you can see, I had the idea to make a partial view but I'm not sure I'm headed in the right direction.
<div class ="containerStructure">
<div class="leftDivStructure">
#if (Model != null && Model.Count() > 0)
{
<ul class="list" id="deplist">
#Treeview.GetTree(Model, Model.FirstOrDefault().headDepartmentID)
</ul>
}
</div>
<div class="rightDivStructure">
Employee
#*#Html.Partial("_PeopleInDepartment", new {depID = Model.departmentID()})*#
</div>
</div>
My employee and department classes both have DepartmentID fields, so when a department is clicked on in my tree view, a parameter () should be passed to the partial view, or whatever needs to be there to handle the parameter and show the employees. Below is the controller that I think has to fetch the result.
public ActionResult PeopleInDepartment(int depID)
{
List<Person> peopleList = new List<Person>();
peopleList = Persons.GetPersons(depID);
return View(peopleList);
}
For further clarifications please comment!
Related
I have a list of elements which are displayed on a page by a for-loop. I want to get data of the selected element into an asp.net view. How can I realize that?
This is the action of the controller which returns a List of elements:
public ActionResult MainContent()
{
SiteContext db = new SiteContext();
db.SaveChanges();
var model = db.Posts.ToList();
return PartialView("MainContent", model);
}
Partial view of MainContent:
#model IEnumerable<LayersDAL.Entity.Post>
#for (int i = 0; i < Model.Count(); i++)
{
<ul>
<li>
//Here is I'm trying to send element Id of current loop iteration to CurrentPost but something goes wrong :(
<a class="post-ref" href="#Url.Action("CurrentPost", "Notes", new { Model.ToList()[i].PostId })">
<h2>#Html.DisplayFor(modelItem => Model.ToList()[i].Title)</h2>
<p>
</p>
</a>
</li>
</ul>
}
CurrentPost action:
public ActionResult CurrentPost(int? id)
{
SiteContext db = new SiteContext();
var post = db.Posts.FirstOrDefault(p => p.PostId == id);
return PartialView(post);
}
Partial view of CurrentPost:
<div class="modal-post">
<div class="modal-post-divs">
<span class="close">×</span>
</div>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div>
<label>#Model.Title</label>
</div>
</div>
List of elements in Browser
Error picture
So, what can I do? :)
UPD:
Thanks guys, I resolved that problem :)
Change your link code to the following:
<a class="post-ref" href="#Url.Action("CurrentPost", "Notes", new { id = Model[i].PostId })">
<h2>#Html.DisplayFor(modelItem => Model[i].Title)</h2>
<p>
</p>
</a>
You've already converted it to a List in your controller, so you don't need to call ToList() on the model in the View (and especially not inside a for loop!). Also make sure that you name the parameter that you're passing to the CurrentPost() method.
In the CurrentPost() method itself you should double check that the id parameter is valid and not null as follows:
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
FirstOrDefault() returns null if no item found so your model will be null.
public ActionResult CurrentPost(int? id)
{
SiteContext db = new SiteContext();
var post = db.Posts.FirstOrDefault(p => p.PostId == id);
return PartialView(post);
}
you can check it on your view to avoid erros.
#if(Model != null)
{
// your code.
}
else
{
<p>No item found </p>
}
I'm using MVC where I have a list of strings that I would like to point to a new page. I'm using Razor and am very new to MVC and cannot seem to find the answer to my question through google.
My list could contain of the following:
"hello"
"goodbye"
"seeya"
I know how to insert strings from the controller to the html page using ViewBag, and I would use the following actionlink if I had a fixed set of strings:
#Html.ActionLink("viewedName", "ChemicalClass", new { mystring = "hello" })
#Html.ActionLink("viewedName", "ChemicalClass", new { mystring = "goodbye" })
#Html.ActionLink("viewedName", "ChemicalClass", new { mystring = "seeya" })
As I understand it, this would generate 3 links that would redirect to the subpage "ChemicalClass", and it would contain the one of the 3 parameters, depending on the link that was clicked.
My quesiton is, how can I do the same, but have the ActionLinks created dynamically, since I won't know how many links are going to be created, nor the content of the strings. My goal is to show these links on the webpage in (preferabely) a list form, e.g.:
<ol>
<li>
hello
</li>
<li>
goodbye
</li>
<li>
seeya
</li>
</ol>
Where each element in the list is a link and not just a string.
Create a view model that stores a collection of links
Model
public class ViewModel
{
public IList<string> Links { get; set; }
}
populate that model in your controller
Controller
public ActionResult Index()
{
var model = new ViewModel
{
Links = new List<string>
{
"Hello",
"Goodbye",
"Seeya"
}
};
return View(model);
}
and finally your view
View
#model MvcApplication1.Models.ViewModel
<ol>
#foreach (var item in Model.Links)
{
<li>
#Html.ActionLink("viewedName", "ChemicalClass", new { mystring = item })
</li>
}
</ol>
Your class holds your collection of strings and razor loops over them to produce your links.
you can use something like this.
<ul>
#foreach (var x in Model)
{
<li>#x.myString </li>
}
</ul>
I have something like this in one of my Views:
#foreach (var item in Model.MyList)
{
<li>
#Html.ActionLink("Select", "ActionName", "Chemical", new {id = item.AdmNum}, new { #class = "label label-info"})
</li>
}
I am using MVC + EF
I have a Feed xml file url that gets updated every 7 minute with items, every time a new item gets added I retrieve all the items to a list variable and then I add these varible to my database table. After that I fill a new list variable which is my ViewModel from the database table. Then I declare the ViewModel inside my view which is a .cshtml file and loop throught all of the objects and display them.
How can I make sure that the newest items get placed on the top and not in the bottom?
This is how I display the items inside my cshtml note that I use a ++number so the newest item needs to be 1 and so on.. :
#model Project.Viewmodel.ItemViewModel
#{
int number = 0;
}
}
<div id="news-container">
#foreach (var item in Model.NewsList)
{
<div class="grid">
<div class="number">
<p class="number-data">#(++number)</p>
</div>
<p class="news-title">#(item.Title)</p>
<div class="item-content">
<div class="imgholder">
<img src="#item.Imageurl" />
<p class="news-description">#(item.Description) <br />#(item.PubDate) | Source</p>
</div>
</div>
</div>
}
</div>
This is how I fill the viewmodel which I use inside the .cshtml file to iterate throught and display the items
private void FillProductToModel(ItemViewModel model, News news)
{
var productViewModel = new NewsViewModel
{
Description = news.Description,
NewsId = news.Id,
Title = news.Title,
link = news.Link,
Imageurl = news.Image,
PubDate = news.Date,
};
model.NewsList.Add(productViewModel);
}
Sorry for the paint lol :P
Any kind of help is appreciated
#foreach (var item in Model.NewsList.OrderByDescending(n => n.PubDate)
What's better?
1) If i make 3 ViewBag on server and then Render my View using this ViewBags?
Server
ViewBag.LeftColumnTales = tales.Where((x, i) => i % 3 == 0);
ViewBag.CenterColumnTales = tales.Where((x, i) => i % 3 == 1);
ViewBag.RightColumnTales = tales.Where((x, i) => i % 3 == 2);
View
<div id="left_column">
#foreach (var t in ViewBag.LeftColumnTales)
{
<div class="item">
#t.NameAn <span>(#(new HtmlString(Html.TimeForReadingHtmlResult((int)t.TimeForReading))))</span>
#(new HtmlString(Html.PeopleTaleVoterHtmlResult((int)t.Analit)))
</div>
}
<!--end of div.item-->
</div>
or
2) If i set ViewBag.tales on server and then on View will make Converting from dynamic data to IEnumerable and devide it to 3 columns?
Server
ViewBag.Tales = tales;
View
<div id="left_column">
#foreach (var t in ((IEnumerable<MVCFairyTales3.Models.AuthorTale>)ViewBag.Tales).Where((x, i) => i % 3 == 0))
{
<div class="item">
#t.NameAn <span>(#(new HtmlString(Html.TimeForReadingHtmlResult((int)t.TimeForReading))))</span>
#(new HtmlString(Html.AuthorTaleVoterHtmlResult((int)t.Analit)))
</div>
}
<!--end of div.item-->
</div>
What's better?
To be honest I don't like any of those two. They both use ViewBag and weak typing. I am sorry but personally I get sick when I see ViewBag/ViewData.
Personally I like using view models and strongly typed views:
public class MyViewModel
{
public IEnumerable<Tale> LeftColumnTales { get; set; }
public IEnumerable<Tale> CenterColumnTales { get; set; }
public IEnumerable<Tale> RightColumnTales { get; set; }
}
which you could populate in your action:
public ActionResult Foo()
{
var model = new MyViewModel
{
LeftColumnTales = tales.Where((x, i) => i % 3 == 0),
CenterColumnTales = tales.Where((x, i) => i % 3 == 1),
RightColumnTales = tales.Where((x, i) => i % 3 == 2),
};
return View(model);
}
and in the strongly typed view:
#model MyViewModel
<div id="left_column">
#Html.DisplayFor(x => x.LeftColumnTales)
</div>
and in the corresponding display template which will be rendered for each element of the collection (~/Views/Shared/DisplayTemplates/Tale.cshtml):
#model Tale
<div class="item">
#Html.ActionLink(
Model.NameAn,
Model.RouteNameAn,
Model.AuthorTalesCategory.RouteNameAn
)
<span>
#Html.Raw(Html.TimeForReadingHtmlResult((int)Model.TimeForReading))
</span>
#Html.Raw(Html.AuthorTaleVoterHtmlResult((int)Model.Analit))
</div>
And even better if you have to repeat this all over your pages is to put it in the _Layout using child actions as explained by Phil Haack in his blog post.
I'm not sure how you app is structured/layered, but in general I like to separate out my business/domain logic from my UI code. It sounds like whether it should be in the left/center/right column is strictly UI logic. In that case I would have my business layer retrieve/create the list of all tales, then leave it up to the UI layer to split it up however it is to be displayed.
When I'm trying to figure out if some logic belongs in the UI layer or not, I imagine that I am going to create many many UI's (web, windows, mobile phone, sharepoint webpart, etc) and ask myself if it's likely that logic (e.g. split it into 3 columns) will change between UI's. If the answer is yes, then that logic belongs in the UI layer.
This maybe very simple but I cant seem to sort it out on my own.
I have created a simple db and entity modal that looks like this
I am trying to create an Create form that allows me to add a new Order. I have a total of 3 tables so what I am trying to do is have the form allowing the person to enter Order date and also has a dropdown list that allows me to select a product from the product table
I want to be able to create a Add or Edit view that allow me to insert the OrderDate into the OrderTable and also insert the OrderID and selected ProductID into OrderProduct.
What steps do I need to do here.
I have created an OrderController and ticked the "Add Actions" and than added a Create View which looks like this
#model Test.OrderProduct
#{
ViewBag.Title = "Create2";
}
<h2>Create2</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>OrderProduct</legend>
<div class="editor-label">
#Html.LabelFor(model => model.OrderID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.OrderID)
#Html.ValidationMessageFor(model => model.OrderID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ProductID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProductID)
#Html.ValidationMessageFor(model => model.ProductID)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
This creates the view that contains a textbox for both OrderID and ProductID however no date.
My controller CreatePost hasnt been changed
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
var data = collection;
// TODO: Add insert logic here
// db.Orders.AddObject(collection);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
My questions are,
1.How do I swap out ProductID textbox to be a dropdown which is populated from Product
2.How do I get the data from FormCollection collection? I thought of just a foreach however I dont know how to get the strongly typed name
Any help for a newbie would be very helpful.
Thank you!
First thing's first, don't bind to the Order entity. Never bind to an EF object, always try and use a ViewModel. Makes life simpler for the View, and that is the goal here.
So, have a ViewModel like this:
public class CreateOrderViewModel
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public int SelectedProductId { get; set; }
public IEnumerable<SelectListItem> Products { get; set; }
}
That's it right now.
Return that to your View in your [HttpGet] controller action:
[HttpGet]
public ActionResult Create()
{
var model = new CreateOrderViewModel
{
Products = db.Products
.ToList() // this will fire a query, basically SELECT * FROM Products
.Select(x => new SelectListItem
{
Text = x.ProductName,
Value = x.ProductId
});
};
return View(model);
}
Then to render out the list of Products: (basic HTML excluded)
#model WebApplication.Models.CreateOrderViewModel
#Html.DropDownListFor(model => model.SelectedProductId, Model.Products)
The only thing i don't know how to do is bind to the DateTime field. I'm guessing you would need an extension method (HTML Helper) which renders out a Date Picker or something. For this View (creating a new order), just default to DateTime.Now.
Now, onto the [HttpPost] controller action:
[HttpPost]
public ActionResult Create(CreateOrderViewModel model)
{
try
{
// TODO: this manual stitching should be replaced with AutoMapper
var newOrder = new Order
{
OrderDate = DateTime.Now,
OrderProduct = new OrderProduct
{
ProductId = SelectedProductId
}
};
db.Orders.AddObject(newOrder);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Now, i also think your EF model needs work.
To me (in English terms), a Product can have many orders, and an Order can have many Products.
So, it should be a many-to-many. Currently it's a 1-1 with a redundant join table. Did you generate that from a DB? If so, your DB possibly needs work.
You should have a navigational property called Products on the Order entity, which references a collection of Product, made possible by a silent join to the join table in the many-to-many.
This also means you no longer have a DropDownList, but a MultiSelectDropDownList.
Thanks Craig. Your few days (as at time of posting) of MVC have solved my few days of trying to get the selected value back from DropDownListFor.
I had no problem in the Create view in getting the selected value of the DDLF, but the Edit view was a completely different matter - nothing I tried would get the selected value back in the Post. I noticed the selected value was lurking in the AttemptedValue of the ModelState, and so Dr.Google referred me here.
I had this in my view
#Html.DropDownList(model => model.ContentKeyID, Model.ContentKeys, Model.ContentKeyName)
where ContentKeys is a SelectList populated from the DB via a ViewModel, and ContentKeyName is the curently selected name.
The wierd thing is, I have another DDL on the view populated in an identical manner. This one works. Why, I don't know. It is the second DDL on the form, but I can't see that making a difference.
I read somewhere else it might have been that I was using Guids as the Id, but that didn't seem to make a difference - I changed to Int32, but don't think I had to - I think it's enums that disagree with DDLF. Nullables seemd to make no difference either.
Now that I've added the form collection to my Post ActionResult, and get the selected value using
-view
#Html.DropDownList("ContentKey", Model.ContentKeys)
-in controller (Post)
contentKeyId = int.Parse(form.GetValue("ContentKey").AttemptedValue);
all is good, and I can get on with more exciting things. Why is that the simplest things can hold you up for so long?
I have been struggling with this over the last day or so too. I'll share my limited knowledge in the hope that it will help someone else.
Note that I use a singleton with a pre-populated list to keep my example application small (i.e. no EF or DB interaction).
To show the ProductList you will need to have a ViewBag or ViewData item which contains the ProductList.
You can set this up in the Controller with something like
ViewData["ProductList"] = new SelectList(Models.MVCProduct.Instance.ProductList, "Id", "Name", 1);
Then update your View to something like:
<div class="editor-field">#Html.DropDownList("ProductList")</div>
For the Post/Create/Update step you need to look into the FormCollection to get your results. Reading other posts it sounds like there used to be a bug in here, but it's fixed now so ensure you have the latest. For my example I have a DropDownList for Product so I just get the selected Id and then go searching for that Product in my list.
[HttpPost]
public ActionResult Create(FormCollection form )//Models.MVCOrder newOrder)
{
MVC.Models.MVCOrder ord = Models.MVCOrder.Instance.CreateBlankOrder();
//Update order with simple types (e.g. Quantity)
if (TryUpdateModel<MVC.Models.MVCOrder>(ord, form.ToValueProvider()))
{
ord.Product = Models.MVCProduct.Instance.ProductList.Find(p => p.Id == int.Parse(form.GetValue("ProductList").AttemptedValue));
ord.Attribute = Models.MVCAttribute.Instance.AttributeList.Find(a => a.Id == int.Parse(form.GetValue("attributeId").AttemptedValue));
UpdateModel(ord);
return RedirectToAction("Index");
}
else
{
return View(ord);
}
}
I've only been working on MVC3 for the last few days, so any advice on improving the above would be appreciated.