Angular2 Use Components initialized class in HTML - html

I was wondering if the following was possible. Can I use a component's initialized classes in the HTML to call a function that isn't in the component? For example:
#Component
...
export class TestComponent implements OnInit {
simpleHandler:SimpleHandler = new SimpleHandler();
...
Then in the HTML call it like so?
<div>
{{simpleHandler.isValid()}}
</div>
Or can it only call functions and properties on the components themselves?
When I try to do this, it gives this error:
TypeError: self.parentView.parentView.context.simpleHandler.isValid is not a function

Yes, you can do it. For example, this pattern is used by ng2-file-upload
#Component({
selector: 'simple-demo',
templateUrl: './simple-demo.html'
})
export class SimpleDemoComponent {
public uploader:FileUploader = new FileUploader({url: URL});
...
}
and in html:
<button type="button" class="btn btn-success btn-xs"
(click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess">
<span class="glyphicon glyphicon-upload"></span> Upload
</button>

Related

initialize html tag from angular type-script

as shown in the below angular type script code, i would like to refer to the divisions mentioned in the below posted .html code using document.getElementById
the result of the log statement is null
please let me know how correctly to referece an html-tag in type-script
.ts:
export class GridCellPopupOverlayComponent implements OnInit {
isVisible = true
container: any
content
closer: any
overlay: any
AoC: any
AvgH: any
Dist: any
I: any
constructor() {
}
initHTMLElements() {
console.log("html init")
this.container = document.getElementById('idGridCellInfoPopupDiv');
this.AoC = document.getElementById('idGridCellInfoAoCValueDiv');
this.AvgH = document.getElementById('idGridCellInfoAvgHValueDiv');
this.Dist = document.getElementById('idGridCellInfoDistValueDiv');
this.I = document.getElementById('idGridCellInfoIValueDiv');
this.closer = document.getElementById('gridCellInfoPopup-closer');
console.log("this.AoC:",this.AoC)
}
}
html:
<div *ngIf="isVisible" id="idGridCellInfoPopupDiv" class="ol-popup">
<!-- <span id="idGridCellLabel" class="label label-success">dsfdsfsa</span> -->
<div class="alert alert-success alert-sm" role="alert">
<div class="alert-items">
<div class="alert-item static">
<div class="alert-icon-wrapper">
<clr-icon class="alert-icon" shape="check-circle"></clr-icon>
</div>
<div id="idGridCellAlertText"class="alert-text">
</div>
</div>
</div>
<!-- <button type="button" class="close" aria-label="Close">
<clr-icon aria-hidden="true" shape="close"></clr-icon>
</button> -->
</div>
<div id="idGridCellInfoAoCValueDiv"></div>
<div id="idGridCellInfoAvgHValueDiv"></div>
<div id="idGridCellInfoDistValueDiv"></div>
<div id="idGridCellInfoIValueDiv"></div>
You can get the elements from the .html by using #ViewChild/#ViewChildren decorators. Behind the scenes they are using document.getElementById. This is the correct way in Angular.
Also watch out for ngAfterViewInit lifeycle method in which you can access your references. View queries are set before the ngAfterViewInit callback is called. (form Angular documentation)
Here is the reference: https://angular.io/api/core/ViewChild
Btw, you can omit static: false since it's default.
TS file
import { HelloComponent } from './hello.component';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
name = 'Angular';
#ViewChild('pRef', {static: false}) pRef: ElementRef;
ngAfterViewInit() {
console.log(this.pRef.nativeElement.innerHTML);
this.pRef.nativeElement.innerHTML = "DOM updated succesfully!!!";
}
}
Template file
<hello name="{{ name }}" ></hello>
<p #pRef>
Start editing to see some magic happen :)
</p>```

'trim' of undefined TypeError in Angular app when I don't call trim anywhere

I'm currently trying to learn Angular and as I'm working through a couple of ideas I had, I ran into the following error in the dev console of Chrome:
ERROR TypeError: Cannot read property 'trim' of undefined
at Function.addMultipleClasses (primeng-dom.js:19)
at ButtonDirective.createIconEl (primeng-button.js:59)
at ButtonDirective.setIconClass (primeng-button.js:78)
at ButtonDirective.set label [as label] (primeng-button.js:92)
at setInputsForProperty (core.js:10940)
at elementPropertyInternal (core.js:9984)
at ɵɵpropertyInterpolate1 (core.js:15551)
at Module.ɵɵpropertyInterpolate (core.js:15514)
at CmsComponent_Template (cms.component.html:12)
at executeTemplate (core.js:9579)
Here is my HTML:
<h1>Angular Router App</h1>
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in AppRoutingModule) -->
<nav>
<ul>
<li><a routerLink="/login" routerLinkActive="active">/login</a></li>
<li><a routerLink="/" routerLinkActive="active">/</a></li>
</ul>
</nav>
<button type="button"
pButton
label="{{word}}"
(click)="buttonPress()">
</button>
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>
And here is my TS:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-landing',
templateUrl: './cms.component.html',
styleUrls: ['./cms.component.scss']
})
export class CmsComponent implements OnInit {
private onWord: number = 0;
private words: Array<string> = ["One","Two","Three","Two"];
public word: string = this.words[this.onWord];
constructor(
) { }
ngOnInit(): void {
}
public buttonPress(): void {
// Bumps the index
this.onWord++;
// Keeps the value in the proper range
if (this.onWord >= this.words.length) {
this.onWord = 0;
}
// Updates the word to the new index
this.word = this.words[this.onWord];
console.log("The button is now on " + this.word);
}
}
I'm using Angular and Typescript. Any thoughts on what's going wrong? The button functions as intended except for that error whenever I click it.
Thanks!
was the same problem, I found a solution on the official forum, in order to fix the error you need to add -> icon = "pi"
<button
icon="pi"
type="button"
pButton
[label]="(documentsCount$ | async)?.toString()"
></button>
This is not an error generated from your code but in the package you're using (in this case PrimeNg). The method seems to be expecting some input which it's not getting. Try passing '$event' in the click method and handle it in the ts file.
(click)="buttonPress($event)"
As an ideal implementation, use the predefined button element provided by PrimeNg.
Turns out having {{word}} as the button label broke things!
Apparently I can't have dynamically changing button labels...
Use p-button instead:
<p-button
label="{{word}}"
(onClick)="buttonPress()">
</p-button>
See the forum entry

Angular 10 scrollTo from parent component to an element in child component html

parent html:
<div>
<button type="button" (click)="scroll(childelementTwo)">TO CHILD</button>
</div>
<div>
<app-child>
</div>
child html:
<div>
<div #childelementOne>
lot of stuff
</div>
<div #childelementTwo>
another lot of stuff
</div>
</div>
if all this html code were in the "same" component.html I would use this function, but not:
scroll(el: HTMLElement) {
el.scrollIntoView();
}
So: How can I scroll to an html element in child component ?
You can use #ViewChildren for this.
List-Item:
#Component({
selector: 'app-list-item',
templateUrl: './list-item.component.html',
styleUrls: ['./list-item.component.css']
})
export class ListItemComponent implements OnInit {
#Input() list;
constructor(private elRef: ElementRef) { }
ngOnInit() {
}
scrollIntoView() {
this.elRef.nativeElement.scrollIntoView();
}
}
List-Component:
#ViewChildren(ListItemComponent) viewChildren!: QueryList<ListItemComponent>;
list = new Array(1000).fill(true)
scrollTo() {
this.viewChildren.toArray()[100].scrollIntoView()
}
HTML:
<button (click)="scrollTo()">scroll to 100</button>
<app-list-item *ngFor="let item of list">
list works!
</app-list-item>
stackblitz: https://stackblitz.com/edit/angular-ivy-6ccaav?file=src%2Fapp%2Fapp.component.html
Mat, your "elements" are in child and you want control in parent. So, first make access to the elements in child using ViewChild
//in your child.component
#ViewChild("childelementOne") childelementOne;
#ViewChild("childelementTwo") childelementTwo;
Then in parent you can do
<div>
<button type="button" (click)="scroll(childComponent.childelementTwo)">
TO CHILD
</button>
</div>
<div>
<!--see that use a template reference variable to the childComponent-->
<app-child #childComponent></app-child>
</div>
scroll(el: ElementRef) {
el.nativeElement.scrollIntoView();
}
See how, in the .html we are using childComponent.childelementTwo. childComponentis the own component app-child, childComponent.childelementTwo is the "variable" that we get in the #ViewChild. By defect is an ElementRef. You get to the HTMLElement using el.nativeElement. Yes, using a template reference we can access to all the public variables and public function of your child.component
I create a stackblitz that is looks like the stackblitz in enno's answer, but see that is complety different
NOTE. You can also use the same referenceVariable in the child.component and use ViewChildren, so you can pass to the function the QueryList and the index

Ng bootstrap modal don't open

I've had this problem since this morning and I can't solve it, I've tried a lot of things but there's no way I can get it opened.
In practice I have to implement a test button that opens a modal with a header and a body, in addition there must be a closing and a disengagement button. This part of HTML works and I tested it, the problem is the part of TypeScript.
In the open function there is the command const modalRef = this.modalService.open(NgbdModalContent); that doesn't work correctly, the fact is that the command works, in fact if instead of NgbdModalContent I put a string works, so it's just NgbdModalContent that should theoretically take HTML.
If you have any idea how to do this would be of immense pleasure, below I leave you the files concerned.
PS. are new to this world, especially about Angular2 and TypeScript so criticism and comments are welcome (I know I've made a big mistake and probably a trivial one (for you)). THANK YOU SO MUCH
TypeScript
import { Component, Input } from '#angular/core';
import {NgbModal, NgbActiveModal} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'post-ngb-modal-demo',
templateUrl: './modal-demo.component.html',
})
export class NgbModalDemoComponent{
constructor(private modalService: NgbModal) {}
open() {
const modalRef = this.modalService.open(NgbdModalContent);
}
}
export class NgbdModalContent {
#Input() name;
constructor(public activeModal: NgbActiveModal) {}
}
HTML
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">Hi there!</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Hello, world!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
</ng-template>
<button class="btn btn-lg btn-outline-primary" (click)="open()">Launch demo modal</button>
I think you need to add dynamically created components to entryComponents inside your #NgModule in your app.module.ts.
#NgModule({
imports: [
....
...
],
declarations: [
NgbdModalContent,
...
],
entryComponents: [NgbdModalContent],
providers: [
....
]
})

Angular 2+ initialize value of input

I am trying to implement a Component which corresponds to a Bootstrap modal including an input. The input is hooked to a variable in the Component class via [(ngModel)]="..." This works if I enter text inside the input (the variable's value gets updated).
What I want to do is that when this component's show() method gets called the input should be populated with text passed in as a parameter. This does not seem to work and I can't figure out how I can set the initial text passed in as a parameter (without using jQuery).
Here's the relevant code:
editdialog.component.html
<div id="edit_todo_modal" class="modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit todo</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Editing todo: {{currentText}}</p>
<div class="row">
<div class="col-md-12">
<!-- THIS IS WHERE I WANT THE INITAL TEXT -->
<input id="edit-todo-modal-input" type="text" class="form-control" [(ngModel)]="currentText">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
editdialog.component.ts
import { Component } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { ListComponent } from './list.component';
import { Injectable } from '#angular/core';
declare var jQuery : any;
#Injectable()
#Component({
selector: 'edit-todo-dialog',
templateUrl: './editdialog.component.html',
styleUrls: ['./editdialog.component.css']
})
export class EditTodoDialogComponent{
currentText: string = "";
index: number;
/* I want to use this method to set the initial value */
show(index: number, text: string): void {
this.currentText = text;
this.index = index;
jQuery("#edit-todo-modal-input").val(this.currentText); // I don't want to use jQuery for showing the initial value, however this works
jQuery("#edit_todo_modal").modal(); // show bootstrap modal
}
}
Thanks in advance.
UPDATE
The show()method gets called from this component
import { Component } from '#angular/core';
import { ListService } from './list.service';
import { OnInit } from '#angular/core';
import { EditTodoDialogComponent } from './editdialog.component';
/**
* The main todo list component
*/
#Component({
selector: 'list-component',
templateUrl: './list.component.html',
styleUrls: ['./list.component.css'],
providers: [ListService, EditTodoDialogComponent]
})
export class ListComponent implements OnInit {
private listService: ListService;
private editTodoDialog: EditTodoDialogComponent;
/* ... */
constructor(listService: ListService, editTodoDialog: EditTodoDialogComponent) {
this.listService = listService;
this.editTodoDialog = editTodoDialog;
}
ngOnInit(): void {
this.getTodos();
}
/* ... */
// TO BE IMPLEMENTED
showEditTodoDialog(index: number) : void {
this.editTodoDialog.show(index, this.todos[index]);
}
}
The event is hooked like this:
<li class="list-group-item" *ngFor="let todo of todos; let i = index">
<div class="todo-content">
<p class="todo-text" (dblclick)="showEditTodoDialog(i)">
{{todo}}
</p>
</div>
<div class="todo-close">
<button (click)="removeTodo(i)" class="btn btn-danger btn-sm">
<i class="fa fa-remove"></i>
</button>
</div>
</li>
The problem is that you are calling the show from ListComponent by using the componentReference.
You should not do that to pass information between components .
You should either use a #Input and #Output i:e Event Emitters if these component have Parent child relationship else the best way is to go for Shared Services where once you load he data to the service the other component is notified of the change and subscribes to the new data.
More info on how to use parent child link
More info on how to use shared serviceslink
Have you tried value?:
<input id="edit-todo-modal-input" type="text" class="form-control" [value]="currentText" ngModel>
For objects, use ngValue:
<input id="edit-todo-modal-input" type="text" class="form-control" [ngValue]="currentText" ngModel>