Parent of htmlagilitypack text node is select instead of option? - html-agility-pack

Using htmlagility, I am searching for text nodes in a dom structure consisting of a select.
<select>
<option>
one
</option>
<option>
two
</option>
</select>
Those nodes parents seems to be the
<select>
instead of an
<option>
Why?
using System.IO;
using System.Linq;
using HtmlAgilityPack;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Foo.Test
{
[TestClass]
public class HtmlAgilityTest
{
[TestMethod]
public void TestTraverseTextNodesInSelect()
{
var html = "<select><option>one</option><option>two</option></select>";
var doc = new HtmlDocument();
doc.Load(new StringReader(html));
var elements = doc.DocumentNode.Descendants().Where(n=>n.Name == "#text");
Assert.AreEqual(2, elements.Count());
Assert.AreEqual("select", elements.ElementAt(0).ParentNode.Name);
Assert.AreEqual("select", elements.ElementAt(1).ParentNode.Name);
}
}
}

[TestMethod]
public void TestTraverseTextNodesInSelect()
{
HtmlNode.ElementsFlags.Remove("option");
var html = "<select><option>one</option><option>two</option></select>";
var doc = new HtmlDocument();
doc.Load(new StringReader(html));
var elements = doc.DocumentNode.Descendants().Where(n=>n.Name == "#text");
Assert.AreEqual(2, elements.Count());
Assert.AreEqual("select", elements.ElementAt(0).ParentNode.Name);
Assert.AreEqual("select", elements.ElementAt(1).ParentNode.Name);
}
you can try with this.
In the library it has like this. You need to remove it. by default the AgilityPack is set to treat option tags as empty.
ElementsFlags.Add("option", HtmlElementFlag.Empty);

That's because HtmlAgilityPack drop closing <option> tag by default. HAP sees your HTML like this :
Console.WriteLine(doc.DocumentNode.OuterHtml);
//result :
//<select><option>one<option>two</select>
And as mentioned in the linked question above, you can alter that behavior by calling following line before initiating the HtmlDocument :
HtmlNode.ElementsFlags.Remove("option");

Related

Blazor select dropdown set active value by code

I am dynamicaly filling the values of a dropdown list based of the content of a List. when a item in the dropdown is selected an option to remove this item is shown. When the item is removed, it is first removed from the list and then the dropdown list is rebuilt, this is where I run in to problems. Instead of returning the dropdown to its default value when it is rebuilt the value just below the removed one is shown as selected (this happens whitout the #onchange value being triggered). How can i make the dropdown list return to its default value when it is being rebuilt?
here is some code, Razor code:
<select class="form-control" #onchange="selectedValue">
<option value="">select one</option>
#foreach (var mod in values)
{
<option value="#mod.Id">#mod.Name</option>
}
</select>
the class named Items that is populationg the list:
public class Items
{
public string Name { get; set; }
public int Id { get; set; }
public Items(string name, int id)
{
Name = name;
Id = id;
}
}
the list itself (before it is populated):
public List<Items> itm = new List<Items>();
the function thats called onchange:
public string SelectedValue = "";
public void selectedValue(ChangeEventArgs selectEvent)
{
SelectedValue = selectEvent.Value.ToString();
}
so to sum it up, this is what's happening:
the list is pupolated with Items.
the select list is built of the items in the list using a #foreach loop (se razor code).
an item is selected from the dropdown, this runs the selectedValue() function and changes the value ud the SelectedValue string.
the selected item is deleted from the list of items
the dropdown is rebuilt using the modefied list
the selected item is now set to the one directly below the deleted item, this is happening without onchange being run. this is the problem
Now the selected value of the dropdown don't correspond to the one of the SelectedValue string.
this can probably be solved by setting the element to its default option/value, but i can't figure out how to do this.
How can I change the selected value to be the default option (in my case the "select one" option with value "") of my dropdown list?
I was having a similar issue. I solved it by putting an if statement in the loop that generates the option elements and from there conditionally add the selected attribute, i.e.;
<select #onchange="listChanged">
#foreach (var item in PageModel.Lists)
{
if(item.Id == currListId)
{
<option value="#item.Id" selected>#item.Name</option>
}
else
{
<option value="#item.Id">#item.Name</option>
}
}
</select>
Sorry for not taking the time to adapt my code to your situation, but you've already solved your problem. This is for anyone else that comes across this problem (prolly me next time i need to do something like this, lol).
How can I change the selected value to be the default option (in my case the "select one" option with value "") of my dropdown list?
I'm afraid you can do it only with JSInterop as follows:
Generally speaking, the selectedIndex property on the select element is set to 0 after you select an option in the select element. This is done in the selectedValue method...
This is a complete working code snippet. Run it and test it...
#page "/"
<select #ref="myselect" id="myselect" class="form-control"
#onchange="selectedValue">
<option selected value="-1">select one</option>
#foreach (var item in items)
{
<option value="#item.ID">#item.Name</option>
}
</select>
<p>#SelectedValue</p>
#code {
[Inject] IJSRuntime JSRuntime { get; set; }
private ElementReference myselect;
List<Item> items = Enumerable.Range(1, 10).Select(i => new Item {
Name = $"Name {i.ToString()}", ID = i }).ToList();
public string SelectedValue = "";
public void selectedValue(ChangeEventArgs args)
{
SelectedValue = args.Value.ToString();
var item = items.Single(item => item.ID == Convert.ToInt32(SelectedValue));
items.Remove(item);
JSRuntime.InvokeVoidAsync("exampleJsFunctions.selectElement", myselect);
}
public class Item
{
public string Name { get; set; }
public int ID { get; set; }
}
}
Put the following JS code in the _Host.cshtml file:
<script src="_framework/blazor.server.js"></script>
<script>
window.exampleJsFunctions =
{
selectElement: function (element) {
element.selectedIndex = 0;
}
};
</script>
A similar problem confronted me in a .NET MAUI Blazor project. In the project I'm working on a view model manages most of the form behavior. In addition, CommunityToolkit.Mvvm is used to manage the property changed management. So this answer will base itself on that configuration adjusted for using bindings. It also follows answer given by #MikeT.
The assumption is that another element is effecting the selection decision. In my case if there is only one element in values to choose from, that element would become selected automatically. Otherwise, the user makes a selection.
<select class="form-control" #Bind="vm.selectedValueId"
disabled="#(vm.IsValuesDataLoaded == false)">
<option value="">select one</option>
#foreach (var mod in values)
{
#if(#mod.Id == #vm.selectedValueId)
{
<option value="#mod.Id" selected>#mod.Name</option>
}
else
{
<option value="#mod.Id">#mod.Name</option>
}
}
</select>
#code{
ValueViewModel vm; //instantiated inside OnInitialized
// more ...
}
As the view model is handling construction of the element list in values, all of the activity takes place there. So this section of view model code would be --
partial void OnSomeOtherPreliminaryPropertyChanged(string? value)
{
// code for processing this event
// condition test could also be for an entry previously selected
if(values.Count == 1)
{
SelectedValueId = whatever_Value_Should_Be_Default_For_Selection;
OnModuleIdChanged(selectedValueId);
}
IsValuesDataLoaded = true;
}
[ObservableProperty]
private int selectedValueId;
[ObservableProperty]
private string selectedValue;
//OnSelected... is called when selectedValueId changes. In this case
// the code is changing the selection
partial void OnSelectedValueIdChanged(int? value)
{
SelectedValue = Values[value].Name
}
public bool IsValuesDataLoaded //controls the enabled state of the select element
{
get;
private set;
} = false;

