Weird behaviour querySelector - javascript

I have component in angular
in component.ts i have line of code that serach html element
var myArticle = document.querySelector('article');
this return null
in component.html
<article id="bodyArticle" *ngIf="isClicked"></article>
but when I use article in sibling component.ts
var myArticle != null from component.ts
How it's work querySelector serach element in all file project ?
I also have other problem. In same componen.html i have button
<div class="btnGrp">
<button (click)="loadXML($event)">Load</button>
<input type="file" name="img" multiple (change)="onChange($event)">
</div>
<article id="bodyArticle" *ngIf="isClicked"></article>
When i click button one click emitted is value true firstly and i must click second to button to load thata to article content
snippet from component.ts
loadXML(event?: Event) {
var myArticle = document.querySelector('article');
console.log(myArticle);
if(this.imageService.getBodyRes() == ''){
myArticle.textContent = 'Error can't load data';
this.isClicked = true;
this.xmlLoadedEvent.emit(this.isClicked);
} else {
myArticle.textContent = this.imageService.getBodyRes();
console.log(myArticle.textContent);
this.isClicked = true;
this.xmlLoadedEvent.emit(this.isClicked);
}
}
How to do that when i click button in article tag ngIf set true value and also loadData without dobule click.

Try to avoid using the document object and its methods in Angular. It will make your life very difficult if you try to do that.
<article #articleId id="bodyArticle" *ngIf="isClicked"></article>
You can put a template variable name on the article tag, and then query it with angular's 'ViewChild' class like so -
import { Component, ViewChild, ElementRef, ...others?....} from '#angular/core'
export class MyComponent {
articleToggle: boolean = false; // add a toggle in your component for turning the article on
#ViewChild('articleId') article: ElementRef;
ngAfterViewInit() {
this.article.nativeElement.<<access DOM attributes here>>
}
}
This will give you access to the DOM node, like you expected your document query to have done, but even so, there are probably still better ways to do what you are doing, especially since you mentioned you are trying to use the article in sibling components. If that is the case, you may want to make it its own component allowing you to avoid queries altogether, but again, impossible to know with so little info.
Also, its generally recommended to avoid comparing to null entirely in typescript. Use 'undefined' instead.
<button (click)="loadXML($event); articleToggle = true">Load</button>
Set your variable to true on click, and then for the article.
<article #articleId id="bodyArticle" *ngIf="articleToggle"></article>
Now it will appear after the first click of the button, as that will be the only time the value changes from false -> true.

Related

Conditinally rendering text using Angular clipboard

I want to implement a simple copy-to-clipboard function in my Angular application.
The function to copy works, however, I do not understand how conditional statements in Angular as most of my experience is in React.
In React I would define my variable
[copy, setCopy] = useState(false)
Then, I would pass this into wherever I want to change the text or graphic element:
<button>{copy ? "click to copy" : "url copied"}</button>
Using the Angular docs, this is what my copy function looks like in Angular:
export class ThankyouComponent {
copied = false
value ='https://url.com'
}
And here is where I want to use it in my HTML file
<button [cdkCopyToClipboard]="value">{{SOME_EVENT ? "copy url" : "url copied" }}</button>
How do I access the click event of cdkCopyToClipboard in order to conditionally render the string within the button as in my React example? I've been looking around online and can't find a solution.
to display something conditionally inside an HTML template in Angular, you can use property that holds text
In HTML button attribute you can add
(click)="myOnClickMethod()"
Which calls method inside ts file whenever click event occures.
And in that method (that should belong to ts file in the same component you can do something like which changes button text to anything you want after the click.
#Component({
// Omitted for simplicity
})
export class MyComponent{
public buttonText: string = 'Copy url'
public myOnClickMethod(): void {
this.buttonText = 'Copied'
}
}
Then you can pass that variable with string interpolation to that button content like
<button ...>{{ buttonText }}</button>
Bonus
If you want to display some blocks conditionally, like some kind of fallback in case of no data, for example, in React you would probably do something like
if(!props.myList || props.myList.length < 1) return <strong>No data</strong>
return <> // some processing </>
In Angular you can use *ngIf directive
<div *ngIf="myList?.length > 0 else no-data">
// some processing
</div>
<ng-template #no-data>
<strong>No data</strong>
</ng-template>
Of course you donĀ“t need to use else statement and you can use opposite condition in other *ngIf instead.

