My function isn't called when I click the <a... tag.
I have the following code in my component:
public htmlstr: string;
public idUser:number;
this.idUser = 1;
this.htmlstr = `<a (click)="delete(idUser)">${idUser}</a>`;
public delete(idUser){
alert("id " + idUser);
}
My html
<div [innerHTML]="htmlstr"></div>
but the function delete isn't called and does not show the alert.
The <div... is created dynamically
If anyone face same issue and above all answer not working then try my trick :
In HTML :
<button onclick="Window.myComponent.test()"> test </button>
In component :
class
constructor(){
Window["myComponent"] = this;
}
test(){
console.log("testing");
}
Your main issue here, on-top of the things pointed out by #Matt Clyde and #Marciej21592, is that you're trying to dynamically add HTML code that needs to be compiled before it can be used (you're trying to bind to a method and variable).
Some ways of doing this can be seen here.
From the code you have supplied, however, there are much easier ways to accomplish what you are after. For starters, I would have that code in the HTML to begin with and hide/show it as needed with ngIf.
i use this method and its work
public htmlstr: string;
public idUser:number;
this.idUser = 1;
this.htmlstr = `<a id='innerHtmlClick'>${idUser}</a>`
this.htmlstr.querySelector(`innerHtmlClick`).addEventListener('click', () => {
this.delete(idUser);
});
public delete(idUser){
alert("id " + idUser);
}
EventListener listen the event bye using id of innerHtml
I assume that it is not a bug but rather Angular's security measure against XSS attacks - for more information I would suggest taking a look here https://angular.io/guide/security#sanitization-example
I somewhat also fail to understand why you insist on passing the event via string literal instead of just simply using:
<div>
<a (click)="delete(idUser)">${this.idUser}</a>
</div>
Your component has inner Html.
Angular will not allow events inside inner Html portions for security reasons. You can use Child components. to make events from inside of inner Html portions. Create a child component and put your html inside the child component and pass the data by using any angular events between parent and child using Input, Output features in Angular
I don't often use [innerHTML], but it looks like the template string you're using <a (click)="delete(idUser)">${idUser}</a> is referencing ${idUser} when you might have meant ${this.idUser}?
Below code snippet worked for me:-
In component :
ngAfterViewChecked () {
if (this.elementRef.nativeElement.querySelector('ID or Class of the Html element')) {
this.elementRef.nativeElement.querySelector('ID or Class of the Html element').addEventListener('click', this.editToken.bind(this));
}
}
inside constructor parameter:-
constructor( private readonly elementRef: ElementRef) {}
import { ElementRef } from '#angular/core';---> at the top of the file
implement 'AfterViewChecked'
Related
I am iterating through a LARGE list of objects all of which will open the same modal window that will be loaded with dynamic information. To make this work, I create a counter called MenuCounter that I know increments just fine.
That said, I am attempting to wrap a hyperlink around the icons I need to use and the injection of the method keeps pointing to the last value of the MenuCounter.
I first tried this:
...
When I ran into the issue, I tried reducing the code to the following but then the page somehow activates the hyperlink and the modal window appears and will not go away.
...
Can somebody please help me out?
Thank you!
You should apply a lambda expression to the Blazor #onclick directive instead of using the onclick Html attribute, in which case it should call a JS function, which you did not mean.
Note that I've introduced a new directive to prevent the default action of the anchor element: #onclick:preventDefault
Test this code:
#page "/"
<a href="#" #onclick:preventDefault #onclick="#(() => SetupChangeName(MenuCounter))" >Click me...</a>
<div>Counter is #output</div>
#code
{
private int MenuCounter = 10;
private int output;
private void SetupChangeName (int counter)
{
output = counter;
}
}
Note: If you use a for loop to render a list of anchor elements, you must define a variable local to the loop, and provide it as the input to your lambda expression, something like this:
#for(int MenuCounter = 0; MenuCounter < 10; MenuCounter++)
{
int local= MenuCounter;
<a href="#" #onclick:preventDefault #onclick="#(() =>
SetupChangeName(local))" >Click me...</a>
}
otherwise, all the lambda expressions will have the the same value for MenuCounter, which is the value incremented for the last iteration. See For loop not returning expected value - C# - Blazor explaining the issue.
I'm not a fan of onclick attributes, but if you're set on this method, I believe you just need to santize the C# and JS in the same line like this:
...
Adding the quotes will ensure at least an empty string is present for JS, and then you can process it.
Alternative method
Since mixing languages like that is quite frustrating, I find it easier to use data tags, for example
...
And then in your JS file:
var links = document.querySelectorAll('[data-menu-counter]');
links.forEach(x => x.addEventListener('click', /* your function code here */);
I have a button that redirects to a new page and at the same time should save data to a Service. As I use it now it looks like this:
<button [disabled]="!isValid" (click)="saveToService()" routerLink="/link">Next</button>
Now I wonder if this is best practice. It feels like the html button is somewhat cluttered by so many seperate functionalities. The obvious alternative is to move the router navigation to a function that does both things, as in:
<button [disabled]="!isValid" (click)="saveAndNavigate()">Next</button>
and in ts:
private saveAndNavigate():void { this.service.setData(data); this.router.navigate(['/link]); }
Is there a 'right' way to do this? Are there some unwanted side effects from doing both actions in html?
Thanks
I would suggest you to do it in router promises. So you can:
this.router.navigate(['/link]).then(() => {
this.service.setData(data);
});
I would implement the OnDestroy function in your component, so you can store the data when the component terminates.
Something like this in HTML:
<button [disabled]="!isValid" routerLink="/link">Next</button>
And like this in your component:
import { Component } from '#angular/core';
#Component({...})
export class ThisComponent implements OnDestroy {
ngOnDestroy(){
saveToService()
}
}
If your navigation is performed regardless of the outcome of the service call, then Fatih's answer would work just fine.
On the other hand, and what I've normally seen, is that page navigation should only occur after (successful) completion of the request. If this is the case, I would remove the routerLink directive from your button and keep the (click) function. That function could look like this:
// if your service is making an Http request
public saveToService() {
this.service.saveStuff().pipe(
tap(() => this.router.navigate(['/somewhere']))
)
}
tap simply performs some action without affecting the data stream, so it's perfect for router navigation.
I have 20+ elements, which all should use the same class (animate.css)
It is super annoying to change all classes if I want to edit the animation, so I saved the animation class in my service in a variable:
animClassSecond = "animate__animated animate__bounceInUp";
But I cant figure out how to add it to [ngClass], this does not work:
[ngClass]="{'select_elem':true, 'btn_2':true, 'dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, 'this.dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, this.dataService.animClassSecond:true}"
[ngClass]="{'select_elem':true, 'btn_2':true, dataService.animClassSecond:true}"
Its either a template error or it does not resolve to the variable. Any ideas?
P.S.: Adding a second [ngClass] attribute also does not work, because the first one is ignored.
is:
[ngClass]="dataService.animClassSecond"
But remember that you need declare the service public in the constructor
constructor(public dataService:DataService){}
NOTE you can use class and [ngClass] in the same tag:
class="select_elem btn_2" [ngClass]="dataService.animClassSecond"
This is probably not achievable in the template since the Angular template language is quite limited.
Just move the logic of ngClass object into your component.ts. There you can use all TypeScript's power
ngOnInit() {
this.ngClassObj = { [dataService.animClassSecond]: true };
}
or if you need it to be dynamic (use this one carefully because it might become a performance issue)
get ngClassObj() {
return { [dataService.animClassSecond]: true };
}
and then
[ngClass]="ngClassObj"
The manual solutions for Auto Reloading the HTML page of a specific component:
Either by navigating to the HTML page on click.
Or calling the ngOnInit() of that component on click.
I am doing it manually using a click event from the HTML code as follows:
HTML Code: app.component.html
<button (click) = reloadPage()>
TS Code: app.component.ts
reloadPage() {
// Solution 1:
this.router.navigate('localhost:4200/new');
// Solution 2:
this.ngOnInit();
}
But I need to achieve this automatically. I hope I am clear. The page should auto-reload after some specific interval and call the ngOnInit() on each interval.
Add correct call to setInterval anywhere in your call:
setInterval(() => reloadPage(), 150000); and inside the method reloadPage put the same logic you have for the button.
An example:
Just put the reloadPage function call inside the constructor:
export class SomeComponent {
constructor() {
setInterval(() => this.reloadPage(), 150000);
}
reloadPage() {
// anything your button doeas
}
}
also note, that correct call of setInterval would be:
setInterval(() => this.reloadPage(), 150000);
Note: My answer just fixes the code you presented. But it seems there is some bigger logical misunderstanding of "reloading page" in angular and using ngOnInit
My final objective is don't have to write HTML like this:
<div id='counter'>
{{counter}}
</div>
<div>
<button
id="startButton"
on-click="{{start}}">
Start
</button>
<button
id="stopButton"
on-click="{{stop}}">
Stop
</button>
<button
id="resetButton"
on-click="{{reset}}">
Reset
</button>
</div>
I would like to know if it is possible to create a Polymer-element without using HTML. For example I tried this:
#CustomTag('tute-stopwatch')
class TuteStopWatch extends PolymerElement {
ButtonElement startButton,
stopButton,
resetButton;
#observable String counter = '00:00';
TuteStopWatch.created() : super.created() {
createShadowRoot()..children = [
new DivElement()..text = '{{counter}}',
new DivElement()..children = [
startButton = new ButtonElement()..text = 'Start'
..onClick.listen(start),
stopButton = new ButtonElement()..text = 'Stop'
..onClick.listen(stop),
resetButton = new ButtonElement()..text = 'Reset'
..onClick.listen(reset)
]
];
}
}
Previous code creates HTML and shadow root correctly, but it doesn't create the binding between the #observable counter and the text of the DivElement.
I know that this is caused because I am trying to create the shadow root after the element has been instantiated/created. So that I should create the template of the element in other place before the template has been bound with its observable.
You can write a manual data binding like this:
changes.listen((changes) {
for (var change in changes) {
if (change.name == #counter) {
myDivElement.text = change.newValue;
}
}
});
changes is a property of the Observable class, which PolymerElement mixes in. (This is difficult to see in the API reference, as it currently doesn't show a class' mixins or the mixed in properties and methods.)
Polymer seems to be mostly about enabling declarative html based bindings. It may be worth exploring using custom elements and shadow dom directly, as you're not really using polymer for anything in this example. To do this you need to change the class definition to:
class TuteStopWatch extends HtmlElement with Observable {
...
}
And register your element with document.register(). You also need to include the polymer.js polyfill for custom elements.