How to dynamically generate CSS file from database in ASP.NET MVC

i was going through a post in stackoverflow and the link is Dynamically generate CSS file from database in ASP.NET MVC
here i am giving the full code and i like to know few thinks from the code.
I think the simplest way would be to add something like the following action method to a controller:
public class CssController : Controller
{
public ContentResult GetTheme()
{
var builder = new StringBuilder();
IDictionary<string, IDictionary<string, string>> css = new Dictionary<string, IDictionary<string, string>>();
/* Populate css object from the database */
foreach (var selector in css)
{
builder.Append(selector.Key);
builder.Append(" { ");
foreach (var entry in selector.Value)
{
builder.Append(string.Format("{0}: {1}; ", entry.Key, entry.Value));
}
builder.AppendLine("}");
}
return Content(builder.ToString(), "text/css");
}
}
Now in your page you can reference it like so:
<link href="<%: Url.RouteUrl(new { controller= "CssController", action = "GetCss" }) %>" rel="stylesheet" type="text/css" />
my question is what file name will show in href ?
the controller CssController & action method GetCss is writing string to out going stream so no file name should show in href....how this code will work?? please guide me to understand this catch......thanks

problem with Razor syntax and trying to output a string from a function

I have the following code in a .cshtml file, but each <option> is returned with nothing in it. I have verified the GetDescription() is returning the right string, so I must have a syntax problem in my Razor code. Can someone tell me what the problem is please?
<select>
#{
Array enumValues = null;
enumValues = Enum.GetValues(typeof(SearchOperatorString));
foreach (var type in enumValues)
{
<option>
#{((Enum)type).GetDescription();} </option>
}
}
</select>
You're making a statement block, which calls GetDescription, but does nothing with it's result.
You want to use a code nugget instead, which prints an expression to the page:
#((Enum)type).GetDescription()
Instead of doing this manually, you should call the DropDownList helper:
#Html.DropDownList("myName",
enumValues.Cast<SearchOperatorString>()
.Select(s => new SelectListItem { Text = e })
)
Try this:
#foreach(var type in Enum.GetValues(typeof(SearchOperatorString))){
<option>#((Enum)type).GetDescription()</option>
}

