HtmlAgility problem - html

i am trying to extract some data between divs.
<div class="movie_general"><div class="img"><a href="/Movies.html" title="Watch Movie">
Fore example if i want the link "/Movies.html" i used:
string hrefValue = doc.DocumentNode
.Descendants("div")
.Where(x => x.Attributes["class"].Value == "movie_general")
.Select(x => x.Element("a").Attributes["href"].Value)
.FirstOrDefault();
MessageBox.Show(hrefValue);
but i get a NullReferenceException at Where(x => x.Attributes["class"].Value == "movie_general")
What am i doing wrong?

It happens because the Linq provider must iterate through all other nodes in the document to check if it matches your search. This document must have at least one div which does not have a class attribute. So, the error happens by trying to read the Value property of an attribute which does not exist.
Replace this
.Where(x => x.Attributes["class"].Value == "movie_general")
.Select(x => x.Element("a").Attributes["href"].Value)
with this
.Where(x => x.Attributes["class"] != null && x.Attributes["class"].Value == "movie_general")
.Select(x => x.Element("a") != null && x.Element("a").Attributes["href"] != null ? x.Element("a").Attributes["href"].Value : string.Empty)

If you already know the class and that the a tag is subordinate to that, why not just grab it directly using:
HtmlDocument doc = new HtmlDocument();
doc.Load("C:\\temp\\stackhtml.html");
string link = doc.DocumentNode.SelectSingleNode("//div[#class='movie_general']//a").GetAttributeValue("href", "unkown");
Console.WriteLine(link);
Console.ReadLine();
and the result:
I added closing div tags to your example so that I could scrape it and dumped it in a file on my c drive:
<div class="movie_general">
<div class="img">
<a href="/Movies.html" title="Watch Movie">
</div>
</div>

Related

Yii2 compareValidator when

During user input validation I would like to compare an attribute with a value.
I have this code:
['ao_id', 'compare', 'when' => function($model) {
return $model->lqp_id == 24 || $model->lqp_id == 26 || $model->lqp_id == 46;
}, 'compareValue' => 50],
It works (however only when 'enableClientValidation' => false), but is it possible, to show rather the name of the foreign attribute somehow? Because it doesn't help much if the user is getting an error message that outer surface (ao_id) must be 50. Nobody has a clue what does it mean, because in the dropdown you see only the names and not the ids. Many thanks!
First of all, if you want your conditional validation to work on the client-side too (when enableClientValidation=>true), then add the whenClient property which contains the javascript code that will do the validation.
Second, you can use the message property to specify a custom validation error.
[
'ao_id',
'compare',
'when' => function ($model) {
return $model->lqp_id == 24 || $model->lqp_id == 26 || $model->lqp_id == 46;
},
'whenClient' => "function (attribute, value) {
return $('#lqp_id').val() == '24' || $('#lqp_id').val() == '26' || $('#lqp_id').val() == '46';
}",
'compareValue' => 50,
'message'=>'ao_id must be 50 when lqp_id is 24, 26 or 46'
]
Attention: be sure to check and change the id of the input field $('#lqp_id') as this is most likely different to my example.
Add message key where you define your own message that will be displayed instead of default one.

Get Value from object in html MVC

How I can get an object in HTML?
I have this:
#Html.DisplayFor(model => model.Product.Keys.Where(x => x.TransactionID == model.TransactionID))
but here I have an object, and I want to display only one value.
I tried something like this:
#{
var key = model.Product.Keys.Where(x => x.TransactionID == model.TransactionID);
}
but model name doesn't exist in the current context.
Any ideas?
You should to use Model
#{
var key = Model.Product.Keys.Where(x => x.TransactionID == Model.TransactionID);
}

Setting Content for Bound Kendo UI MVC TabStrip using Razor

