Angular 13 - How to open form without button and the button click - html

My angular component has a tree and several nodes. When I double click on a node the click event runs a web api and retrieves data for an id that will be used to create a dynamic form using npm package: #rxweb/reactive-dynamic-forms. Once the data request is 'completed' a button appears and when clicked it opens the form with appropriate fields for the id selected. I would like to eliminate the need for this secondary button click. I've tried several suggestions but just cannot get anything to work.
I'm using Infragistics controls and bootstrap for the form.
html:
<div class="column-layout my-pane-layout">
<div *ngIf = "isShowFormButton" >
<button #open igxButton="raised" igxRipple="white" (click)="form.open()">Run</button>
<igx-dialog #form [closeOnOutsideSelect]="true" >
<igx-dialog-title>
<div class="dialog-container">
<igx-icon>vpn_key</igx-icon>
<div class="dialog-title">Form</div>
</div>
</igx-dialog-title>
<form class="input-group-form" [formGroup]="dynamicForm.formGroup" (ngSubmit)="onSubmit()">
<div class="container">
<div class="controls" viewMode="horizontal" [rxwebDynamicForm]="dynamicForm" [uiBindings]="uiBindings">
</div>
<button igxButton="raised" type="submit" igxRipple class="button" [disabled]="!dynamicForm.formGroup.valid">
<igx-icon>
directions_run
</igx-icon>
<span>Submit</span>
</button>
</div>
</form>
<div igxDialogActions>
<!-- <button igxButton (click)="form.close()">CANCEL</button> -->
<button igxButton (click)="form.close()">Submit</button>
</div>
</igx-dialog>
</div>
<h6 class="h6">
Levels
</h6>
<igx-tree #tree class="tree" selection="None" >
<igx-tree-node *ngFor="let level1 of myData" [data]="level1">
{{ level1.Name }}
<igx-tree-node *ngFor="let level2 of level1.levels" [data]="level2">
{{ level2.Name }}
<igx-tree-node *ngFor="let level3 of level2.levelplus" [data]="level3" (dblclick)="onDoubleClick($event,level3)">
{{level3.Name }}
</igx-tree-node>
</igx-tree-node>
</igx-tree-node>
</igx-tree>
</div>
XYZ.component.ts:
export class XYZComponent implements OnInit {
#ViewChild('form') dialog: IgxDialogComponent;
myData: any[];
public tree: IgxTreeComponent;
public selectedNode;
public ID: number = 2;
isShowRunButton: boolean = false;
public dynamicForm!: DynamicFormBuildConfig;
public dynamicFormConfiguration!: DynamicFormConfiguration;
constructor(private dynamicFormBuilder:RxDynamicFormBuilder){}
ngOnInit() {
populate tree with data here ...
}
public onDoubleClick(event,node) {
console.log(node);
event.stopPropagation();
this.runParameters(node.Id);
}
public runParameters(Id) {
this.aSer.getApi(Id).subscribe({next:(data: any[]) => {this.myData = data;},
error: err => {console.log(err); },
complete: () => {
this.dynamicForm =
this.dynamicFormBuilder.formGroup(this.myData,this.dynamicFormConfiguration);
this.isShowFormButton = true;
//this.dialog.open();
}
});
}
public onSubmit() {
console.log(this.dynamicForm.formGroup);
this.isShowFormButton= false;
//this.dialog.open();
}
}
If I uncomment out the 'this.dialog.open()' the code throws the following error:
TypeError: Cannot read properties of undefined (reading 'open')
Many postings say that I need to use a #ViewChild but it seems that it cannot find that reference : #ViewChild('form') dialog: IgxDialogComponent;
Any help would be much appreciated. Code works fine with the 'Run' button click but I want to eliminate that extra step.

Related

How to pass value from one component to another? (Angular)

