QuerySelector is part of typescript or javascript. Can I use this in my angular application Which one needs to use instead of QuerySelector in angular application. Why I am asking, Suppose generating dynamic HTML on that condition I want to handle some event on the dynamically generated HTML.
On that condition, querySelector is very easy. But some people are telling like do not to use quesrySelector that is not good practice. So, How to handle these issue?
document.querySelector(".myclass");
document.querySelector("ul").querySelectorAll('li');
In order to dynamically render content you can use structural directive like *ngIf
<div>
<p *ngIf="displayElement; else showThis">Content</p>
<ng-template #showThis>
else content
</ng-template>
</div>
querySelector it's not a good practice in Angular.
Instead of using querySelector or similar from there, a declarative way can be used instead to access elements in the view directly:
<input #myname> // html
#ViewChild('myname') input; // add this in ClassComponent
ngAfterViewInit() {
console.log(this.input.nativeElement.value); // Input will be available in this lifecycle
}
Related
I am trying to figure out how to automatic trigger a click event on certain element after all data are loaded.
the code is like this in html file:
<div *ngFor="let location of locations; let i = index;" class="location-wrapper" (click)="onSelect($event, i); $event.stopPropagation();">
<div class="location">{{ location }}</div>
</div>
the onSelect() method is doing some expansion of something that related to current location.
What I am trying to achieve is that I want the very first element of the *ngFor can be automatically clicked to show the things that related to it every time I get to this page.
Or maybe we can achieve it using other similar approach?
I have tried several ways to do this,
like putting some code in window.on('load', function() { // blablabla });
or using ngAfterViewInit() and ngAfterViewChecked(), both not work well.
You can do this in at least 2 ways. The first one would be old-fashioned javascript click(). The second would be just using component logic, just create an variable like selectedLocation which would hold current index or Object that is currently expanded. Don't forget to add initial value to it to make it after load page visible.
Javascript dispatchEvent (not Angular friendly solution)
Simply we just need to grab our item and use click() function. That's it. To grab an element we can use basic javascript method document.getElementById(elementId)" or with template variable.
<div
[id]="'location_' + i" <!-- For biding with document.getElementById -->
#locationPanel <!-- For biding with template variable -->
*ngFor="let location of locations; let i = index;" class="location-wrapper" (click)="onSelect($event, i); $event.stopPropagation();">
<div class="location">{{ location }}</div>
</div>
With Id it would look like document.getElementById("location_0").click() this gonna dispatch click event on element.
For template variable in your component you need to add
#ViewChildren locationPanel: QueryList<any>;
openFirstLocation() {
if (this.locationPanel && this.locationPanel.first)
this.locationPanel.first.nativeElement.click();
}
And in afterViewInit just call this.openFirstLocation();
Please note that it's not Angular friendly because Angular does not like when you interfere directly with DOM. However as long we don't change anything in structures then everything should be fine, but we should avoid manipulating dom with plain javascript.
Please note that too about using #ViewChild and document.* methods.
Use this API as the last resort when direct access to DOM is needed. Use templating and data-binding provided by Angular instead. Alternatively you can take a look at Renderer2 which provides API that can safely be used even when direct access to native elements is not supported.
Relying on direct DOM access creates tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker.
From Angular docs link
Angular has these for DOM manipulation but I'm contemplating using jQuery for these functions: closest()/children().
So,
Is there an angular function that is identical to jQuery's function closest() which basically just returns the element?
Are there any alternatives for these functions in Angular without resorting writing native JS/TS operations to achieve those things just like jQuery, which does the dirty work? Or is alright to proceed with jQuery (I'm not dealing with template manipulation anyway)? Thanks.
Update
The window.getComputerStyle could easily replicate jquery's css().
Regarding jquery's selector: closest()
<div id="ample" #zero>
<div id="xample" #first>
<div #x>
</div>
<div #y>
</div>
</div>
</div>
I would like to know what could be an alternative operation for Angular. I could only think of making a function that traverses upward which jquery already does -give or take the DIVs and the element id are dynamically added/changed/deleted.
For example, from #y I want to know its nearest parent with an ID of ample. We know that the target element has a selector of #zero but retrieving it from div #y will take DOM traversal (which jquery simplifies using closest()) or some mapping/array operations that sorts out relations of the element which may be arduous in some point.
Is there is, one should avoid using jquery in Angular, here is an example how:
#Component({
selector: 'sample',
template: `
<span #tref>I am span</span>
`
})
export class SampleComponent implements AfterViewInit {
#ViewChild("tref") tref: ElementRef;
ngAfterViewInit(): void {
// outputs `I am span`
console.log(this.tref.nativeElement);
}
}
The keywords for you to search would be Elementref and Viewchild
JQuery is very powerful compared to what Angular is exposing for DOM manipulation. Sometimes it's very useful and very hard to ignore in an Angular application.
You can install it in your project but the only problem is that jQuery can access the DOM globally without encapsulation, so if you want to use good practices you should only access/modify DOM of the component you are using jQuery in, if you want to access/modify DOM outside of a component, you should pass needed data as #Input or use events to notify other components as #Output.
Do not forget to declare the variable $ in your component if you have installed it globally because typescript does not know what $ is.
declare var $ :any;
Using Jquery is not the angular way, but there are certain times where our hands are tied, only in those situations use jQuery. Use #viewchild or #viewchildren to get the DOM element.
span My name is XXX #name >
In .ts file
#ViewChildren('name') components: QueryList;
console.log(this.components.toArray());
You will be able to get the DOM. If you want only DOm element use #viewchild. In case of #viewchildren it will return an array of all the DOM elements which use the same selector or template reference variable.
Is there a way to execute angular2 component from JavaScript and get generated HTML in return?
I would like to write a function in plain JavaScript/JQUERY which should execute angular2 component and returns generated HTML.
For example: something like
var html = executeAngular2ComponentAndReturnHtml('componentName');
Any help will be highly appreciated.
Thanks
You can use the innerHTML attribute to do that. Bind it to a property of your component class like this:
<div [innerHTML]="customHtml"> // note the casing of innerHTML.
</div>
In your Component code:
this.customHtml = executeAngular2ComponentAndReturnHtml('componentName');
Important: You may also want to mark your HTML as trusted so that Angular2's DOM sanitizer does not remove parts of it. For this, please see angular 2 html binding
I am writing a handler to do something to some element when a user clicks on a different element.
At first I had the following (This is using Angular2 but I suspect the only different is how the onclick event is handled):
<span>
<input type="text" id="bioName">
</span>
<span class="icon-pencil" (click)="editField(bioName);"></span>
...but this didn't work. I then found an example that identified the input field differently, and this did work for me. It was as follows:
<span>
<input type="text" #bioName>
</span>
<span class="icon-pencil" (click)="editField(bioName);"></span>
Unfortunately I can't find anything that explains this. Searching for "hash" and "pound" with HTML and Javascript yield too many results that have nothing to do with this in the subject area.
So what does # do in this case? Can id not be used to obtain a reference to the DOM element when setting up an event handler? What is this called so I can google it and read the appropriate documentation?
This is Angular2 specific, regular HTML doesn't recognize this syntax, i.e. you have to use id="bioName" in order to access the tag with CSS/JavaScript.
Here is more information on how to use # in Angular2.
Hash (#) is syntax for defining template variable in Angular 2 templates. It is used to assign unique identifiers to template elements which you can later use to get a reference to template elements in component. In your case, for example, you can use bioName variable to get a reference to input element in your component and you can do whatever you want with it there - get file name, file size or even file itself. This is done using ViewChild decorator. You can check out answer I wrote few days ago and see what template variables are mostly used for.
It seems that getting an element in AngularJS is a bad idea, i.e. doing something like:
$('.myElement')
in say, a controller is not an angular way of doing things.
Now my question is, how should I get something in angular?
Right now, what I'm doing (and is an accepted way of doing it) is by watching a variable, and my directive does something based on it.
scope.$watch('varToWatch', function (varToWatch) {
if(attrs.id == varToWatch)
{
//Run my Directive specific code
}
});
However, while this particular design works for most cases, watch is an expensive operation, and having lots of directives watching can really slow down your application.
TL:DR - What is an angular way of getting a directive based on a variable on the directive? (like the one above)?
If you want to get/set values you don't need to fetch the element using jQuery. Angular data binding is the way to do it.
directives is the way to go if you want to do animations or any kind of element attributes and DOM manipulation.
Your code is basically right; the directive should watch something in the $scope and perform it's logic when that thing changes. Yes, watch statements are expensive, and that is a problem once your number of watches start to approach ~2000.
Looking at your code though, I see one problem:
The variable $scope.varToWatch references an id in the template.
When this variable changes, you want something to happen to the element which has this id.
The problem here is in the first point: The controller should know nothing about the DOM, including the id of any element. You should find another way to handle this, for example:
<div my-directive="one"> ... </div>
<div my-directive="two"> ... </div>
<div my-directive="three"> ... </div>
...etc
And in your directive:
scope.$watch('varToWatch', function (varToWatch) {
if(attrs.myDirective == varToWatch)
{
// Run my Directive specific code
}
});
You are very vague as to what you're trying to achieve, but I'll try to answer in context of your last comment.
I have a lot of the same directives (therefore the code will run on all of them), but I need to get only one directive from the lot.
You talk a lot about getting the right element. The directive element is passed to the link function in the directive. If you are not using this element (or children of it) directly, but rather trying to search for the element you want somehow, you are most likely approaching the problem the wrong way.
There are several ways to solve this, I'm sure. If you're thinking about animations, there is already support for that in Angular, so please don't try reinvent the wheel yourself. For other logic, here are two suggestions:
Secondary directive
If the logic you want to apply to this directive is generic, i.e. it could be applied to other directives in your application, you could create a new directive which works together with directives. You can set prioritization in directive in order to control which directive is executed first.
<main-directive ... helper-directive="{{condition_for_applying_logic}}"></main-directive>
jsFiddle example
Expanding main directive
If the logic is tightly coupled to this directive, you can just create a new attribute, either dynamic or static, and bind to it in the directive. Instead of checking 'attrs.id == varToWatch', you check if $scope.apply-logic === 'true' and apply the logic then.
<main-directive ...></main-directive> <!-- Not applied here -->
<main-directive apply-logic="true" ...></main-directive> <!-- Applied here -->
<main-directive apply-logic="{{some.varOnScope}}"...></main-directive> <!-- Conditional -->
Please comment if something is unclear.