I have the following in a controller :
outputmodel.Add(new SP_RESULTS.RS_Plans()
{
id = Convert.ToDecimal(SPOutput["id"]),
name = Convert.ToString(SPOutput["name"]),
code = Convert.ToString(SPOutput["code"]),
from = Convert.ToDateTime(SPOutput["from"]),
to = Convert.ToDateTime(SPOutput["to"]),
days = Convert.ToDecimal(SPOutput["days"]),
type_id = convert.YoString(SPOutput["type_id"]),
package = Convert.ToString(SPOutput["package"]),
day = Convert.ToDecimal(SPOutput["day"]),
charge = SPOutput["charge"] as decimal?,
type = Convert.ToString(SPOutput["type"]),
percentage= SPOutput["percentage"] as decimal?,
taxes = Convert.ToDecimal(SPOutput["taxes"]),
order = Convert.ToDecimal(SPOutput["order"]),
level = SPOutput["level"] as decimal?,
Column15 = Convert.ToDecimal(SPOutput[15]),
type_order = (SPOutput["type_order"]) as decimal?,
adults = SPOutput["adults"] as decimal?,
});
var order = outputmodel.OrderBy(c => c.from);
ViewData["RS_Output"] = order;
grabbing output from an MS SQL stored procedure and storing in a viewdata (ordered by the FROM date).
My HTML has the following line to start to build the table
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
//basic <tr> <td> </td> </tr> table setup, using #item.variablename to pull info from the viewdata.
}
The output I am trying to achieve is for every TYPE under CODE, where the from date => current date, list the room type /package name etc.
and the output I am getting is
what I am trying to get is
What I think I need is a foreach after the current foreach, but I cannot for the life of me figure it out in my head.
I've changed the
var order line in my controller to now read
var order = outputmodel.OrderBy(c => c.rate);
..and I've put the HTML table create code in an if loop
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
if (item.to >= DateTime.now)
{
//basic <tr> <td> </td> </tr> table setup, using #item.variablename to pull info from the viewdata.
}
}
.. but, as I say, I am stumped.
I think I need another foreach within the newly created if loop, but I cannot figure out how.
#foreach (var item in ViewData["RS_Output"] as Enumerable<app.Models.SP_RESULTS.RS_Plans>)
{
if (item.to >= DateTime.now)
{
//other table headers/data
<tr>
<td>
#item.type
</td>
</tr>
<tr>
<td>
Room Type
</td>
<td>
Package / Service
</td>
<td>
Availablility
</td>
<td>
Charge
</td>
<td>
PAX
</td>
<td>
Level
</td>
</tr>
<tr>
==> #foreach (subitem = item.type)
==> {
==> foreach (item.type)
==> {
<td>
#item.type_id
</td>
<td>
#item.package
</td>
<td>
#item.Column15
</td>
<td>
#item.charge
</td>
<td>
#item.adults
</td>
<td>
#item.level
</td>
==> }
==> }
</tr>
}
}
can someone please advise?
thanks
UPDATE:
Hi, what I found worked was, if I create a variable called string previous_type =" " , and another called decimal previous_id =0 , then, in the view, I can amend with
if (item.to >= item.checkdate)
{
if ((previous_id != item.id) && (previous_type != item.type.ToString()) )
{
//some more code
if (item.type.ToString().Equals(previous_type) == false)
{
previous_type = item.type.ToString();
previous_date_from = item.date_from;
}
//etc
}
Thanks everyone for their help
OK, I think what you want is to first group the data, then show a table which then shows 'sub-tables' for each type of accomodation?
if so, then yes you can do this with nested foreach loops, but you'd still be better off strongly typing your view and doing the grouping stuff in the controller (or possibly better in some sort of service layer so it can be more easily tested/re-used)... but to get you started, something like this:
Models:
//Raw data
public class DataRowModel
{
public int Id { get; set; }
public string Class{ get;set;}
public string Description { get; set; }
public DateTime BookingDate { get; set; }
}
//Grouped data
public class GroupedDataRowModel
{
public string Class { get; set; }
public IEnumerable<DataRowModel> Rows { get; set; }
}
//View model
public class DataRowsViewModel
{
public IEnumerable<GroupedDataRowModel> Results { get; set; }
}
Controller Action:
public ActionResult TestData()
{
var PretendDatabaseCall = new List<DataRowModel>
{
new DataRowModel{
Id =1,
BookingDate =new DateTime(2017,1,1),
Description ="Booking 1",
Class="Room"
},
new DataRowModel{
Id =2,
BookingDate =new DateTime(2017,2,1),
Description ="Booking 2",
Class="Room"
},
new DataRowModel{
Id =3,
BookingDate =new DateTime(2017,3,1),
Description ="Booking 3",
Class="Suite"
},
new DataRowModel{
Id =4,
BookingDate =new DateTime(2017,4,1),
Description ="Booking 4",
Class="Room"
},
};
//We can now get the data from the database. We want to group by class so we can
//get a summary of items by class rather than a big flat list. Most LINQ to SQL implementations
//(e.g. Entity Framework) when working with Raw entities could convert this to SQL so the SQL server
//does the grouping, but if not it can happen in memory (get all records, then standard LINQ does it on
//the complete list)
var dataGroupedByClass = PretendDatabaseCall
//Minor Edit: apply filtering here not in the view!
.Where(x=>x.BookingDate >= Datetime.Now)
//Group by class.
.GroupBy(x => x.Class)
//for each class, get the records.
.Select(grpItem => new GroupedDataRowModel()
{
//'key' is the thing grouped by (class)
Class = grpItem.Key,
//grpItem has all the rows within it accessible still.
Rows = grpItem.Select(thisRow => thisRow)
});
var model = new DataRowsViewModel
{
Results = dataGroupedByClass
};
return View("~/Views/Home/TestData.cshtml", model);
}
And View:
#* Strongly typed view. saves any casting back and forth.*#
#model SimpleWeb.Models.DataRowsViewModel
#{
ViewBag.Title = "TestData";
}
<h2>TestData</h2>
<table>
<thead></thead>
<tbody>
#foreach (var groupEntry in Model.Results)
{
#*Add single row with just the class...*#
<tr><td>#groupEntry.Class</td></tr>
#*Header row for each class of booking*#
<tr>
<td>Id</td>
<td>Description</td>
<td>Date</td>
</tr>
foreach (var row in groupEntry.Rows)
{
#*add new row for each actual row*#
<tr>
<td>
#row.Id
</td>
<td>
#row.Description
</td>
<td>
#row.BookingDate
</td>
</tr>
}
}
</tbody>
</table>
This produces Data like I think you want:
Room
Id Description Date
1 Booking 1 01/01/2017 00:00:00
2 Booking 2 01/02/2017 00:00:00
4 Booking 4 01/04/2017 00:00:00
Suite
Id Description Date
3 Booking 3 01/03/2017 00:00:00
Obviously you want the 'Room' and 'Suite' parts to contain more information, but this should hopefully help get you started?
Related
I have this list so far:
List<object> lista = new List<object>();
foreach (var item in group)
{
lista.Add(new
{
ver = item.FirstOrDefault().vereda.DESCRIPCION,
prod = item.Count()
});
}
ViewBag.veredasEncu = lista;
i have to send that data to a view in order to build a table:
<tbody>
#{
if (ViewBag.veredasEncu != null)
{
List<object> lis = ViewBag.veredasEncu;
for (int i = 0; i < lis.Count(); i++)
{
<tr>
<td></td>
<td></td>
</tr>
}
}
}
</tbody>
I cant get to place the info on the <td> tags cose every item in the foreach iteration throws something this:
item = {ver = "Loma", prod = 5}
how can i make that 2 values look like an array, or is there a way to separate them in order to place them in the correct tag?
I solved the issue by creating a class:
public class dataTab
{
public string ver { get; set; }
public int prod { get; set; }
}
then is just change the loop for this:
lista2.Add(new dataTab
{
ver = item.FirstOrDefault().vereda.DESCRIPCION,
prod = item.Count()
});
so it could be post in the HTML like this:
foreach (var item in ViewBag.veredasEncu)
{
<tr>
<td>#item.ver</td>
<td>#item.prod</td>
</tr>
}
However im not sure that creating a class for every type of list that i need to create is a good idea. IF someone have an idea on how to fill the list in a more generic way please respond this.
I want to get list select list data from controller to view as I have a list in controller like:
public static List<SelectListItem> Survey()
{
// List<string> QuestionType = new List<string> { "SingleLineTextBox", "MultiLineTextBox", "YesOrNo", "SingleSelect", "MultiSelect" };
List<SelectListItem> QuestionType = new List<SelectListItem>()
{
new SelectListItem() { Text = "SingleLineTextBox",
Value = "SingleLineTextBox"},
new SelectListItem() { Text = "MultiLineTextBox",
Value = "MultiLineTextBox"},
new SelectListItem() { Text = "YesOrNo", Value = "YesOrNo" },
new SelectListItem() { Text = "SingleSelect", Value = "SingleSelect"},
new SelectListItem() { Text = "MultiSelect", Value = "MultiSelect"}
};
return QuestionType;
}
I have written my view as under I have a dropdown in view in which i want to add this list item,
#model Survey.Question
#{
ViewBag.Title = "ManageQuestion";
}
<fieldset>
<legend> Enter Questions For Survey</legend>
<table>
<tr>
<td>
#Html.Label("Question Type")
</td>
<td>
#Html.DropDownFor("QuestionType")
</td>
</tr>
<tr>
<td>
#Html.Label("Title");
</td>
<td>
#Html.TextBoxFor(a=>a.Text)
</td>
</tr>
<tr>
<td>
#Html.Label("Description")
</td>
<td>
</td>
</tr>
<tr>
<td>
#Html.Label("Value")
</td>
<td>
#Html.TextAreaFor(a=>a.Options)
</td>
</tr>
</table>
</fieldset>
#Html.DropDownFor("QuestionType") is a dropdown in which I want to add list items; Survey.Question is table Question in database Survey. I can write it like this to get list item in view but I have added table in model as I need data from table:
#model List<SelectListItem>
How can I figure it out in order to get list from controller to view?
Hoping for your suggestion
There are many ways around this one being said b D-Shih as you send the list via a ViewBag/ViewData and such but i prefer you send it via a ViewModel.
So Question Model is what you have bound to the view. you can easily add the following properties to your model:
Public Class Question
{
//Other Properties
public string SelectedItemId { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
While you are filling the Items within the Question Model you may later want to take the SelectedItemId so you add it there as well (my suggestion is that you create a ViewModel for this one).
Then in your controller:
Question q = new Question();
q.Items = Survey(); //this is your method that returns SelectListItem, or you can create it here.
return view(q);
Then Within your view:
#Html.DropDownListFor(x => x.SelectedItemId , Model.Items , "-- Select Type --")
I have little success in getting one method for getting list on view from controller that is,
I returned the list from view like,
List<SelectListItem> QuestionType = Survey();
return View(QuestionType);
Use tuple to add more than one value to model ,
#model Tuple<Survey.Question,List<SelectListItem>>
Get the value for textbox from it like,
#Html.TextAreaFor(model=>Model.Item1.Options)
But getting type error while getting value for dropdown,
#Html.DropDownListFor("QuestionType", Model.Item2)
Hopefully some syntax error any solution ?
SOLVED:
#{
List<SelectListItem> QuestionType = new List<SelectListItem>();
QuestionType.Add(new SelectListItem
{
Text = "SingleLineTextBox",
Value = "SingleLineTextBox"
});
QuestionType.Add(new SelectListItem
{
Text = "MultiLineTextBox",
Value = "MultiLineTextBox",
Selected = true
});
QuestionType.Add(new SelectListItem
{
Text = "YesOrNo",
Value = "YesOrNo"
});
QuestionType.Add(new SelectListItem
{
Text = "SingleSelect",
Value = "SingleSelect"
});
QuestionType.Add(new SelectListItem
{
Text = "MultiSelect",
Value = "MultiSelect"
});
}
#Html.DropDownList("DDlDemo", new SelectList(QuestionType, "Value", "Text"))
I have a model with 34 numbered properties in it as shown below
Public Class ViewModel
{
public string RatingCategory01 { get; set; }
public string RatingCategory02 { get; set; }
public string RatingCategory03 { get; set; }
//...and so on until category #34
}
Rather than code an input for each category in Razor Pages, I would like to use a loop to iterate through all the categories and generate the appropriate control groups. I have tried the code below:
<tbody>
#for (var i = 1; i < 35; i++)
{
string n;
#if (i > 0 && i < 10)
{
n = "RatingCategory0" + i.ToString();
}
else
{
n = "RatingCateogry" + i.ToString();
}
<tr>
<td>
<label asp-for="#string.Format("RatingCategory" + n)" class="control-label"></label>
</td>
<td>
<select asp-for="#string.Format("RatingCategory" + n)" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</td>
<td>
<input asp-for="#string.Format("RemedialTime" + n)" class="form-control" />
</td>
</tr>
}
</tbody>
When I build the project and navigate to the page, I get this error:
InvalidOperationException: Templates can be used only with field
access, property access, single-dimension array index, or
single-parameter custom indexer expressions.
I'm not sure if I am on the right track here. I would really like to create a loop to generate these inputs so make future maintenance and changes easier. It's probably pretty obvious from my code/question that I am pretty new to this, so any help is appreciated.
EDIT TO ADD SOLUTION:
I used the solution provided by Ed Plunkett which I have checked below. I altered it a bit and ended up creating a new class called 'Rating' because I found that in practice I needed a more complex object. Inside my view is now
public List<Rating> Ratings = { get; set; }
In the controller, I use a loop to add as many empty ratings as I need to the list depending on the number I need.
for (var i = 0; i < 34; i++)
{
vm.Ratings.Add(new Rating());
}
Though this will likely be updated to use something other than a hard-coded number as the application evolves.
Finally, I used a loop in the view to create a group of controls for every Rating in my List. In this case it is a TableRow containing different controls in different columns:
#for (var i = 0; i < Model.Ratings.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.Ratings[i].Category)
</td>
<td>
<div class="form-group">
<select asp-for="Ratings[i].RatingValue" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</div>
</td>
<td>
<input asp-for="Ratings[i].RemediationMinutes" class="form-control" />
</td>
</tr>
}
I've found that the data in this group of inputs can be bound as a List by simply including
List<Rating> Ratings
in the parameters on whichever method runs when the form is submitted.
This is what you want instead of those 34 properties and their implied 34 RemedialTime siblings:
public List<String> RatingCategory { get; set; } = new List<String>();
public List<String> RemedialTime { get; set; } = new List<String>();
If you have 34 of something and the names differ only by an index number, that's a collection, not 34 distinct properties with sequentially numbered names. Then you can enumerate the 34 items with a foreach loop, or index them individually as RatingCategory[0] through RatingCategory[33]. In C#, collection indexes start at zero, so the first one is 0 and the thirty-fourth one is 33. You get used to it.
You should also look up what String.Format() does. String.Format("Foo" + 1) is exactly the same as "Foo" + 1.
You could convert your model class to dictionary;
var viewModel = new ViewModel()
{
RatingCategory01 = "a",
RatingCategory02 = "b",
RatingCategory03 = "c"
};
var dictionaryModel = viewModel.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(prop => prop.Name, prop => prop.GetValue(viewModel, null));
Then you can iterate the dictionary in the view.
I have a table of fees I am trying to parse through to return data, but it is returning a few blanks before it actually returning the string of data.
<table id="Fees">
<thead>
<tr>
<th>Rate Code</th>
<th>Description</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td class="code">A1</td>
<td>Charge Type 1</td>
<td class="amount">$11.20</td>
</tr>
<tr>
<td class="code">C2</td>
<td>Charge Type 2</td>
<td class="amount">$36.00</td>
</tr>
<tr>
<td class="code">CMI</td>
<td>Cuba Medical Insurance</td>
<td class="amount">$25.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">Total:</td>
<td class="amount">$145.16</td>
</tr>
</tfoot>
</table>
I return by xpath
private By lst_Fee
{
get { return By.XPath("//*[#id=\"Fees\"]/tbody/tr"); }
}
Selenium code:
IList<IWebElement> fees = GetNativeElements(lst_Fee, 5);
List<string> actual = new List<string>();
foreach (IWebElement elem in fees)
{
actual.Add(GetText(elem, ControlType.Label));
}
Questions
Is ControlType.Label correct for a table? I am getting a few blank elems before actually getting to the data.
If I wanted to separate each Rate, Description and Fee out in each item to make sure the cost adds up to Total correctly, how can I do that?
I would do something like the below. I created a class Fee that holds the parts of a fee: the code, description, and amount. For each table row , you would extract these three values and store them in an instance of the Fee class. The function returns a collection of Fee instances. To get the sum of the fees themselves, you would call the GetFees() method and then iterate through the Fee instances summing the amount into the final Total.
public class Fee
{
private String code;
private String desc;
private BigDecimal amount;
private Fee(String _code, String _desc, BigDecimal _amount)
{
this.code = _code;
this.desc = _desc;
this.amount = _amount;
}
}
public List<Fee> GetFees()
{
List<Fee> fees = new ArrayList<Fee>();
List<WebElement> rows = driver.findElements(By.cssSelector("#Fees > tbody > tr"));
for (WebElement row : rows)
{
List<WebElement> cells = row.findElements(By.cssSelector("td"));
fees.add(new Fee(cells.get(0).getText(), cells.get(1).getText(), parse(cells.get(2).getText(), Locale.US)));
}
return fees;
}
// borrowed from http://stackoverflow.com/a/23991368/2386774
public BigDecimal parse(final String amount, final Locale locale) throws ParseException
{
final NumberFormat format = NumberFormat.getNumberInstance(locale);
if (format instanceof DecimalFormat)
{
((DecimalFormat) format).setParseBigDecimal(true);
}
return (BigDecimal) format.parse(amount.replaceAll("[^\\d.,]", ""));
}
You can grab all the column headers and as well the row data by the below code:
Happy coding =
//// Grab the table
IWebElement grid;
grid = _browserInstance.Driver.FindElement(By.Id("tblVoucherLookUp"));
IWebElement headercolumns = grid.FindElement(By.Id("tblVoucherLookUp"));
_browserInstance.Driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(75));
_browserInstance.ScreenCapture("Voucher LookUp Grid");
//// get the column headers
char[] character = "\r\n".ToCharArray();
string[] Split = headercolumns.Text.Split(character);
for (int i = 0; i < Split.Length; i++)
{
if (Split[i] != "")
{
_log.LogEntry("INFO", "Voucher data", true,
Split + " Text matches the expected:" + Split[i]);
}
}
I'm new to C# MVC so please be patient. I'm having some trouble displaying output for the ViewBag. The application is a Fantasy Football webpage so I have three tables (right now, more to come) one for the basic player info (dbo.Player), one for player background (dbo.PlayerBackground), and one is just a definition table for the team (dbo.Team).
In one of my pages I have a name search and search by position and want to return information across these three tables.
public ActionResult Index()
{
var players = (from p in db.Players
join pb in db.PlayerBackgrounds on p.playerId equals pb.playerID
join t in db.Teams on p.teamAbbre equals t.teamAbbre
select new { playerID = p.playerId, playerName = p.name, position = p.position,
height = pb.height, weight = pb.weight, college = pb.college, dob = pb.dob,
imageUrl = pb.imageUrl, years = pb.years,
teamName = t.name
}).ToList();
ViewBag.data = players;
return View();
}
The query works fine but in index.cshtml I keep getting errors.
#foreach (var player in ViewBag.data)
{
<tr class="success ui-dragable playerRow" style="display: none;">
<td>
<input type="checkbox" />
</td>
<td>
#Html.ActionLink( (string)player.playerName, "Details", new { id = player.playerID }, new { #class = "detailsLink" })
</td>
<td>
#player.teamName
</td>
<td>
#player.position
</td>....
From the research I've done, this seems like it should work. I've tried it both with and without the (string) cast. Without the cast it gives me red squiggly saying I should cast and when I do I get:
Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a definition for 'playerName'
As I step through, I can watch player and it has the various properties just as it should. Any idea what I'm doing wrong?
The problem is that you are using an anonymous object. I can't really take the credit for that finding though, the answer I found was right here (Go give him an up-vote). Refer to it for complete details.
Essentially, the short of the story is that Anonymous objects are emitted as internal by the compiler. This causes problems when trying to use them as Razor views because they are compiled into a separate assembly by the ASP.Net runtime (internal only allows access in the same assembly).
So, the solution is to define a view model:
public class PlayerViewModel
{
// Replace with the actual type of playerId
public int playerId { get; set; }
// etc...
}
And use it in your controller:
public ActionResult Index()
{
var players = (from p in db.Players
join pb in db.PlayerBackgrounds on p.playerId equals pb.playerID
join t in db.Teams on p.teamAbbre equals t.teamAbbre
select new PlayerViewModel { playerID = p.playerId, ... }).ToList();
return View(players); // Use the strongly-typed model property for your view
// instead of ViewBag.data (It's recommended)
}
And finally in your view:
#* At the beginning of your view *#
#model IEnumerable<PlayerViewModel>
...
#foreach (var player in #Model)
{
<tr class="success ui-dragable playerRow" style="display: none;">
<td>
<input type="checkbox" />
</td>
<td>
#Html.ActionLink(player.playerName, "Details", new { id = player.playerID }, new { #class = "detailsLink" })
</td>
<td>
#player.teamName
</td>
<td>
#player.position
</td>....