Update HTML without using field in Component class - less stateful way - angular6

Say we have a Component class like so:
#Component({
selector: 'app-comp',
})
export class Component {
v: any;
template: `
<div>{{v.bar}}</div>
`
constructor(){}
onChange(v: any){
this.v = v;
}
}
my question is - instead of using v as a class property - is there any way to update the HTML without having to assign v to the instance?
Instead of calling this.v = v, perhaps there is some Angular call we can use, this.updateView({v}); - this would be less stateful way of doing it I suppose.

Related

How to set an Input property without binding in Angular

I have a simple Angular component which takes a debounce time as an Input parameter. The parameter type is a number with a default value equals to 0. It is used later on as a debounceTime value.
input.component.ts
import { AfterViewInit, Component, Input } from '#angular/core';
import { debounceTime } from 'rxjs';
#Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss']
})
export class InputComponent implements AfterViewInit {
#Input() debounce: number = 0;
ngAfterViewInit(): void {
//(...).pipe(debounceTime(this.debounce)).subscribe(...);
}
}
I would like to pass the debounce value from the parent component without binding to the local variable or constant.
app.component.html
<app-input debounce="200"></app-input>
It doesn't work because debounce value comes as a string type from a template. It does work when I use binding to the local constant. A console error confirms the type.
Error: src/app/app.component.html:3:12 - error TS2322: Type 'string' is not assignable to type 'number'.
I can disable strict templates checks as this is a default for Angular 15 but rxjs "debounceTime" function won't accept a string parameter.
How can I pass a number directly from the template without using bindings?
You can just use [] for input to pass a number like this:
<app-input [debounce]="200"></app-input>
you'll now get a number.
Use a setter
debounce: number = 0;
#Input() set _debounce(value:any){
this.debounce=+value;
}
Or use
.pipe(debounceTime(+this.debounce)
NOTE: The + is a confortable way to parse a string to number

error TS2740 on Angular when trying to load .json

I am a novice in Angular/Typescript and I am trying to build a dynamic page with the champion_info.json in order to display all the data in a list. I tried to upload the data from my json file but I have this error and I don't know what to do about it. I watched every possible youtube video about this subject but I still can't find an answer.
How can I simply load my data from the .json file and use it in order to display it in a list ?
This is my hero.component.ts :
import { Component, OnInit } from '#angular/core';
import * as Heroes from "src/hero_json/champion_info.json";
interface heroes1 {
title: String;
id: Number;
name: String;
key:String;
}
#Component({
selector: 'app-hero',
templateUrl: './hero.component.html',
styleUrls: ['./hero.component.css']
})
export class HeroComponent implements OnInit {
liste !: heroes1 = Heroes;
constructor() {
}
ngOnInit(): void {
}
}
This is the error
And this is where you can find the champion_info.json file: https://www.kaggle.com/datasnaek/league-of-legends?select=champion_info.json
Probably the problem is you need to add
declare module "*.json" {
const value: any;
export default value;
}
in a file in the app folder as
json-typings.d.ts
Anyways here is the repo I create to answer your question with basic and complete way to build this app you can clone a try to u
repor basic app
First, as you have indicated above you need to get .data from the JSON object, in your component class:
heroes = json_data.data;
In your template use the angular keyvalue pipe to parse and display data where you have a list of objects rather than an array.
<div *ngFor="let hero of heroes | keyvalue">
{{ hero.value.title }}
{{ hero.value.name }}
// etc
</div>

Angular - What's the best way to include html in multiple components?

