How to print a tree using Razor - html

I'm trying to print a simple HTML tree structure, consisting of ul and li elements. I want to be able to pass the view an IEnumerable<T> where T has some hiearchy information (e.g. parent). Now I want the view to output the Tree control much like ASP.NET's Tree used to work. Is there any way to do this in MVC3 using Razor?

I've so far ended up doing it like this:
#PrintCategoryTree(Model.Where(x => !x.ParentCategoryID.HasValue))
#functions{
public IHtmlString PrintCategoryTree(IEnumerable<Aurora.Models.Category> levelCategories) {
if (levelCategories.Count() == 0) { return new HtmlString(String.Empty); }
System.Text.StringBuilder sb = new System.Text.StringBuilder();
TagBuilder childBuilder = new TagBuilder("li");
foreach(var item in levelCategories.OrderBy(x => x.Name)) {
childBuilder.Attributes.Clear();
childBuilder.Attributes.Add("id", item.CategoryID.ToString("N"))
var sub = PrintCategoryTree(Model.Where(x => x.ParentCategoryID == item.CategoryID));
childBuilder.InnerHtml = item.Name + sub.ToString();
sb.AppendLine(childBuilder.ToString());
}
TagBuilder tagBuilder = new TagBuilder("ul")
{
InnerHtml = sb.ToString()
};
return Html.Raw(tagBuilder.ToString());
}
}
The reason being, this is still in the Razor View. And I can keep my presentation logic in my view. It's not exactly what I'd hoped, but I thought I'd share it with you guys here anyway.

Sure it's possible. :) You can acctually go about this in a few ways.
Use something like jsTree and only output the first level of the tree. When a user expands a node, jsTree issues an AJAX callback to get more, and that's just a matter of loading the nodes underneath whatever they opened. I know that's not exactly what you asked, but I wanted to mention it.
If you can either modify the query or do a bit of pre-processing on the data before passing it to razor, change each item in the IEnumberable so that it also includes it's "level" in the tree (1 for a root node, 2 for it's child, 3 for a child of a child, etc). Outputting it at that point is pretty easy. Create a variable in the view holding the current level. When you go to the next row, check if the new level is the same as the old one. If it's not, either open or close enough <ul> tags that you get to the right one for that element.
If you can't do that either, you'll need to keep track of the nodes as you see them in razor. The reason why is that when you find a child from a node that isn't the last one you saw, you'll need to get that node back to figure out how many </ul> tags you need to add to get to the right level. Off the top of my head you could do that by having the view create a Hashtable with the row's key and level for each row you hit. Then when you hit an element and don't know where to put it, look up its parent in the hashtable (since you'll have already seen the parent assuming these are ordered correctly).
Far as I'm aware there's no "display this blob of stuff as a tree" command, so you need to write some logic to get the number of tags to build the levels correct. But hopefully that will help you get started. :)

Related

Select parent of XCUIElement

