I have two classes: Store and Machine.
Right now I have constructed a view where I show the Stores associated to the logged user:
public async Task<IActionResult> Index()
{
var Tiendas = await _context.Stores.Where(t => t.Usuario == User.Identity.Name).Include(t => t.Machines).ToListAsync();
LiqIndexData Liquid = new LiqIndexData()
{
StoreL = Tiendas,
};
return View(Liquid);
}
In this code I also added the Machines asociated to each Store.
The View:
In my View I would like to present, for each Store, all of the Machinesregistered. For this I'm using nav-tabs
Nav Tab based on the number of Stores
<ul class="nav nav-pills">
#foreach (var item in Model.StoreL)
{
<li>#item.StoreName</li>
}
</ul>
<div class="tab-content">
#foreach (var item in Model.StoreL)
{
<div class="tab-pane fade in active" id="#item.StoreID"></div>
}
The Problem:
Info shown in the body of each Nav-tab:
I'm trying to access the information of each Machine associated with each Store. For this I'm trying to use:
#foreach (var item in Model.StoreL)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Machines
.Where(m=>m.StoreID==item.StoreID))
</td>
But I don't know how to access the property. If I try:
#Html.DisplayFor(modelItem => item.Machines
.Where(m=>m.StoreID==item.StoreID).PropertyXYZ)
I get:
'IEnumerable' does not contain a definition for 'PropertyXYZ' and no extension method 'PropertyXYZ' accepting a first argument of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?)
Any advice?
As the error is trying to tell you, your collection of Machines has no PropertyXYZ.
Depending on what you actually want to do, you can either use First() to get only one Machine (and no others), or use a loop to go through all of them.
Related
After running the 7.5.11 upgrade on our Umbraco web site I am getting an exception in one of my Partial View Macro Files (cshtml):
System.NullReferenceException: Object reference not set to an instance of an object.
at Umbraco.Web.PublishedContentExtensions.GetPropertyValue(IPublishedContent content, String alias)
The exception is pointing to this line:
var mediaItem = Umbraco.TypedMedia(item.GetPropertyValue("propAliasString"));
The line is in this foreach loop:
#foreach (var item in news.Where(x => x.GetPropertyValue<int>("group") == year.Key).OrderByDescending(y => y.GetPropertyValue("date")))
{
var mediaItem = Umbraco.TypedMedia(item.GetPropertyValue("announcement"));
<div class="panel-body">
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> #item.GetPropertyValue("headline")
<p style="margin-left:16px;" class="date">#( Convert.ToDateTime(item.GetPropertyValue<string>("date")).Date.ToString("d. MMMMM yyyy"))</p>
</div>
}
I have tried to browse the Umbraco documentation for changes in the api from 7.4 to 7.5 regarding this issue.
Before the upgrade this worked.
Has any of you experienced this kind of error? - and more important found a solution?
best regards
Jesper
Found the solution - kind of embarrasing.
A check on the property for null value is needed:
var mediaItem = Umbraco.TypedMedia(item.GetPropertyValue(".."));
if(mediaItem != null)
{
....
}
Anyways my excuse is that it worked before the upgrade. :-)
/Jesper
I have a View which displays a ViewModel on the page. I want to allow the user to press a button to create a CSV file which is then emailed to them. I have the POSt working but the ViewModel being sent back is always empty even though the page clearly show many rows.
This is part of the View in question:
<table style="width:99%" cellpadding="3" class="ContentTable" border="1" align="center">
#using (Html.BeginForm("SubmitExcel", "AllRecognition", new { AllRecognitions = ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel }, FormMethod.Post, new { id = "submitExcel" }))
{
<tr>
<td style="padding:3px;">
<input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />
</td>
</tr>
}
<tr style="background-color:#5D7B9D;color:white;">
<th style="width:4%;padding:3px;font-size:12px;">Date</th>
<th style="width:8%;padding:3px;font-size:12px;">Employee</th>
<th style="width:8%;padding:3px;font-size:12px;">Recognized By</th>
<th style="width:6%;padding:3px;font-size:12px;">5-Star Standard</th>
<th style="width:70%;padding:3px;font-size:12px;">Description</th>
<th style="width:4%;padding:3px;font-size:12px;">Points</th>
</tr>
#{
if (ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel != null)
{
foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)
{
#:<tr>
#:<td style="width:4%;padding:3px;font-size:12px;">#item.Date</td>
#:<td style="width:8%;padding:3px;font-size:12px;">#item.Employee</td>
#:<td style="width:8%;padding:3px;font-size:12px;">#item.RecognizedBy</td>
#:<td style="width:6%;padding:3px;font-size:12px;">#item.FiveStarStandard</td>
#:<td style="width:70%;padding:3px;font-size:12px;">#item.Description</td>
#:<td style="width:4%;padding:3px;font-size:12px;">#item.Points</td>
#:</tr>
}
}
}
</table>
This is the controller side receiving the POST method:
public ActionResult SubmitExcel(List<ViewModels.AllRecognitionViewModel> AllRecognitions)
{
ViewBag.NoSearch = "block";
ViewBag.SupervisorSearch = "none";
ViewBag.DepartmentSearch = "none";
ViewBag.EmployeeSearch = "none";
DataTable dtAllRecognitions = Base.SQLHelper.ConvertListToDataTable(AllRecognitions.ToList());
DataSet dsAllRecognitions = new DataSet();
dsAllRecognitions.Tables.Add(dtAllRecognitions);
FHSBase.FHS.DataHelper.SendMeExcelFile(dsAllRecognitions, "Recognitions", CurrentUser);
ViewModels.AllRecognitionBigViewModel AllRecognitionBigViewModel = new ViewModels.AllRecognitionBigViewModel();
AllRecognitionBigViewModel.AllRecognitionViewModel = null;
Models.DateRange DateRange = new Models.DateRange();
DateRange.fromDate = DateTime.Today.Date;
DateRange.toDate = DateTime.Today.Date;
AllRecognitionBigViewModel.DateRange = DateRange;
ViewBag.AllRecognitionBigViewModel = AllRecognitionBigViewModel;
List<SelectListItem> empList = new List<SelectListItem>();
string VPath = "Index";
return View(VPath, empList);
}
The "AllRecognitions" view model is empty in the ActionResult but isn't empty in the view itself. How can I get the current view model back to the ActionResult (SubmitExcel) with the current values seen in the View?
Will this work for you? It's a different approach. Since you are not manipulating anything in your view and just want to export to Excel, why not just put your results in TempData instead of ViewBag and then retrieve it upon the POST? TempData is good in memory for that one trip back.
So, in your initial controller render, do:
TempData["AllRecognition"] = ThisIsMyAllRecognitionViewModelData;
Then when they submit to excel, that data is still in Temp.
public ActionResult SubmitExcel()
{
var MyDataMadeIt = TempData["AllRecognition"];
// do some stuff
}
Your form is empty. So no data is being posted to the server.
When you submit a form, it doesn't send the entire page to the server. (How would the server even know what to do with the HTML to get the values you want from it?) It sends key/value pairs from form elements. And you have only one form element:
<input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />
So only that one key/value is being sent to the server.
The first thing you need to do is wrap your form around the data that you want to post to the server. So basically around your entire table.
Next, you'll need to emit actual form elements. The text on the page isn't usable data, it's just page content. Since the model is a List<ViewModels.AllRecognitionViewModel> then you should be able to do it with a simple modification to your loop. So instead of this:
foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)
You would want this:
for (var i = 0; i < ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel.Count(); i++)
The form elements can be hidden, so you don't change the current UX in your page. Maybe something like this:
#Html.HiddenFor(x => x[i].Date)
or perhaps:
#Html.HiddenFor(x => x.AllRecognitionViewModel[i].Date)
I'm actually guessing a bit here, since I'm much more accustomed to using the model instead of the ViewBag for this. (Which you may want to try using instead, it'll probably make things simpler.) You may need to do some debugging to determine exactly what's going on in the server-side code here.
Ultimately, what you're looking to see in your client-side code are elements similar to this:
<input type="hidden" name="AllRecognitionViewModel.Date[0]" value="..." />
So, if the above #Html.EditorFor suggestions don't work, you can always do it manually. In the loop it might look something like this:
<input type="hidden" name="AllRecognitionViewModel.Date[#i]" value="#ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel[i].Date" />
The key thing to notice is the name attribute. By sending these "arrays" of key/value pairs to the server, the model binder should be able to construct the List<ViewModels.AllRecognitionViewModel>.
What is the best approach to take when converting a basic ActionResult to JSON objects and rendering them in a PartialView? My objective is to modify the application so that instead of the page rendering only the comments in the db at the time of the page request to a type of data service that updates thePartialView to add any incoming comments that may have been posted since the last page request. I think the solution I am looking for will use OData in json format and then bind that data using knockout.js, but not sure.
Here is the Controller ActionResult which returns an IEnumerable list of objects from the repository to a PartialView:
[ChildActionOnly]
public ActionResult GetCommentsById(int AId = 0)
{
if (AId == 0)
return HttpNotFound();
return PartialView("_CommentsPartial",
_unitOfWork.ArticleRepository.GetCommentsByArticleId(AId));
}
Here is a snippet of the PartialView to keep things short:
#model IEnumerable<BlogSite.Models.Comment>
#using BlogSite.Helpers;
<ul id="comments-list">
#{
foreach (var comment in Model)
{
<!--Grabs Parent Comment and then all replies w/ParentCommentId b4 grabs new Parent Comment -->
if (comment.isRoot && comment.ParentCommentId == null)
{
<!-- Comment -->
int counter = 0; foreach (var c in Model) { if (c.ParentCommentId == comment.CommentId) { counter += 1; } }
<li id="#comment.CommentId" itemscope itemtype="http://schema.org/UserComments" class="comment-container" tabindex="#comment.CommentId">
Then I call it from the Details view:
<div id="comments-panel" class="panel-box">
<div class="show-comments"><div id="upNdown"></div><span id="showNhide">Show Comments</span></div><br /> <br />
<div id="comments-partial" style="display:none;">
#Html.Action("AddComment", "Comment", new { AId = Model.ArticleId })
#Html.Action("GetCommentsById", "Article", new { AId = Model.ArticleId })
</div>
</div>
How can I make this conversion as painless as possible? Thanks in advance!
I think I gather from your question that the controller already did its work and that you simply want to "consume" the data output from it as if it were an AJAX request using the same js code. You can do this fairly easily by just serializing the data in the model using the Newtonsoft Json.NET api and extensions provided by Forloop.HtmlHelpers. These can be installed as nuget packages if you haven't already.
First, you would place this in your partial view
Note: If you don't want to install the Newtonsoft package you can replace JsonConvert.SerializeObject with the System.Web.Helpers method Json.Encode
#{
using (var context = Html.BeginScriptContext())
{
Html.AddScriptBlock("var jsonData=" + JsonConvert.SerializeObject(Model) + ";");
}
}
Then in your layout page, to ensure that your script block is rendered at the appropriate time, add this call to Html.RenderScripts
#Scripts.Render("~/bundles/jquery")
#*Add any other dependency scripts*#
#Html.RenderScripts()
#RenderSection("scripts", required: false)
This is why you need the Forloop.HtmlHelpers package, these extension methods help mitigate out-of-order script code getting rendered in the partial view before jQuery or anything else has started up.
Hope that helps
The following statement is erring in my MVC razor view.
#foreach (var user in Model.Users)
{
<li>Add user</li>
<li><a href="#">#user.FirstName #user.LastName
if(user.FirstName != "")
{
#(#user.UserName)
}else{
#user.UserName
}
</a></li>
}
I cannot seem to figure out the razor to handle this properly.
Most likely you are looking for #if instead of if because starting HTML tag switches context from "code" to HTML and you need to switch it back to "code".
#foreach(...
{
<span>...
#if(...)
{
<text>#user.UserName</text>
}
</span>
}
I am facing problem in retrieving Subject title of a mail from Unread mails using Selenium webdriver-C#.
Here's the HTML code :
<div class="ae4 UI UJ" gh="tl">
<div class="Cp">
<div>
<table id=":8e" class="F cf zt" cellpadding="0">
<colgroup>
<tbody>
<tr id=":8d" class="zA zE">
<td class="PF xY"></td>
<td id=":8c" class="oZ-x3 xY" style="">
<td class="apU xY">
<td class="WA xY">
<td class="yX xY ">
<td id=":87" class="xY " role="link" tabindex="0">
<div class="xS">
<div class="xT">
<div id=":86" class="yi">
<div class="y6">
**<span id=":85">
<b>hi</b>
</span>**
<span class="y2">
</div>
</div>
</div>
</td>
<td class="yf xY "> </td>
<td class="xW xY ">
</tr>
I am able to print 'emailSenderName' in console but unable to print 'text' (subject line i.e. "hi" in this case) as it is between span tags. Here's my code.
//Try to Retrieve mail Senders name and Subject
IWebElement tbl_UM = d1.FindElement(By.ClassName("Cp")).FindElement(By.ClassName("F"));
IList<IWebElement> tr_ListUM = tbl_UM.FindElements(By.ClassName("zE"));
Console.WriteLine("NUMBER OF ROWS IN THIS TABLE = " + tr_ListUM.Count());
foreach (IWebElement trElement in tr_ListUM)
{
IList<IWebElement> td_ListUM = trElement.FindElements(By.TagName("td"));
Console.WriteLine("NUMBER OF COLUMNS=" + td_ListUM.Count());
string emailSenderName = td_ListUM[4].FindElement(By.ClassName("yW")).FindElement(By.ClassName("zF")).GetAttribute("name");
Console.WriteLine(emailSenderName);
string text = td_ListUM[5].FindElement(By.ClassName("y6")).FindElement(By.TagName("span")).FindElement(By.TagName("b")).Text;
Console.WriteLine(text);
}
I had also tried by directly selecting the Text from tag of 5th Column (td), which contains the subject text (in my case), but no results.
I might went wrong somewhere or may be there is some other way of doing it.
Please suggest, Thanks in advance :)
The 'getText' method available in the Java implementation of Selenium Web Driver seems to do a better job than the equivalent 'Text' property available in C#.
I found a way of achieving the same end which, although somewhat convoluted, works well:
public static string GetInnerHtml(this IWebElement element)
{
var remoteWebDriver = (RemoteWebElement)element;
var javaScriptExecutor = (IJavaScriptExecutor) remoteWebDriver.WrappedDriver;
var innerHtml = javaScriptExecutor.ExecuteScript("return arguments[0].innerHTML;", element).ToString();
return innerHtml;
}
It works by passing an IWebElement as a parameter to some JavaScript executing in the Browser, which treats it just like a normal DOM element. You can then access properties on it such as 'innerHTML'.
I've only tested this in Google Chrome but I see no reason why this shouldn't work in other browsers.
Using GetAttribute("textContent") instead of Text() did the trick for me.
Driver.FindElement(By.CssSelector("ul.list span")).GetAttribute("textContent")
Try this
findElement(By.cssSelector("div.y6>span>b")).getText();
I had the same problem. Worked on PhantomJS. The solution is to get the value using GetAttribute("textContent"):
Driver.FindElementsByXPath("SomexPath").GetAttribute("textContent");
Probably too late but could be helpful for someone.
IWebElement spanText= driver.FindElement(By.XPath("//span[contains(text(), 'TEXT TO LOOK FOR')]"));
spanText.Click();
IWebElement spanParent= driver.FindElement(By.XPath("//span[contains(text(), 'TEXT TO LOOK FOR')]/ancestor::li"));
spanParent.FindElement(By.XPath(".//a[contains(text(), 'SIBLING LINK TEXT')]")).Click();
bonus content here to look for siblings of this text
once the span element is found, look for siblings by starting from parent. I am looking for an anchor link here. The dot at the start of XPath means you start looking from the element spanParent
<li>
<span> TEXT TO LOOK FOR </span>
<a>SIBLING LINK TEXT</a>
</li>
This worked for me in Visual Studio 2017 Unit test project. I'm trying to find the search result from a typeahead control.
IWebElement searchBox = this.WebDriver.FindElement(By.Id("searchEntry"));
searchBox.SendKeys(searchPhrase);
System.Threading.Thread.Sleep(3000);
IList<IWebElement> results = this.WebDriver.FindElements(By.CssSelector(".tt-suggestion.tt-selectable"));
if (results.Count > 1)
{
searchResult = results[1].FindElement(By.TagName("span")).GetAttribute("textContent");
}