I need to put a loading in multiple components of my project. So instead of putting the same HTML over and over again across the components, I need to know what's the best way to not repeat code, but I don't know if this is correct thing to do. I have created a component called loading-graphic, which I bind it in every HTML file of the respective components. I read about ngTemplateOutlet and ngContent, but to be honest it doesn't make sense in my head to use it for this case (and I don't get it too... I'm a beginner on it). So, on what should I bet? Thanks.
Base on your question, I think creating Reusable Components with NgTemplateOutlet would be the best solution to avoid repeating HTML in different component Templates. It allows you to pass parameters base on your host component and makes your Angular app easier to test and develop since it sllows easily modified reusable component for various use cases without having to modify individual components itself.
Since you are a begginer I am going to Illustrate simple way of using NgTemplateOutlet, however dive deep later on Templates and Stamps.
Imaging you have a reusable Search component where you want to hide a check box base on the parent component. Your Template will look like below.
we pass data from the parent component to the child/Search component using #Input and property binding, so we define which checkboxes to hide base on Parent component.
here is the code sample for Search Component
search.component.ts
======================
import { Component, OnInit, Output, EventEmitter, Input } from '#angular/core';
#Component({
selector: 'app-search',
templateUrl: './app-search.component.html',
styleUrls: ['./app-search.component.css']
})
export class AppSearchComponent implements OnInit {
accountName: string = '';
#Output() accountSearchChange = new EventEmitter<string>(); //1. Event Binding to pass data from Child to Parent Component ->Int
#Input() searchMode: 'account' | 'holder' | 'distribution' = 'account'; //Use NgTemplateOutlet for reusable componenet
constructor() { }
ngOnInit() {
}
//2. Event Binding to pass data from Child to Parent Component ->Invoke Emit
doSearchFilter(searchText: string) {
console.log('Search Child: doSearchFilter -> ' + searchText);
this.accountSearchChange.emit(searchText);
}
clearFilters() {
console.log('Account Search: Clear Filter is called');
this.accountName = '';
}
}
search.component.html
=====================
<ng-container [ngSwitch]="searchMode">
<div class="input-full-width" *ngSwitchCase="'account'">
<mat-checkbox class="example-container check-full-width">Show active and pending only</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only holders with missing requirements</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only active Holders</mat-checkbox>
</div>
</ng-container>
I am using Search component inside Account component and below is the code sample.
in HTML file i am referring app-search css selector and pass the search Mode defined in ts.
import { Component, OnInit, ViewChild, AfterViewInit } from '#angular/core';
import { MatSort, MatTableDataSource, MatPaginator } from '#angular/material';
import { Router } from "#angular/router";
import { Observable } from 'rxjs';
import { AccountService } from 'src/app/core/services/account.service';
import { Deal } from 'src/app/core/models/deal';
#Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css']
})
export class AccountsComponent implements OnInit, AfterViewInit {
displayedColumns: string[] = ['accountId', 'accountShortName', 'accountType'];
public dealDataSource = new MatTableDataSource<Deal>();
dealsObservable: Observable<Deal[]>;
searchMode = 'distribution';
isLoadingResults = true;
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
constructor(private router: Router, private api: AccountService) { }
......................................................
<app-search (accountSearchChange)='doFilter($event)' [searchMode]="searchMode"></app-search>
Hope this is clear.
i think loading component is not a bad Idee. Use loading component in your app-root component. You can use a loading.service and interceptor to display or hide the component. You will get automatically a Loading Indicator for each API call.
sample: https://medium.com/#zeljkoradic/loader-bar-on-every-http-request-in-angular-6-60d8572a21a9

Data showing as undefined when I am trying to pass data from parent component to child components

I am trying to pass data from parent component to child components, but I am getting data is undefined,
In my below code
parent component
here part_data I have declared in service
this._PartService.GetDataValues(this.search.item, this.search.filter_type, release_part).subscribe(
result => {
this.resultData = result;
this._PartService.part_data = this.resultData.data
})
child component
Here I am using observer
this.searchService.observer.subscribe(search => {
this.part = search.item;
this.specification = this._PartService.part_data.partMasterDataCompleteList[0];
})
Here I am getting PartMasterDataCompleteList[0] is undefined
Use this as the child component:-
#Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
#Input() hero: Hero;
constructor() {}
ngOnInit() {}
}
When you need to feed data to the component, use the below code:-
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
The hero property is #Input which receives data from the outer environment.
For more information, refer to Angular tutorial:-
https://angular.io/tutorial/toh-pt3
You can pass your data in sub component and get using #Input method.
There is one other way to pass data from one component to other component.
Click here

Access constant in HTML in Ionic 3

My project is in Ionic 3. I have a data provider class for storing constants.
ex
export const CONST1 = 1;
export const CONST2 = 2;
#Injectable()
export class DataProvider {
constructor() {
}
}
In my Display Page, I want to use the constant data. So If I do
import * as Data from './../../providers/data/data';
I can directly access Data.CONST1 in my Display.TS file.
How do access the values in my Display.HTML file? There Data.CONST1 is not working.
One way to do it would be by assigning the Data object to a public property of that component:
import * as Data from './../../providers/data/data';
// ...
#Component({
selector: 'page-display,
templateUrl: 'display.html'
})
export class DisplayPage {
public constants = Data;
// ...
}
And then in the view
<p>{{ constants.CONST1 }}</p>
EDIT:
I want to use it as an input parameter and <ion-input
maxLength="constants.CONST1"></ion-input> does not work.
That's actually because you need to use attribute binding, to let angular know that the expression between "" must be interpreted:
<ion-input [attr.maxLength]="constants.CONST1"></ion-input>