lit / vaadin-dialog: How to access the dialog elements in my web app?

I am using Lit 2.3 and try to use the the #vaadin/vaadin-dialog component from within my own Lit component to show an input field and access the value of it when a user clicks the Okay button. My own render method looks something like this:
render() {
return html`
<vaadin-dialog
header-title="settings"
.opened="${this.dialogOpened}"
#opened-changed="${this.dialog_opened_change}"
${dialogRenderer(this.renderDialog, [])}
${dialogFooterRenderer(this.renderFooter, [])}
></vaadin-dialog>
`
}
And the renderDialog method that renders the inside of the dialog looks like this:
private renderDialog = () => html`
<div>
<div class="settings-dialog">
<label for="userid">User-Id</label><input name="userid" id="userid" type="text" value=${this.userid}>
</div>
</div>
`
I can successfully open and close the dialog. What I don't understand is how to get the value of the input field after the dialog closed. And in fact I would also like to initialize the value of the input field without using a local state (${this.dockid}), but that at least works.
I tried accessing the values from within "this.dialog_opened_change" but I don't seem to understand what to hang my querySelector on to get to my <input>.
This is what I do, currently, but it looks unorthodox at best to me:
I call the method _apply_settings from a button within the dialog's footer
private renderFooter = () => html`
<button class="modal-ok" id="apply_settings" #click="${this._apply_settings}">
</button>
`
And in the _apply_settings I poke around in the DOM to get to the vaadin-dialog's overlay and use that as a starting point for querySelector:
private _apply_settings(e: Event) {
let overlay = (<HTMLElement>e.target)?.parentNode?.parentNode
let settings = new Settings()
settings.user_id = (<HTMLInputElement>overlay?.querySelector("#userid")).value
this._close_settings()
}
While this works for now, I have trouble believing that it is the way it should be. The documentation unfortunately stops at showing a dialog and never explains how to access things so I wonder if I am missing something entirely trivial?

How can a template-driven form access a control inside a component?

I have a <form> that uses the Template Driven approach.
The form contains a mix of native controls, like <input> and <select>, and also wrapper components, like <text-control> and <checkbox-control> that contains the native <input> element inside of it.
How can the form access the <text-control>'s native element to read its error and touched state?
Also, if I want to place a validator directive on that wrapper component and have it pipe-down to the native <input>, how can I approach this?
I tried to use ngModel on the wrapper component, but it doesn't work, since ngModel is hooking to the wrapper component, not the underlying <input> element.
Another approach was to use <ng-content>, but the underlying native element takes a lot of attributes, so much that it'll be a pain to copy-n-paste it everywhere.
Example:
<checkbox-control>
<!-- Now, it's easy for the form to access the control. -->
<!-- But I've too many attributes and properties that go here. -->
<input type="checkbox">
</checkbox-control>
PS: I am not looking to use ElementRef to access the native element, I just want the <form> to be aware of the native elements' error state, so that I can tell whether the form is valid or not.
Example:
<form #editor="ngForm">
<input type="text" validateText />
<select validateSelect>
<option value="1"></option>
<option value="2"></option>
</select>
<!-- Does not work. -->
<checkbox-control validateCheckbox></checkbox-control>
</form>
Thanks in advance.
You can use element's local reference in template approach.
Please refer to this code I just wrote to explain :
https://stackblitz.com/edit/angular-ivy-7no9ok?file=src/app/app.component.html
I found a reasonable solution that works without writing much code, and without editing too many components.
Step 1: Create your validator
#Directive({ selector: '[assertNoSpecialChars]' })
export class SpecialCharacterValidator implements Validator {
// The directive will have a reference for the name of the form's control.
#Input() assertNoSpecialChars: string = '';
validate(group: FormGroup): ValidationErrors | null {
const control = group.controls[this.assertNoSpecialChars];
// For simplicity, let's say we don't want the dollar sign in our input.
if (control.value.includes('$')) {
return { invalid: true };
} else {
return null;
}
}
}
Step 2: Apply the directive on your form
<form #f="ngForm" [assertNoSpecialChars]="'username'">
<text-control></text-control>
</form>
Step 3: Bind the input event of your component to the hosting form component
<form #f="ngForm" [assertNoSpecialChars]="'username'">
<text-control (input)="updateFormManually($event, 'username')"></text-control>
</form>
Step 4: Get a reference for your NgForm, and implement the update mechanism
#Component({})
export class FormComponent implements AfterViewInit {
// Grab a reference for your NgForm.
#ViewChild('f', { static: true }) f!: NgForm;
// Create your form control.
username: FormControl = new FormControl();
// Register your control to the NgForm.
ngAfterViewInit(): void {
this.f.form.addControl('username', this.username);
}
// Update the control manually.
updateFormManually(event: any, controlName: string): void {
this.f.form.controls[controlName].setValue(event.target.value);
}
}
Now, the form's validity state, and error messages will be correctly bound.

