How do I programmatically add children to the children of the parent Node using Angular Tree component - treeview

I am having a problem using angular tree component. I believe that this problem is simple but I can quite wrap my head around it. I can't seem to be able to add child nodes to the children of the parent node. That is the grandchildren of the root node and so on. I will like to add great grandchildren and so on but I am still struggling with adding grand children. If anybody has seen integralUiTreeview on lidorsystems. Then you can understand. Here is the link to the example https://www.lidorsystems.com/support/articles/angular/treeview/treeview-add-items-dynamically/
of what I am trying to do. Looking around all over the internet I came to learn that this isn't very easy to do but I am using angular tree component and all I just want to know is how to add children with just a click of the button. I have succeeded in adding children to a root node but only to the first root. What I will like to know is how to add a child to whatever node I want to and possibly how to delete it too. I believe I can take care of the editing part myself. If lidorsystems was free I would have used it. How can I do this?
Here is the code that I used to add the root nodes
createRootNode() {
this.mainQuestion = this.surveyForm.get('firstQuestion').value;
this.nodes.push({name: this.mainQuestion, children: []});
console.log(this.nodes);
this.tree.treeModel.update();
}
while this is the one for the child node for the first root. Though it's all conventional:
addNode(tree) {
this.nodes[0].children.push({
name: 'a new child',
children: []
});
tree.treeModel.update();
}
And here is the html:
<div >
<tree-root class="tree" #tree [nodes]="nodes" [focused]="true" [options]="options">
<ng-template #treeNodeTemplate let-node>
<span title="{{node.data.name}}">{{ node.data.name }}</span>
<span class="pull-right">{{ childrenCount(node) }}</span>
<button (click)="addNode(tree)">add</button>
</ng-template>
</tree-root>
</div>

For anyone who wants to use angular tree component to create a tree view this is the basic process of adding and deleting nodes from the tree:
Adding - Ts file :
addNode(parent: TreeNode) {
this.tree.treeModel.setFocus(true);
const value = {
name: 'a new child',
children: []
};
if (parent) {
parent.data.children.push(value);
}
this.tree.treeModel.update();
}
Html file :
<div >
<tree-root class="tree" #tree [nodes]="nodes" [focused]="true" [options]="options">
<ng-template #treeNodeTemplate let-node>
<span title="{{node.data.name}}">{{ node.data.name }}</span>
<span class="pull-right">{{ childrenCount(node) }}</span>
<button mat-icon-button (click)="addNode(node)" aria-label="Icon-button with an add icon">
<mat-icon>add</mat-icon>
</button>
<button mat-icon-button (click)="removeNode(node)" aria-label="Icon-button with a delete icon">
<mat-icon>close</mat-icon>
</button>
</ng-template>
</tree-root>
For deleting nodes :
removeNode(node: TreeNode): void {
if (node.parent != null) {
node.parent.data.children.splice(node.parent.data.children.indexOf(node.data), 1);
this.tree.treeModel.update();
}
}
The Html for deleting nodes is written above. If you are curious about the children count function written above here is the code:
childrenCount(node: TreeNode): string {
return node && node.children ? `(${node.children.length})` : '';
}
you can also go to https://github.com/500tech/angular-tree-component/issues/432 to browse issues that have to do with adding and removing nodes. Most solutions are written there though the community isn't very big or very active. I hope this helps. If I find a solution for editing I will post it here too.

Related

Cypress: Check if an element contains a specific other element

