htmlagilitypack remove row attributes - html-agility-pack

How do we remove the inline height attribute from html?
<tr style="height:2px;">
</tr>
<tr style="height:2px;">
</tr>
I want only height attributes to be removed from all tr tags.
Thanks a lot in advance,

You can:
If your trs have no other styles other than height, you can simply remove strip them from their style attribute (the line I commented out)
Otherwise, you can write something like the snippet below to filter which style keys you want to remove
string html = #"<tr style='height:2px;'>
</tr>
<tr style='height:2px;'>
</tr>";
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
var trs = doc.DocumentNode.SelectNodes("tr");
foreach (var tr in trs)
{
Console.WriteLine(tr.OuterHtml);
//tr.Attributes.Remove("style");
var filteredStyles = GetStyles(tr.GetAttributeValue("style"), "height");
tr.SetAttributeValue("style", string.Join(":", filteredStyles));
Console.WriteLine(tr.OuterHtml);
}
Helper function:
private static List<string> GetStyles(string style, params string[] keysToRemove)
{
List<string> styles = new List<string>();
var stylesKeyPairs = style.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (keysToRemove != null)
{
foreach (var styleKeyPair in stylesKeyPairs)
{
var styleKeys = styleKeyPair.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (!keysToRemove.Contains(styleKeys.FirstOrDefault()))
styles.Add(styleKeyPair);
}
}
else
styles.AddRange(stylesKeyPairs);
return styles;
}
Output (for both solutions, in this case):

Related

Kendo TreeView does not generate data attributes for custom template

I am using Kendo.Mvc.UI.TreeViewItemModel to build kendo treeview. Othe than Id and Text property this class also has HtmlAttributes property of type IDictionary<string, string>
I can add any additional data into HtmlAttributes property while building model, and kendo would render these Html Attributes as html data attribute on tree node.
public IEnumerable<TreeViewItemModel> BuildTreeView(IEnumerable<MyGroup> groups)
{
var nodes = new List<TreeViewItemModel>();
foreach (var grp in groups)
{
var root = new TreeViewItemModel()
{
Id = "0",
Text = grp.Key,
HasChildren = grp.Any()
};
foreach (var item in grp)
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("data-template-id",item.TemplateID.ToString());
dictionary.Add("data-filename", item.FileName);
root.Items.Add(new TreeViewItemModel()
{
Text = item.DisplayText,
HasChildren = false,
HtmlAttributes = dictionary
});
}
nodes.Add(root);
}
return nodes;
}
cshtml
#(Html.Kendo().TreeView()
.Name("MyTreeView")
.DataTextField("Text")
.BindTo(Model.Items))
and then in JS i can access this data
var _treeView = $("#MyTreeView").getKendoTreeView();
var htmlItem = _treeView.select();
var templateid = htmlItem.data("template-id");
var filename = htmlItem.data("filename");
This is working fine as long as im using kendo's default template. However if i use custom template then Kendo does not generate data attributes
#(Html.Kendo().TreeView()
.Name("MyTreeView")
.TemplateId("treeViewItemTemplate")
.BindTo(Model.Items))
<script type="text/x-kendo-tmpl" id="treeViewItemTemplate">
<span>#=item.text#</span>
# if (!item.items){#
<span class="glyphicon glyphicon-plus-sign" aria-hidden=true></span>
#}#
</script>
now when the page renders and using F12 if look at the tree node ( which is li tag) i don't see any data attributes

Image cropper doesn't work within nested foreach