How can I select the parent element of an XCUIElement in XCUITest? According to the documentation, the class has children() and descendants() but nothing to select parents or siblings. It seems to me I must be missing something - how can Apple have an element tree without navigation in both directions???
I know there is a method containing() on XCUIElementQuery but that is not the same. I also know that an accessibilityIdentifier might help but I am thinking of writing a generic method for testing any view with a given Navbar label. Passing in all the identifiers of all the elements I would like to access does not seem like a good option.
Unfortunately there is no direct named method to access parent elements similar to children() and descendants() provided by Apple but you were actually on the right track with containing(). There are two ways I usually approach it when I need to locate a parent element based on children/descendants:
Using containing(_:identifier:)
let parentElement = app.otherElements.containing(.textField, identifier: "test").firstMatch
or
let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 0)
Using containing(_ predicate: NSPredicate)
let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").firstMatch
or
let parentElement = app.otherElements.containing(NSPredicate(format: "label CONTAINS[c] 'test'").element(boundBy: 0)
These are just examples with random data/element types because you didn't mention exactly what you want to achieve but you can go from there.
Update:
As usual the Apple documentation doesn't do a good service. They say 'descendants' but what they actually mean is both direct descendants(children) and non-direct descendants(descendants). Unfortunately there is no guarantee and there is no generic solution. It should be based on your current needs and the application implementation. More examples that could be useful:
If you don't want the first element from the query you are better off using element(boundBy: index). So if you know that XCUIElementQuery will give you 5 elements and you know you need the 3rd one:
let parentElement = app.otherElements.containing(.textField, identifier: "test").element(boundBy: 2)
Fine graining of your element locators. Lets say you have 3 views with identifier "SomeView", these 3 views each contain 2 other subviews and the subviews have a button with identifier "SomeButton".
let parentViews = app.otherElements.matching(identifier: "SomeView")
let subView = parentViews.element(boundBy: 2).otherElements.containing(.button, identifier: "SomeButton").element(boundBy: 1)
This will give you the second subview containing a button with identifier "SomeButton" from the third parent view with identifier "SomeView". Using such an approach you can fine tune until you get exactly what you need and not all parents, grandparents, great-grandparents etc.
I wish Apple provided a bit more flexibility for the locators with XCTest like Xpath does for Appium but even these tools can be sufficient most of the time.

How to know if Perl Mojo::DOM::find returns or matches any DOM Element

Am currently parsing a series of webpages with Mojo::DOM and the only criterion for me to proceed down the web page is if there's an element found within.
I have my DOM object built like this:
my $urlMJ = Mojo::URL->new($entry->link);
my $tx = $ua->get($urlMJ);
my $base = $tx->req->url;
my $dom = $tx->res->dom;
my $divVideo = $dom->find('div#searchforme');
My question is, how do I know if $divVideo is empty?
I realise that from this question on google groups and grokbase answered by SRI (Riedel), if find doesn't match any element, it returns (if I get it correctly) the DOM object collection initiating the find and an empty DOM collection, which happens to be the result.
I thought of using an each to get to the empty DOM collection within, but won't the DOM returned contain the initial DOM structure?
I have tried using if (defined($divVideo)) , I also tried dumping with print Dumper($divVideo). All it returned was $VAR1 = bless( [], 'Mojo::Collection' );
I tried $dom->find('div#searchforme')->size , return values was 0 and even for those web pages that didn't fall into this category.
Can somebody please help me out?
Is my approach to this wrong?
if find doesn't match any element, it returns (if I get it correctly) the DOM object collection initiating the find and an empty DOM collection, which happens to be the result.
You're misunderstanding find. It returns just a Mojo::Collection of Mojo::DOM objects that represent each matching element in the page. Nothing else. So if no matches are found, just an empty collection is returned
This object has a size method, so you can say
my $divColln = $dom->find('div#searchforme');
if ( $divColln->size > 0 ) {
...
}
Alternatively you could use the each method to convert the collection into a list, and assign it to an array like this
my #divColln = $dom->find('div#searchforme')->each;
if ( #divColln ) {
...
}
Or if you are expecting to find just one such element (which it looks like you're doing here) then you can just pick the first item from the collection, like this
my $divVideo = $dom->find('div#searchforme')->[0];
if ( $divVideo ) {
...
}

Making HTML elements access a dynamically generated value

So I am making a data entry program where the user presses buttons to generate new inputs (numbers text etc.) and when finished the lists are generally between 100-10000 items.
The program has been coming along well, but now I am at a point where one set of data entered must generate the coices for an array [1,2, . . .] which is part of a later set of data.
So what I have done is setup buttons with the ID based on the earlier inputs. (The whole data set is saved as a JSON)
And what I want to do is when the button is pressed it looks pressed and writes to an HTML element the ID of the button which will later be read and saved to JSON.
My problem is centered on getting the correct information back to the user.
function doStuff(container){
for (var u = 0, c = someJSON.length; u < c; u++){
var someButton = document.createElement('button');
someButton.id = someJSON.id;
someButton.className = 'someButton';
someButton.onclick = function() {
writeIDToHTML(container,someButton,someButton.id);
}
container.appendChild(someButton);
}
}
function writeIDToHTML(container,theButton,theID){
console.log("theID")
console.dir(theID)
}
This prints only the last ID in the loop. How do I get each ID to each button?
The other thing to do is to give the button a pressed look.
Bonus points if it is reversable.
You should not add a listener on each element. The way to do it is adding a listener on the container and get the id of the clicked event (via event.target). This is called event delegation.
I could explain it, but this guys made a perfect answer to your question : http://davidwalsh.name/event-delegate
Btw, you should consider using a library like jquery to manipulate your DOM. It implements event delegation and advanced cross browser DOM manipulation utilities. For instance, you would not need to add a 'container' property since you can access it by the parent() method.

Can not clear the JList Java

I am using NETBEANS 7 . I have on JList in my project. Here is my code
private void dateChooserSelectedDatePropertyChange(java.beans.PropertyChangeEvent evt) {
DefaultListModel model = new DefaultListModel();
listSerialNo = new JList(model);
UpdatePurchase updatePurchase = new UpdatePurchase();
Vector<String> serialNo = new Vector<String>();
serialNo = updatePurchase.getSerialNo(date);
if(serialNo.size()>0){
listSerialNo.setListData(serialNo);
}
else{
model.clear();
this.resetFields();
}
}
If I write above code then list doesn't show any thing & its not clearing the JList. But If I remove
DefaultListModel model = new DefaultListModel();
listSerialNo = new JList(model);
then list populates with some data from vector serialNo.
My problem:
I want to clear the contents of list if date value changed & no records are present for selected date.
Also I want to clear the contents of list when I click "Reset" button on my form.
Basically I can not clear the contents of jList. Please guide me. Thanks.
Try this-
listSerialNo.setListData(new Vector());
This will re-set the list data with a empty vector object.
so all the list data will be removed.
You probably already have an JList component added which is visible. In your code you create a new one while overriding the field containing the old one. Therefore you lost the 'connection' to the old (and visible) one. By updating the new one nothing visible will change as you update the wrong one.
By removing those 2 lines, you are not creating a new JList anymore and are updating the correct one.
So why not just remove those 2 lines as that seems to work according to your post.
The method setListData actually replaces the data model of your JList. Thus you do not clear the current list model but the initially assigned (which is no longer under use). Therefore, do not use setListData but add the new elements directly to your original model.

retrieving a variable value from lower level

well i created some variables in the main stage level, with something like this:
for(i=0,i<10,i++){
var var_name="var_num_"+i;
this[var_name]="some value";
}//<-----------------------------------------------------works
so i get 10 variables named "var_num0", "var_num1", "var_num2" each one with some value.
and i can acces them any where calling this
var second_var=MovieClip(root).var_num0;//<--------------works
my problem comes when i want to call all the variables from a lower level or in another frame or somewhere else using another loop:
var third_var;
for(j=0,j<3,j++){
third_var=this["MovieClip(root).var_num_"+j];//<---------DOSNT WORK
trace(this["MovieClip(root).var_num_"+j]);//<------------returns "undefined"
}
how can i make this work? i tried a lot of things and nothing...
thanks you all
In your case both "root" and "this" are the scope you want to access the vars from. so try this:
var third_var:MovieClip;
for(j = 0; j < 3; j++)
{
third_var = MovieClip(root)[var_num_ + j];
trace(third_var);
}
Also you should have semi-colons in your for loop rather than comers.
I'd like to preface my answer with a suggestion you use a 'Document Class' with AS3 to make things like namespaces and inheritance much clearer. You know exactly where things are accessible when using document based, object oriented programming versus the timeline programming available through the Flash IDE (its only there because of AS1/2). Tut: http://www.kirupa.com/forum/showthread.php?223798-ActionScript-3-Tip-of-the-Day/page14
On to the answer: You are trying to move two levels of inheritance in one set of [] Another way of writing your first "Doesn't work" line is:
this.myMovieClip["var_num"+j"];
You could also use: this["MovieClip"]["var_num"+j];
Basically, you need to take the "MovieClip(root)" out of the string you are using to call your variable because you are passing through two levels of inheritance: this->MovieClip->targetVar
You need to use two periods, a period and a set square bracket or two sets square brackets to move two levels of inheritance. A period . and a set of square brackets [] both accomplish the task of moving one level deeper, so putting the . inside the string used to call up your variable won't work.
Explanation:
The following three examples all return the same variable:
myMovieClip.my_variable
myMovieClip["my_variable"]
var str:String = "my_variable";
myMovieClip[str];