Having trouble setting the tab content for a databound tabstrip. I found an example of how to to it using webforms syntax, but can't successfully convert this to razor:
Here is webforms syntax from here:
.BindTo(Model,
(item, navigationData) =>
{
item.Text = navigationData.Text;
item.ImageUrl = navigationData.ImageUrl;
item.Content = () =>
{%>
Some random content I want to appear
<% };
})
Here is how I am trying to do it in Razor:
#(Html.Kendo().TabStrip()
.Name("OrderDetailsTabs")
.BindTo(Model, (item, model) =>
{
item.Text = "Part: " + model.WOHdr.OrderDetailId; // tab text
item.Content = () =>
{
(#<text>
Test #(model.WOHdr.Id)
</text>);
};
Which produces the error:
A local variable named 'item' cannot be declared in this scope because it would give a different meaning to 'item', which is already used in a 'parent or current' scope to denote something else
You have to use .InlineTemplate...not .Content
tab.Template.InlineTemplate =
#<text>
#(Html.EditorFor(model => tabModel, "WorkOrder", tabModel))
</text>;

Help building up LINQ query when using anonymous types

I've started out with:
IQueryable<Author> authors2 = db.Authors;
Then I build up authors2 in multiple if statements, here's one of them
authors2 = authors2.Where(t => t.ItemAuthors.Any(b => b.Item.CategoryItems.Any(z => z.categoryID == int.Parse(ddlCategory.SelectedValue))));
Then finally I would like to append this to the end of the built up where clauses
authors2.OrderBy(x => x.text).Select(x => new
{
authorText = string.Format("{0} ({1})",x.text, x.ItemAuthors.Count())
});
To bind a control like this:
ddlAuthor.DataSource = authors2;
ddlAuthor.DataTextField = "authorText";
ddlAuthor.DataBind();
Apparently the compiler is not very happy about my select new statement. How can I rewrite this to achieve the same goal? I believe this is called creating an anonymous type.
It says an explicit conversion exists(are you missing a cast?) I can't figure out how to cast it.
In your third statement, the returned type is not same as authors2 because the Select projects a different type other than Author
So assign the value to a new variable
var authorsFinal = authors2
.OrderBy(x => x.text)
.Select(x => new
{
authorText = string.Format("{0} ({1})",
x.text,
x.ItemAuthors.Count())
});

How do I get the collection of Model State Errors in ASP.NET MVC?

How do I get the collection of errors in a view?
I don't want to use the Html Helper Validation Summary or Validation Message. Instead I want to check for errors and if any display them in specific format. Also on the input controls I want to check for a specific property error and add a class to the input.
P.S. I'm using the Spark View Engine but the idea should be the same.
So I figured I could do something like...
<if condition="${ModelState.Errors.Count > 0}">
DisplayErrorSummary()
</if>
....and also...
<input type="text" value="${Model.Name}"
class="?{ModelState.Errors["Name"] != string.empty} error" />
....
Or something like that.
UPDATE
My final solution looked like this:
<input type="text" value="${ViewData.Model.Name}"
class="text error?{!ViewData.ModelState.IsValid &&
ViewData.ModelState["Name"].Errors.Count() > 0}"
id="Name" name="Name" />
This only adds the error css class if this property has an error.
<% ViewData.ModelState.IsValid %>
or
<% ViewData.ModelState.Values.Any(x => x.Errors.Count >= 1) %>
and for a specific property...
<% ViewData.ModelState["Property"].Errors %> // Note this returns a collection
To just get the errors from the ModelState, use this Linq:
var modelStateErrors = this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors);
Condensed version of #ChrisMcKenzie's answer:
var modelStateErrors = this.ModelState.Values.SelectMany(m => m.Errors);
This will give you one string with all the errors with comma separating
string validationErrors = string.Join(",",
ModelState.Values.Where(E => E.Errors.Count > 0)
.SelectMany(E => E.Errors)
.Select(E => E.ErrorMessage)
.ToArray());
Putting together several answers from above, this is what I ended up using:
var validationErrors = ModelState.Values.Where(E => E.Errors.Count > 0)
.SelectMany(E => E.Errors)
.Select(E => E.ErrorMessage)
.ToList();
validationErrors ends up being a List<string> that contains each error message. From there, it's easy to do what you want with that list.
Thanks Chad! To show all the errors associated with the key, here's what I came up with. For some reason the base Html.ValidationMessage helper only shows the first error associated with the key.
<%= Html.ShowAllErrors(mykey) %>
HtmlHelper:
public static String ShowAllErrors(this HtmlHelper helper, String key) {
StringBuilder sb = new StringBuilder();
if (helper.ViewData.ModelState[key] != null) {
foreach (var e in helper.ViewData.ModelState[key].Errors) {
TagBuilder div = new TagBuilder("div");
div.MergeAttribute("class", "field-validation-error");
div.SetInnerText(e.ErrorMessage);
sb.Append(div.ToString());
}
}
return sb.ToString();
}
Here is the VB.
Dim validationErrors As String = String.Join(",", ModelState.Values.Where(Function(E) E.Errors.Count > 0).SelectMany(Function(E) E.Errors).[Select](Function(E) E.ErrorMessage).ToArray())
If you don't know what property caused the error, you can, using reflection, loop over all properties:
public static String ShowAllErrors<T>(this HtmlHelper helper) {
StringBuilder sb = new StringBuilder();
Type myType = typeof(T);
PropertyInfo[] propInfo = myType.GetProperties();
foreach (PropertyInfo prop in propInfo) {
foreach (var e in helper.ViewData.ModelState[prop.Name].Errors) {
TagBuilder div = new TagBuilder("div");
div.MergeAttribute("class", "field-validation-error");
div.SetInnerText(e.ErrorMessage);
sb.Append(div.ToString());
}
}
return sb.ToString();
}
Where T is the type of your "ViewModel".
Got this from BrockAllen's answer that worked for me, it displays the keys that have errors:
var errors =
from item in ModelState
where item.Value.Errors.Count > 0
select item.Key;
var keys = errors.ToArray();
Source: https://forums.asp.net/t/1805163.aspx?Get+the+Key+value+of+the+Model+error