I have this following Structure where the there are nested child components in Parent Component and i need to lay the focus on the Child component div programmatically.
I have reproduces this stackblitz for the above problem.
If you see the ts file, I have done the following
ngAfterViewInit(){
console.log(this.test);
this.test.toArray()[1].nativeElement.focus();
this.test.toArray()[1].nativeElement.firstElementChild.nativeElement.focus(); //this dosent work
}
The console.log returns me a query list of divs (in my original case a list of components) and in that I have a firstElementChild a (component) whose firstChildElement is a div I need to style and add focus on. But I am unable to do so as if you check the stacktrace it returns Cannot read property 'focus' of undefined.
In simple works the Structure:
ParentComponent -> viewChild gets me Query List of Child -> the array element has components -> each component has a firstElementChild -> property that has a div that I need to put focus on
UPDATE
Thanks all for your feedback and help really appreciated but now the thing is
It works but only if i give tab index for all the nested divs i was hoping that it trickles down the child the tab index. this is the problem. Any work around to this problem as this way i need to modify the template fro all my nested components in the broader sense
Try to change
this.test.toArray()[1].nativeElement.firstElementChild.nativeElement.focus(); //this dosent work
to
this.test.toArray()[1].nativeElement.firstElementChild.focus(); //this should work :-)
nativeElement.firstElementChild is NOT an ElementRef but already an HTMLElement => it doesn't have a property nativeElement.
Alternatively, children property of the HTML Element will also return a HTMLElementCollection as below,
this.test.toArray()[1].nativeElement.children[0].focus();
Updated Stackblitz
Updated template
<div>
<p> Welcome to the world of {{testHtml}} </p>
</div>
<div *ngFor = "let i of numbers; let ii = index" [attr.tabIndex] = "ii==00 ? 0 : null" #test>
<div>{{i}}
<div>
MY test
</div>
</div>
</div>
updated .ts file
ngAfterViewInit(){
this.test.toArray()[0].nativeElement.focus();
}
Note: You were using same tabindex for all the elements. You need to define tabindex for a selected one that you want to focus initally.
Related
I have a custom component in angular that i re-use everywere in my app. This is a button component and i call it like this where i want to use it: <app-delete-btn></app-delete-btn>
I want to set the attribute tabindex="1" to my component but it does not work.
This attribute gives a TAB order to specific html elements.
Upon inspecting this strange behaviour, and as of my understanding, tabindex works but you have to specify it for the parent and ALL the child components
So i did this and it worked:
Upon declaring my custom component in my html <app-delete-btn tabindex="1"></app-delete-btn> i gave him the tabindex
and then i had to add it in the app-delete-btn.ts button inside the component <button tabindex="1">Delete</button>
The problem is that i may re-use that button therefore i can't add the tabindex from within the component itself otherwise is going to apply everywhere i use it.
Finally my question is:
Is there a way when calling <app-delete-btn></app-delete-btn> to assing a tabindex property to all of it's childrens (and by childrens i mean the button delcared in the html of the component)?
Add this to your button :
#HostBinding('attr.tab-index')
tabIndex = 1;
This should do the exact same thing as this
<app-delete-btn tabindex="1"></app-delete-btn>
But automatically
Hi I am using an UI Library (forced to, company issue..) which provides an Angular component, which renders a form.
Now I want to disable all of the input fields an buttons inside this form. But the component of the library doesn't provide me the possibility to pass a parameter to change the status to read only.
Now I have no other option to do dirty DOM hacking. However it doesn't seem to work.
Here is my HTML of my own component, where I render the Library Component:
<component-of-the-library #formComponent></component-of-the-library>
Now inside my own components class I reference it:
#ViewChild('formComponent', {read: ElementRef}) formComponent: ElementRef;
However when I use the nativeElement feature and the querySelectorAll() function I don't see the button elements:
ngAfterViewInit() {
console.log(this.formComponent.nativeElement);
console.log(this.formComponent.nativeElement.querySelectorAll('button'))
}
The first line outputs the DOM of the library component. There I also see the buttons.
However the second line just returns an empty NodeList.
Am I missing something?
Instead of doing all these, come up with a div overlay and with the size of your form and make show or hide it based on your needs.
It will be easier than disabling each form inputs and buttons. Also the overlay is not the component but your div.
I able to read the DOM Nodes present in the child component from the parent Component using ViewChild() https://stackblitz.com/edit/angular-edmyur?file=src%2Fapp%2Fapp.component.ts
EDIT: I see another problem. AfterViewChecked gets called multiple times...
I found the answer myself. The problem is the LifeCycleHook. AfterViewInit works for your own component, but it doesn't wait for the child components to finish rendering.
When I use AfterViewChecked it works!
However I am still puzzled, that logging the nativeElement has always given me the correct DOM, even though it's still not rendered.
I have a multi-picklist component that houses a list of checkbox components. The parent component has a CSS :focus and :hover class for the mouseover. All controllable elements are in the tab index (so you can awkwardly use the keyboard to tab through the checkboxes).
Now we want to allow arrow-key navigation in the multi-picklist. Basically I need to use #ViewChild to grab the element I need and set .focus() to it. And that's exactly what I did in a different component, and that worked. However, here it does not work. The only difference is that in the component where it works my *ngFor loop iterates over a list of divs and in this case it does so over a list of components.
So what this boils down to is that I can't get the :focus pseudo class to stick to my component.The following code is not complete, just trying to get the first element focused. Sort of PoC.
MultiPicklist.component.html:
<div (keydown.ArrowDown)="arrowDownHit($event)">
<div [hidden]="multiPicklistState === 'collapsed'" (keydown.Escape)="closeBox()" #multiPicklist>
<app-checkbox
#checkboxItems
class="multi-picklist-checkbox"
*ngFor="let item of picklistItemList; trackBy: trackByFunc; let i = index;"
[fieldName]="item.value"
[label]="item.label"
[chckbxId]="'multiPicklistChkbx'+title+i"
[tooltip]="item.tooltip"
[isChecked]="item.isChecked"
(checkboxChanged)="inputChanged($event)">
</app-checkbox>
</div>
</div>
MultiPicklist.component.scss:
.multi-picklist-checkbox:hover, .multi-picklist-checkbox:focus{
background-color: $multi-picklist-item-hover-bg-color;
}
MultiPicklist.component.ts:
arrowDownHit(ev){
ev.preventDefault();
this.multiPicklist.nativeElement.firstElementChild.focus();
}
It's worth mentioning that the :hover pseudo class works, hovering the mouse over the component gives it the correct background color. But not .focus(). Furthermore, no error is thrown,.
This is just a workaround, not an actual answer or solution.
I was able to set the focus of an HTML element inside the component. It resulted in an ugly little line of code:
this.multiPicklist.nativeElement.firstElementChild.lastElementChild.focus();
Of course in an ideal universe I'd be able to just apply the focus to the entire component, or whatever div the browser ends up seeing.
For example I have a function itemDragged(). How inside that function to get a reference of ion-item-sliding to get its current class attribute?
<ion-item-sliding *ngFor="let activity of activities" (click)="showModalInfo(activity)" (ionDrag)="itemDragged($event)">
You can use reference variable in template
<ion-item-sliding #iis *ngFor="let activity of activities" (click)="showModalInfo(activity)" (ionDrag)="itemDragged(iis)">
<p>{{iis.class}}</p>
But digging in the event class you can get the class like this
itemDragged(event){
console.log(event._elementRef.nativeElement.className);
}
Would you use a ViewChild, and get the nativeElement?
In the component API, note that the key is how you will access it in your code, the value passed to ViewChild is the "#" id you gave the component (this is ES6, in TS you'd use the ViewChild annotation):
...
queries { 'myElement' : new ViewChild ( 'myelement' ) }
...
In the markup:
<div #myelement></div>
In your component function (a handler, wherever you need to grab it), the nativeElement property gives you a reference to the HTML element, so you can set innerHTML, add a handler, or whatever else you need to do. There's of course other ways to do this by binding to properties (e.g. you can bind to (click) or [innerHTML]):
...
this.myElement.nativeElement... // etc.
...
Alternatively, if you wanted to use a single handler for multiple elements (say, a bunch of different clickable divs) you could use event.target to do the same thing. The problem with that is, because giving IDs is considered "bad" these days, you have to have an alternative way of IDing which DIV got clicked. You can check the label (innerHTML), you can give it a dummy style and check for that, etc.
<div id="divWrapper">
<input id="firstInput"/>
<div id="insideDiv">
<input id="secondInput"/>
</div>
</div>
This is the basic structure of my HTML. So, given an access to "divWrapper" element how do I check if it contains "secondInput".
NOTE: This is to be done without jQuery. Only using Angularjs utility functions.
UPDATE: The solution should be dynamic. Meaning it should find any child, how much ever down the level it is in the DOM tree.
UPDATE 2: Please don't get misguided by "input" element. I don't want to track the text inside it. I want to a logic which will check if me or any of my child is clicked. If NOT then hide myself and my children.
var secondInput = angular.element(document.querySelector("#divWrapper #insideDiv #secondInput"));
This will return the element you're looking for or empty if it doesn't find anything.
angular.element("#someId").find("someOtherSelector") already does exactly what you want.
So for your question you could just do...
angular.element("#divWrapper").find("#secondInput"); // Empty array OR a match