I just recently started learning Angular and I have a question. I want to implement a search method to search for a product on my site, I made search.pipe.ts, which works, but the input for input is in the header.component.ts component, and the products array is in the car-list.component.ts component.
car-list.component.html
<div *ngFor="let car of cars | paginate: { itemsPerPage: pageNumber, currentPage: currentPg} | **search:searchStr**" class="col-md-3">
<div class="product box">
<img src="{{'data:image/jpg;base64,' + car.image }}" alt="">
<h3>{{ car.name }}</h3>
<div class="price">{{ car.price | currency:'USD' }}</div>
<button class="btn btn-primary btn-sm">Add to cart</button> <!--(click)="addToCart(tempProduct)"-->
</div>
<br>
</div>
header.component.html
<form class="d-flex me-5">
<input type="text" class="form-control me-2" placeholder="Search cars...">
</form>
header.component.ts
export class HeaderComponent implements OnInit {
searchStr: string = '';
constructor() {
}
ngOnInit(): void {
}
}
search.pipe.ts
#Pipe({
name: 'search'
})
export class SearchPipe implements PipeTransform {
transform(cars: any[], value: any) {
return cars.filter(car => {
return car.name.includes(value);
})
}
}
I want the input values ​​from the header component to be passed to the car-list component so that I can find the product I need.
In this case you can use a shared service where you can pass data from your header component and load that data in your products component.
For further reference - Angular 4 pass data between 2 not related components
use #Input and #Output decorators to communicate between components

EditForm in Blazor app have multiple submit buttons

I have a simple Blazor Editform where i have multiple buttons with different navigations & toast notifications. I have OnValidSubmit attached to Editform. Now the validations are working for all the buttons. I have added onclick() to trigger button functions but I want onclick to be triggered only if user has entered all the details. Hope I have explained well. Please let me know for additional input.
Current output for Forward or Next buttons are : if No values entered -> Correct validation(asked to fill in details) -> forward notification displayed.
Expected output :
if No values entered -> Correct validation(asked to fill in details).
if All values entered -> Correct validation -> forward notification displayed.
Here is some code:
<EditForm EditContext="#editContext" OnValidSubmit="HandleValidSubmit" #onreset="HandleReset">
<DataAnnotationsValidator />
<div class="form-row">
<div class="form-group col">
<label>Role</label><br />
<InputRadioGroup #bind-Value="model.Role" class="form-control">
#foreach (var option in rdOptions)
{
<InputRadio Value="option" /> #option
<text> </text>
}
</InputRadioGroup>
<ValidationMessage For="#(() => model.Role)" />
</div>
<div class="form-group col">
<label>Company Name</label>
<InputSelect id="txtCompanyName" class="form-control" #bind-Value="#model.CompanyName">
<option selected value="-1">-Select-</option>
<option value="CompanyName1">CompanyName1</option>
<option value="CompanyName2">CompanyName2</option>
</InputSelect>
<ValidationMessage For="#(() => model.CompanyName)" />
</div>
</div>
<div class="form-row">
<div class="text-left col-3">
<button type="submit" class="btn btn-primary btn-success">Save</button>
</div>
<div class="text-right col-3">
<button type="submit" class="btn btn-primary" #onclick="#Forward">Forward</button>
</div>
<div class="text-right col-3">
<button type="submit" class="btn btn-primary" #onclick="#Review">Next</button>
</div>
<div class="text-right col-3">
<button type="reset" class="btn btn-secondary">Clear</button>
</div>
</div>
</EditForm>
code section:
#code {
private Model model = new Model();
private EditContext editContext;
List<Model> models = new();
protected override void OnInitialized()
{
editContext = new EditContext(model);
}
private void HandleValidSubmit()
{
var modelJson = JsonSerializer.Serialize(model, new JsonSerializerOptions { WriteIndented = true });
JSRuntime.InvokeVoidAsync("alert", $"SUCCESS!! :-)\n\n{modelJson}");
toastService.ShowSuccess("saved successfully!");
}
private void Forward()
{
toastService.ShowInfo("Forwarded!!");
}
private void Review()
{
toastService.ShowInfo("Review!!");
}
private void HandleReset()
{
model = new Model();
editContext = new EditContext(model);
}
}
Change type="submit" to type="button"
Except maybe for the Save button.
You can do validation manually in your button event handlers and then not use the EditForm OnValidSubmit, and set the button types to button.
...
if (editContext.Validate())
go
else
alert
...
FYI - The relevant bit of code from EditForm looks like this:
private async Task HandleSubmitAsync()
{
Debug.Assert(_editContext != null);
if (OnSubmit.HasDelegate)
{
// When using OnSubmit, the developer takes control of the validation lifecycle
await OnSubmit.InvokeAsync(_editContext);
}
else
{
// Otherwise, the system implicitly runs validation on form submission
var isValid = _editContext.Validate(); // This will likely become ValidateAsync later
if (isValid && OnValidSubmit.HasDelegate)
{
await OnValidSubmit.InvokeAsync(_editContext);
}
if (!isValid && OnInvalidSubmit.HasDelegate)
{
await OnInvalidSubmit.InvokeAsync(_editContext);
}
}
}
In my opinion you will need to use JavaScript to check the inputs were filled or not and based on that you can disable or enable the submit button