I have a problem in Umbraco 7. I'm using a nested Multiple Node Tree Picker, but the GetCropUrl doesn't work. The crop function is ok, I've already used it.
#{
if (CurrentPage.HasValue("artists"))
{
var artistList = CurrentPage.artists.ToString().Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
var artistCollection = Umbraco.Content(artistList);
foreach (var artist in artistCollection)
{
if (artist.HasValue("coverImages"))
{
var coverImagesList = artist.coverImages.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
var coverImagesCollection = Umbraco.Media(coverImagesList);
foreach (var coverImage in coverImagesCollection.RandomOrder().Take(1).Where("Visible"))
{
<img src="#coverImage.GetCropUrl(305, 195)"/>
}
}
}
}
}
Update:
I changed the code and I started to use Id.
When I use this:
foreach (var coverImage in coverImagesCollection)
<p>#coverImage.Id</p>
<img src="#Umbraco.TypedMedia(1105).Url"/>
}
I got back the the image id from #coverImage.Id, and the image is working.
When I use this:
foreach (var coverImage in coverImagesCollection)
<img src="#Umbraco.TypedMedia(coverImage.Id).Url"/>
}
The image is still good.
After I'm cropping with fix id.
foreach (var coverImage in coverImagesCollection)
<img src="#Umbraco.TypedMedia(1105).GetCropUrl(305, 195)"/>
}
Working, but then:
foreach (var coverImage in coverImagesCollection)
<img src="#Umbraco.TypedMedia(coverImage.Id).GetCropUrl(305, 195)"/>
}
I got an error:
'Umbraco.Web.Models.PublishedContentBase' does not contain a definition for 'GetCropUrl'
How is that possible?
I've got an answer from Umbraco forum. He said coverImage.Id is dynamic, so I need to try this, and it worked perfectly:
foreach (var coverImage in coverImagesCollection)
<img src="#Umbraco.TypedMedia((int)coverImage.Id).GetCropUrl(305, 195)"/>
}

Extracting via HtmlAgilityPack

I'm using the HtmlAgilityPack and trying to extract an image name from html. Here's the html string I have:
sHtml = "<HTML><HEAD></HEAD><BODY>Here are some images.</br>1) < IMG style='MARGIN-BOTTOM: 20px; MARGIN-LEFT: 20px' align=right src='images/sample001.jpg'>2) < IMG style='MARGIN-BOTTOM: 25px; MARGIN-LEFT: 25px' align=right src='images/sample002.png'></br> And some docs as well.</br>1) href='javascript:parent.POPUP({url:'testDoc001.htm',type:'shared',width:600,height:645})'></br>2) href='javascript:parent.POPUP({url:'testDoc002.html',type:'shared',width:700,height:712})'></br></BODY></HTML>"
In WPF C# I pass this string into the following routine:
private static List<string> ExtractHtmlInfo(string sHtml)
{
HtmlDocument doc = new HtmlDocument();
doc.Load(new StringReader(sHtml));
HtmlNode root = doc.DocumentNode;
List<string> anchorTags = new List<string>();
//foreach (HtmlNode link in root.SelectNodes("//a"))
foreach (HtmlNode link in root.SelectNodes("//img"))
{
string att = link.OuterHtml;
anchorTags.Add(att);
}
return anchorTags;
}
When I step through the code I see that the line:
string att = link.OuterHtml;
provides the entire < img node ... which is more than I want.
I would like anchorTags to have just the folder and name of the file, as in:
[0] = images/sample001.jpg
[1] = images/sample002.png
So, I need something other than .OuterHtml but cannot find it.
Can anyone help?
You are looking for the values of the src attributes of the image elements:
foreach (HtmlNode img in root.SelectNodes("//img"))
{
string att = img.Attributes["src"].Value;
anchorTags.Add(att);
}

Razor and Html.DropDownList: Setting selected values for a dropdown for each row in a list?