ASP.NET MVC2 validation not working with drop down list in IE <8

I have a form with a dropdownlist rendered using Html.DropDownListFor(...). The view model field that corresponds with the dropdown list has a [Required(...)] attribute attached to it. This works fine on my local machine, but as soon as I publish to our development server, the drop down lists keep displaying the required error message, even when a value is selected in the list. This only happens in IE - Firefox submits just fine.
Any thoughts?
Relevant code
View:
<ol class="form">
<li>
<%= Html.LabelFor(x => x.ContactTitle) %>
<%= Html.DropDownListFor(x=>x.ContactTitle, Model.GetTitleOptions()) %>
<%= Html.ValidationMessageFor(x => x.ContactTitle) %>
</li>
<!-- more fields... -->
</ol>
View Model:
[Required(ErrorMessage = "Title is required")]
[DisplayName("Title")]
public string ContactTitle { get; set; }
// ...
public SelectList GetTitleOptions()
{
return new SelectList(new string[]
{
"","Dr.", "Mr.", "Ms.", "Mrs.", "Miss"
});
}
It's all pretty basic stuff... I'm at a loss.
Edit: Just discovered this bug is limited to IE 8 compatibility view (and maybe prior versions). IE 8 in standards mode works as expected...
Chalk this one up to stupidity. The code in the example produces output similar to the following:
<select>
<option></option>
<option>Dr.</option>
<option>Mr.</option>
<option>Ms.</option>
<option>Mrs.</option>
<option>Miss</option>
</select>
And the relevant MVC validation function (when a RequiredAttribute is applied to a property that corresponds to a drop down list) is:
Sys.Mvc.RequiredValidator._validateSelectInput = function Sys_Mvc_RequiredValidator$_validateSelectInput(optionElements) {
/// <param name="optionElements" type="DOMElementCollection">
/// </param>
/// <returns type="Object"></returns>
for (var i = 0; i < optionElements.length; i++) {
var element = optionElements[i];
if (element.selected) {
if (!Sys.Mvc._validationUtil.stringIsNullOrEmpty(element.value)) {
return true;
}
}
}
return false;
}
Notice the function checks element.value. In the case of the html above, the value attribute is empty because there is no value attribute on the option elements. Therefore, the validation function returns false and the error occurs. This only appears to happen in IE <8, presumably because other browsers by default assign an option element's text to the value attribute if none is specified.
The solution was to modify the way I was returning the select list items from which the drop down list was built like so:
public IEnumerable<SelectListItem> GetTitleOptions()
{
return BuildSelectListItems(new string[]
{
"","Dr.", "Mr.", "Ms.", "Mrs.", "Miss"
});
}
private List<SelectListItem> BuildSelectListItems(IEnumerable<string> values) {
return (from v in values
select new SelectListItem()
{
Text = v,
Value = v
}).ToList();
}
This results in the much more predictable HTML output:
<select>
<option value=""></option>
<option value="Dr.">Dr.</option>
<option value="Mr.">Mr.</option>
<option value="Ms.">Ms.</option>
<option value="Mrs.">Mrs.</option>
<option value="Miss">Miss</option>
</select>
which of course the function validates properly.

MS AJAX Library 4.0 Sys.create.dataView

One again Microsoft poor documentation has left me confused. I am trying to use the new features of the .NET 4.0 framework. I am using the following code to populate the Title and Director but it keeps getting blank.
The service returns the result correctly like
[d: { title = "ss, director ="" } something like that but the li never gets populated.
<script language="javascript" type="text/javascript">
Sys.require([Sys.components.dataView, Sys.components.dataContext,Sys.scripts.WebServices], function () {
Sys.create.dataView("#moviesView",
{
dataProvider: "MovieService.svc",
fetchOperation: "GetMovies",
autoFetch: true
});
});
</script>
And here it the HTML code:
<ul id="moviesView">
<li>
{{Title}} - {{Director}}
</li>
</ul>
IS THIS THE LATEST URL TO Start.js file.
<script src="http://ajax.microsoft.com/ajax/beta/0911/Start.js"></script>
Here is the Ajax-Enabled WCF Service:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MovieService
{
[OperationContract]
public Movie GetMovies()
{
return new Movie() { Title = "SS", Director = "SSSSS" };
}
}
[DataContract]
public class Movie
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Director { get; set; }
}
You need to add the sys-template class attribute to the unordered list tag.
<ul id="moviesView" class="sys-template">
Here's an excerpt from Client-side Data Binding in ASP.NET AJAX 4.0
The one other requirement for defining
a template is the parent element must
have the sys-template CSS class
applied, and that class must be
defined with display set to none, as
shown in the example above. This
convention serves two purposes – it
helps the parser identify which
elements are part of a template on
your page (which will become important
when we use declarative
instantiation), and it keeps the
template markup hidden until ASP.NET
Ajax has completed the binding (it
will toggle the display to be
visible).

Resources