Angular TypeError when the page opens

In my code, I'm displaying a list of elements on the main page. When you click one of those elements, a side-pannel opens and you are able to see the details of that element. Whenever the page first opens I get the error below. The code doesn't crash or anything, but I don't get why the error keeps appearing. What should I do to fix it?
StickerListComponent.html:167 ;
<fuse-sidebar class="sidebar centra details-sidebar fuse-white" name="sticker-preview-sidebar" position="right">
<sticker-preview [StickerData]="previewStickerData"></sticker-preview>
</fuse-sidebar>
sticker-preview-component html ;
<div class="h-100-p" fxLayout="column" fusePerfectScrollbar>
<div class="group mt-32">
<div fxLayoutAlign="start center">
<header class="purple-fg" style="font-size:18.72px"><strong>Sticker Data:</strong></header>
</div>
<p>
<span id="sticker" contenteditable [textContent]="_stickerData?.StickerData" (input)="onStickerDataChange($event.target.innerHTML)">
{{_stickerData?.StickerData}}
</span>
</p>
</div>
<div fxLayout="row" fxLayoutAlign="start center">
<button mat-button matRipple class="purple-500 fuse-white-fg mr-12" (click)="imageSource()"> Display </button>
</div>
<div>
<img [src]="url" *ngIf="hidden" />
</div>
</div>
sticker-preview-component ts:
export class StickerPreviewComponent implements OnInit {
EditIndex: number;
public _stickerData: IStickerData = {};
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
Filter: IFilter = {};
hidden = false;
url;
#Input()
set StickerData(prm: IStickerData) {
if (this._stickerData != prm) {
this._stickerData = prm;
}
33 this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;
}
get StickerData(): IStickerData {
return this._stickerData;
}
dataSource: MatTableDataSource<IStickerData>;
ngOnInit() {
this._productionService.getStickerDataList(this.Filter)
.subscribe((response: any) => this._stickerData = response);
}
imageSource(): void{
this.hidden = !this.hidden;
}
it seems the _stickerData is undefined at the time when it assigns to the url, try to check whether the _stickerData has the proper value and then assign it.
if(this._stickerData)
this.url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" + this._stickerData.StickerData;

Why does using *ngIf = "authService.getUser()" in my top level app component html break my login component?

I am new to Angular so please bear with my naivety. I created a login component, which I use to prevent access to router navigation links in the main app-component html until the user logs in. The login component itself is another routed page, and I wanted to instead add the login component to my mainpage, and hide the routing navigation links until the user logs in.
To hide the user navigation links in the app-component html I tried using
*ngIf="authService.getUser()".
*ngIf="authService.getUser()" hides the navigation components until the user is logged in as expected, but it unexpectedly also didn't render the entirety of the login component (the user and password fields and submit button), except the first text which simply says "LOGIN". So the user has no way to login.
this is app.component.html:
<app-login>
</app-login>
<div class="page-header" >
<div class="container">
<div *ngIf="authService.getUser()" class="navLinks">
<a [routerLink]="['/home']"> Home</a>
<a [routerLink]="['/about']"> About App</a>
<a [routerLink]="['/login']">Login</a>
<a [routerLink]="['/protected']">Protected</a>
</div>
</div>
</div>
<div id="content">
<div class="container">
<router-outlet></router-outlet>
</div>
</div>
and these are my login component files
login.component.ts:
import { Component } from '#angular/core';
import { AuthService } from '../auth.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
message: string;
constructor(public authService: AuthService) {
this.message = '';
}
login(username: string, password: string): boolean {
this.message = '';
if (!this.authService.login(username, password)) {
this.message = 'Incorrect credentials.';
setTimeout(function() {
this.message = '';
}.bind(this), 2500);
}
return false;
}
logout(): boolean {
this.authService.logout();
return false;
}
}
login.component.html:
<h1>Login</h1>
<div class="alert alert-danger" role="alert" *ngIf="message">
{{ message }}
</div>
<form class="form-inline" *ngIf="!authService.getUser()">
<div class="form-group">
<label for="username">User: (type <em>user</em>)</label>
<input class="form-control" name="username" #username>
</div>
<div class="form-group">
<label for="password">Password: (type <em>password</em>)</label>
<input class="form-control" type="password" name="password" #password>
</div>
<a class="btn btn-default" (click)="login(username.value, password.value)">
Submit
</a>
</form>
<div class="well" *ngIf="authService.getUser()">
Logged in as <b>{{ authService.getUser() }}</b>
<a href (click)="logout()">Log out</a>
</div>
What is in your authService.getUser() method? If it is asynchronous all you have to do is *ngIf="(authService.getUser() | async)".
I don't understand why are you using *ngIf="!authService.getUser()" inside your login component like this.
Semantically the purpose of this component is to be used when you're not logged.
So simply try to wrap your <app-login></app-login> in your app.component.html into a div wich have the *ngIf="!authService.getUser()"
Like this :
<div *ngIf="!authService.getUser()">
<app-login></app-login>
</div>
Also I recommand you to not use directly the service method like this in the html but a flag instead for example isLogged init to false and update it when the user successfully logged.
Your ng-if will be : *ngIf="!isLogged | async"