I need to check a div to see if it contains an 'a' (link). I've been looking at similar questions here, but none seem to do exactly what I'm trying.
Example markup:
<div if='description>
blablablabla
blablablabla
blablablabla
</div>
<div id='description'>
blablablabla
The link
blablablabla
</div>
<div id='description>
blablablabla
blablablabla
blablablabla
</div>
The ids are duplicated, and there's nothing I can do about that. However, using .eq() in these cases seems to work fine.
What I need to do, is to check, for instance, cy.get('#description').eq(0) to see if there is an 'a' within it, and click it if there is. If there is no 'a', do nothing.
I've tried things like this, without success:
if (cy.get('#description').eq(0).contains('a')) {
cy.get('#description').eq(0).within( () => {
cy.get('a').click();
});
Probably incorrect code anyway, with regards to the use of within.
This, too:
if (cy.get('#description').eq(0).children().contains('a')) {
cy.get('#description').eq(0).within( () => {
cy.get('a').click();
});
All help and/or hints are, as allways, appreciated. (Man, I miss Selenium sometimes, where I could use Java or Kotlin code with near full freedom.)
You can use children for this as mentioned by #lbsn and then add click().
cy.get('#description').eq(0).children('a').click()
You can also look into this, like when you get the a then click on it and exit each() and if there is no child element, the test continues and doesn't fail.
cy.get('div#description').each(($ele) => {
if ($ele.children('a').length > 0) {
cy.wrap($ele).children('a').click()
return false //Remove if you want to continue the loop even after finding the child element
}
})

Cypress how to get all children element from the parent element but no grand children

Cypress how to get all children element from the parent element but not grand children
getting those two button directly from the body but not the sub/grand child(ren) from the form
For example
<body>
<button></button>
.
.
.
<button></button>
<form>
<button></button>
.
.
.
</form>
</body>
It should be as simple as specifying the direct descendants only with parent > child selector
Please try this
cy.get('body > button')
.its('length')
.should('eq', 2)
You need to be specific about the children you are looking for,
cy.get('body').children('button')
for instance,
<body>
<button>C1</button>
<button>C2</button>
<div>D</div>
<form>
<button>GC</button>
</form>
</body>
cy.get('body').children('button')
.invoke('text')
.should('eq', 'C1C2')
Checking immediate parent
I was also looking for a way to specify the parent is <body>.
There's no native Cypress way, but you can add a jQuery expression
Cypress.$.expr[":"].parentIs = function(el, idx, selector) {
return Cypress.$(el).parent().is(selector[selector.length - 1]);
}
cy.get('button')
.filter(':parentIs(body)')
You can apply a combination of children() and not to get the two children instead of three.
cy.get('body').children('button').not('form')
In case you want to get specific children element you can use the .eq() command:
cy.get('body').children('button').eq(0) //yields first button
cy.get('body').children('button').eq(1) //yields second button
You can also use filter() if your buttons have unique texts. This in my opinion is a much cleaner way-
cy.get('body').find('button').filter(':contains("buttontText")')

AlpineJS - autoselection doesn't work when there is more than one todo

I'm making a little todo app in AlpineJS and I'm running into a problem of autoselecting a field when a user clicks on a todo. This problem only happens when there are multiple todos.
When the user enters the first todo onto the app, they can then click on the todo to edit it. The field will autoselect as expected. However, when the user adds more than one todo onto the app and wants to edit the n+1 todo, the field will no longer autoselects, but the first todo still autoselects just fine.
Here's my CodePen for your reference.
Here's what my edit functionality looks like:
JS
[...]
edit(todo) {
todo.editing = !todo.editing
this.$nextTick(() => {
document.getElementById(`edit-${todo.id}`).select()
})
}
[...]
HTML
[...]
<div
x-text="todo.item"
class="todo-item"
#click="edit(todo)" // <-- point of interest
x-show="!todo.editing"
:class="{ 'completed' : todo.completed }"
>
</div>
<input
type="text"
x-show="todo.editing"
:value="todo.item"
x-model="todo.item"
#keydown.enter="edit(todo)"
#keydown.escape="todo.editing = false"
#click.away="todo.editing = false"
class="edit-input"
:id="`edit-${todo.id}`" // <-- point of interest
/>
[...]
Would anyone have an idea of what might be wrong with my logic? Thank you for the help!
It seems using setTimeout(() => { ... }, 1) (timeout of 1ms) works, see the following Codepen modified from yours.
There are known issues in Alpine.js around $nextTick.
I would also recommend using x-ref instead of ids, so your input would look as follows:
<input
type="text"
x-show="todo.editing"
:value="todo.item"
x-model="todo.item"
#keydown.enter="edit(todo)"
#keydown.escape="todo.editing = false"
#click.away="todo.editing = false"
class="edit-input"
:x-ref="`edit-${todo.id}`"
/>
You can then set edit(todo) to be:
edit(todo) {
todo.editing = !todo.editing
setTimeout(() => {
this.$refs[`edit-${todo.id}`].select()
}, 1)
}

Need waypoints repeating on multiple items

Here's my code, lifted from waypoints docs:
var sticky = new Waypoint.Sticky({
element: $('.objectheader')[0]
})
<div class="object" id="object2">
<div class="objectheader" id="header2">Header Item 2</div>
<div class="objectbody" id="body2"><img src="images/samplechart.png" /></div>
<div class="clear"></div>
</div>
Basically, I have multiple objects (the list will grow, so hard coding for each ID isn't an option) that each contain an objectheader and objectbody. Every time I hit an objectheader, I want it to apply the sticky class and stick at the top until it reaches a new one, however it's only working on the first object. I know I'm missing something simple here...
with [0] you are specifying the very first element. You need to iterate over them to add a waypoint to each element...
var sticky = [];
$('.objectheader').each(function(idx){
sticky[idx] = new Waypoint.Sticky({ element: this });
});
then sticky will be an array of all your waypoint objects.

How to perform click event on an element present in the anchor tag?

<div class="buttonClear_bottomRight">
<div class="buttonBlueOnWhite">
<a onclick="$find('{0}').close(true); callPostBackFromAlert();" href="#">Ok</a><div
class='rightImg'>
</div>
</div>
</div>
In the above code i wanted to click on Ok button present in the anchor tag.But an id is not generated because of which i cannot directly perform a click action. I tried a work around mentioned below.
IElementContainer elm_container = (IElementContainer)pw.Element(Find.ByClass(classname));
foreach (Element element in elm_container.Elements)
{
if (element.TagName.ToString().ToUpper() == "A")
{
element.Click();
}
}
But here elm_container returns null for intial instances due to which we cannot traverse through it. Is there any other easy method to do it ?
Try this...
Div div = browser.Div(Find.ByClass("buttonClear_bottomRight")).Div(Find.ByClass("buttonBlueOnWhite"));
Debug.Assert(div.Exists);
Link link = div.Link(lnk => lnk.GetAttributeValue("onclick").ToLower().Contains(".close(true)"));
Debug.Assert(link.Exists);
link.Click();
Hope it helps!
You can simply Click on the link by finding its text
var OkButton = Browser.Link(Find.ByText("Ok"));
if(!OkButton.Exists)
{
\\Log error here
}
OkButton.Click();
Browser.WaitForCompplete();
Or you can find the div containing the link like,
var ContainerDiv = Browser.Div(Find.ByClass("buttonBlueOnWhite"));
if(!ContainerDiv.Exists)
{
\\Log error here
}
ContainerDiv.Links.First().Click();
Browser.WaitForComplete();

Resources