This seems like it should be simple, but I can't figure out how to make it work.
My data model has a "Server" table, and a "ServerType" table. PKs for both tables are ints, and Server has a field ServerTypeId which is a fk to ServerType.Id.
I have a Razor List.cshtml that is typed to IEnumerable:
#model IEnumerable<Server>
<table border="1">
<tr>
<th>
Server Type
</th>
<th>
Name
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DropDownListFor(modelItem => item.ServerTypeId, (IEnumerable<SelectListItem>)ViewData["ServerType"])
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
My controller has:
public ActionResult List()
{
var s = GetServers();
ViewData["ServerType"] = GetServerTypes();
return View("List", s);
}
private List<SelectListItem> GetServerTypes()
{
string id;
SelectListItem si;
List<SelectListItem> sl = new List<SelectListItem>();
IQueryable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (var item in items)
{
id = item.Id.ToString();
si = new SelectListItem { Value = id, Text = item.Description };
sl.Add(si);
}
return sl;
}
This displays the data, but the value in the dropdown is not selected. I've tried both Html.DropDownList and Html.DropDownListFor, with different permutation of names for the ViewData property, with and without the Id at the end.
Do I need to create a viewmodel that has copies of the ServerType in order to set the Selected property? Or is it a problem because my ids are ints, and the SelectItemList Value property is a string?
For anyone else still looking for he answer. I had to do 3 things to get this to work
User #for instead of #foreach. This is so it has an index to work
with when naming things.
Don't have the ViewBag variable name the
same as the property. It tries to help you out and binds things too
early if they have the same name.
Pass the current value in the constructor for SelectList. My
code ended up as:
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DropDownListFor(modelItem => Model[i].operatorToken,
new SelectList(ViewBag.operatorTokensList, "Value", "Text", Model[i].operatorToken),
"Select", htmlAttributes: new { #class = "form-control" })
...ect...
}
Notice the 4th parameter to SelectList() sets the selected value.
My operatorTokensList was valued with:
new[] { new { Value = ">", Text = ">" },
new { Value = ">=", Text = ">=" },
new { Value = "=", Text = "=" },
new { Value = "<=", Text = "<=" },
new { Value = "<", Text = "<" } };
(The user was selecting "greater than", "greater than or equal", etc.)
At no point in your population of the List in GetServerTypes() do you specify that any of the items are selected. This is something you need to do manually, as MVC3 isn't smart enough to infer it for you in the DropDownListFor method. This is further complicated by the fact that you are not using a single model.
A better way to do this might be:
(Keep in mind in the below code, I'm assuming that the Server class has a primary id called "Id")
For the controller code:
public ActionResult List()
{
IEnumerable<Server> s = GetServers();
ViewData["ServerTypes"] = GetServerTypes(s);
return View("List", s);
}
private Dictionary<int, SelectList> GetServerTypes(IEnumerable<Server> s)
{
Dictionary<int, SelectList> sl = new Dictionary<int, SelectList>();
IEnumerable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (Server srv in s) {
sl.Add(srv.Id, new SelectList(items, "Id", "Description", srv.ServerTypeId));
}
return sl;
}
For the view code:
(Also note below how the I've corrected the arguments used in the lambda functions)
#model IEnumerable<Server>
<table border="1">
<tr>
<th>
Server Type
</th>
<th>
Name
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DropDownListFor(modelItem => modelItem.ServerTypeId, (IEnumerable<SelectListItem>)(ViewData["ServerTypes"][item.Id]))
</td>
<td>
#Html.DisplayFor(modelItem => modelItem.Name)
</td>
</tr>
}
</table>
#TreyE correctly points out that you never specify that any particular select list item should be selected when displayed in the view.
There are several ways you can do this. First is to use the SelectList object and use its constructor that allows you to pass in the object that should be selected, it's the overload SelectList(IEnumerable, String, String, Object) MSDN SelectList.
SelectList is only supported on .NET 3.5+ though FYI.
Second, in GetServerTypes() you could write:
private List<SelectListItem> GetServerTypes()
{
List<SelectListItem> sl = new List<SelectListItem>();
IQueryable<ServerType> items = (from t in _entities.ServerTypes select t);
foreach (var item in items)
sl.add(new SelectListItem { Value = item.id, Text = item.Description, Selected = item.isSelected } );
return sl;
}
Also remember that only one item should be selected, so make sure that if you do try to use some boolean property it is not possible that more than one item could have its isSelected property set to true.
Alternatively, if you need to use some type of if statement to decide if Selected = true (i.e. your item has no isSelected boolean) then you can add that in the foreach loop.
foreach(var item in items)
{
if //condition
sl.Add(new SelectListItem { Value = item.id, Text = item.Description, Selected = true });
else
sl.Add(new SelectListItem { Value = item.id, Text = item.Description, Selected = false });
}

Replacing tags in HtmlAgility

I'm trying to replace all of my h1 tags with h2 tags and I'm using HtmlAgility pack.
I did this:
var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
//item.Replace??
}
}
and i got stuck there. I've tried this:
var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
HtmlNode newNode = new HtmlNode(HtmlNodeType.Element, doc, item.StreamPosition);
newNode.InnerHtml = item.InnerHtml;
// newNode suppose to set to h2
item.ParentNode.ReplaceChild(newNode, item);
}
}
problem there is that i have no idea how to create a new h2, get all the attributes etc.
i'm sure theres a simple way to do that, any ideas?
var headers = doc.DocumentNode.SelectNodes("//h1");
if (headers != null)
{
foreach (HtmlNode item in headers)
{
item.Name = "h2"
}
}
A similar approach replacing the tags using Descendants instead of SelectNodes:
IEnumerable<HtmlNode> tagDescendants = doc.DocumentNode.Descendants("h1");
foreach (HtmlNode htmlNode in tagDescendants)
{
htmlNode.Name = "h2";
}

Resources