angular 2 md-sidenav display list items on click in the dashboard

I have created a sidebar nav menu in angular 2 something like shown in the pic below.
https://imgur.com/a/hj4zO
UPDATE:
But when i click on the link in the category list, i get all the products relevant to clicked category. But it is not displaying to the right in the dashboard.
My logic to display the products is in another component: dashboard.component.html
<div id="products" class="row list-group">
...
</div>
But on click of an item in sidebar (from sidebar component), i m not sure where to put the above div, so i get all the products filtered based on the click to my dashboard area.
Basically when i onclick getProducts (from one component: sidebar, the function from another component : dashboard should be called)
sidenav.component.html
<md-sidenav-container fullscreen>
<md-sidenav #sidenav mode="side" class="example-sidenav" [ngStyle]="{ 'width.em': sidenavWidth }" opened="true" (mouseover)="increase()"
(mouseleave)="decrease()">
<md-nav-list>
<md-list-item routerLinkActive="active">
<md-icon md-list-icon>room_service</md-icon>
<div fxFlex="10"></div>
<div *ngIf="sidenavWidth > 6" class="sidenav-item">
<p class="lead">Smiles 4 abc </p>
<div class="catList" id="catList">
<a *ngFor="let cat of categories; let i = index" href="javascript:; " class="list-group-item " (click)="getProducts(cat._id,i)">{{cat.category_name}}</a>
</div>
</div>
</md-list-item>
</md-nav-list>
</md-sidenav>
<div class="example-sidenav-content">
<router-outlet></router-outlet>
</div>
</md-sidenav-container>
sidenav.component.ts
ngOnInit() {
this.qty[1]=1;
this.catService.getMaincategories('Yes').subscribe(
(mainCategories) => {
this.categories = mainCategories.mainCategories
let firstCatId = this.categories[0]._id;
this.getProducts(firstCatId,0)
}
);
this.navbar.ngOnInit();
}
getProducts(category_id, i) {
console.log('prod clicked..');
this.productService.getProductsOncategory(category_id).subscribe(
(products) => {
this.products = products.products
}
);
}
product.service.ts
getProductsOncategory(category_id){
let catUrl=this.domain+"products/getProductsOncategory"
let headers = new Headers();
headers.append('Content-Type','application/json');
let catIdObj = JSON.stringify({category_id:category_id,product_status:'Yes'})
return this.http.post(catUrl,catIdObj,{headers:headers})
.map((response:Response)=>response.json())
.catch(this.handleError);
}
app.component.html
<sidenav></sidenav>
Logic is very simple in Angular for dealing these type of problem. Follow following steps:
Create a Subject in your service:
public products$ = Subject<{}>(); // use model instead of {}
In your sidenav component inject service and emit product data.
this.service.products$.next(data)
In your dashboard component subscribe to subject.
this.service.products$.subscribe(data => { console.log(data)});
Hope it will help.