Is there a way to use a field's value in the Model to set a style attribute dynamically?

I have created a CSHTML email template file to send alerts when a specific event is about to end (i.e. sends an alert 10-minutes before end, then at 5-minutes before end, etc.). I want to highlight the type of event (since there can be more than one) by color to differentiate the messages. What I currently have is this:
<strong style="color: {event color}">#alert.event</strong> is scheduled to end: <strong>#endEasternTime
I would like to be able to set the {event color} based on the value in #alert.event, but I'm not sure that I can. I tried to create a script in the file, but I'm not sure how to get its return value into the style tag:
<script>
// Get the root element
var r = document.querySelector(':root');
// Create a function for getting a variable value
function getEventColor(event) {
// Get the styles (properties and values) for the root
var rs = getComputedStyle(r);
// Get the color associated with the event (default is the border color)
return (event === "event1"
? rs.getPropertyValue('--evt1Color')
: (event === "event2"
? rs.getPropertyValue('--evt2Color')
: (event === "event3"
? rs.getPropertyValue('--evt3Color')
: rs.getPropertyValue('--bdrColor')
)
)
);
}
</script>
Note that I have created HTML variables in the file to match up the colors to other styles in the file (such as border-color) -- for space considerations and clarity, I'm not going to show that here.
Is there a way to do this? If not using the inline CSS above, can I update a class or id on the fly using something like the script method above and, if so, what's the best way. I appreciate any help.
You could use if.. else..
#if (#alert.event == value1)
{
<strong class="color1">
}
else if (#alert.event == value2)
{
<strong class="color2">
}
else {
<strong class="colorn">
}
Or simpler way could be naming the classes based on your event and adding color using CSS.
<strong class="event #alert.event">
The tag has 2 classes (event and the value generated)
Lets say #alert.event = "test", then add the CSS to the class as shown below.
.event.test {
color: <color code>
}

ngIf and click not working for dynamic html in angular2

ngIf and click not working for dynamic html.
when load html using innerHtml then ngIf and click event not loading.
export class FillInBlanksComponent implements OnDestroy, OnInit {
question = '';
editable = true;
ngOnInit() {
question = '<span *ngIf="editable" name="answers['+incr+']" contenteditable="true" (click)="onclick()"> </span>';
}
onClick(){
alert("clicked!!!!");
}
}
The function you're calling is (click)="onclick()" but you've defined onClick().
Another thing is, you might want to DOM Sanitize the string else you'll get a warning. Please check this answer out on how to do that.
check you have done spelling mistake use (click)="onClick()"
you are using small c in your function call

Categories

Resources