I've got this:
#Component({
selector: 'app-edit',
templateUrl: './altera-estatutos.component.html',
styleUrls: ['./altera-estatutos.component.css']
})
export class AlteraEstatutosComponent implements OnInit {
id: String;
professor: Utilizador;
updateForm: FormGroup;
professores: Utilizador[];
avaliacaoEditar : any = {};
constructor(private avalService: AvaliacaoService, private userService: UtilizadorService ,private router: Router, private route: ActivatedRoute, private snackBar: MatSnackBar, private fb: FormBuilder) {
this.createForm();
}
createForm(){
this.updateForm = this.fb.group({
docente: ['', Validators.required],
});
}
ngOnInit() {
this.route.params.subscribe(params => {
console.log(params);
this.id = params.id;
if( this.id != undefined){
this.userService.getUtilizadoresById(this.id).subscribe( (res: any) => {
console.log(res);
this.professor = res;
this.updateForm.get('docente').setValue(this.professor);
console.log(this.updateForm.get('docente').value);
});
}
});
this.userService
.getUtilizadores()
.subscribe((data: Utilizador[]) => {
this.professores = data;
console.log('Data requested ...');
console.log("-------------");
console.log(this.professor);
console.log("--------------");
});
}
editEstatutos(id){
this.router.navigate([`/app/altera-estatutos/${id}`]);
}
And this is the HTML
<form style="margin-left: 22%"[formGroup]="updateForm" class="edit-form">
<mat-form-field class="field-full-width">
<mat-select style="width:500px" placeholder="Docente" formControlName="docente" #docente>
<mat-option *ngFor="let disc of professores" [value]="disc">
{{disc.nome}}
</mat-option>
</mat-select>
</mat-form-field><br> </form>
<button style="margin-left:40%" mat-raised-button color="primary" (click)="editEstatutos(docente.value._id)"> Procurar Estatutos </button>
<br><br>
<span class="cgb">Adicionar </span><span class="cg">Estatuto</span>
Here's what happens:
When I run the page, I receive from the ID route the ID from an object. I look for it and put it in "professor", and get all "professores" into another array, to present on the page.
When I print variables like "this.professores" on log console, it is ok , but outside the subscribe they are undefined. What can I do? Why am I losing all the data?
The function "subscribe" is async, in this case, the function execution order is not the same in the code.
for solution try:
this.route.params.subscribe(params => {
console.log(params);
this.id = params.id;
if( this.id != undefined){
this.userService.getUtilizadoresById(this.id).subscribe( (res: any) => {
console.log(res);
this.professor = res;
this.updateForm.get('docente').setValue(this.professor);
console.log(this.updateForm.get('docente').value);
secondFunction()
});
} else {
secondFunction()
}
});
secondFunction() {
this.userService
.getUtilizadores()
.subscribe((data: Utilizador[]) => {
this.professores = data;
console.log('Data requested ...');
console.log("-------------");
console.log(this.professor);
console.log("--------------");
});
}
Related
I want to display the ngx-wheel using api but I'm having trouble displaying the data.
Here my Service :
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class RestServices {
restEndpoint:string = 'https://gorest.co.in/public/v2/users'
constructor(
private httpClient: HttpClient
) { }
async getServiceId() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
}
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions)
}
Here my Component :
private subscription: Subscription | undefined;
items: any = []
ngOnInit(): void {
this.subscription = this._restService.getServices()
.subscribe((res:any)=>{
let item = res
this.items = item.map((v:any) => ({
text: v.name,
id: v.id,
textFillStyle: "white",
textFontSize: "16"
}));
})
}
ngOnDestroy(): void {
this.subscription?.unsubscribe()
}
Here for html
<ngx-wheel #wheel [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>
I hope to find the answer here. Thank you
First, you don't need await, async and ,toPromise()... remove them and simply return
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions);
inside your component you should use your constructor only for simple data initialization: if you have to consume a rest api it is a better approach to move that piece of code inside the ngOnInit method:
items: any[] = []
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.restService.getServiceId().subscribe(response => {
console.log('response success: ', response);
this.items = response; //this may change a little based on your api
console.log('items: ', this.items);
}, errorLog => {
console.log('response error: ', errorLog)
});
}
The above solution is valid, you can enrich it by adding a *ngIf="isLoaded" on your html element and set to true the isLoaded INSIDE subscribe method. but if you prefer you can do the following in the component.ts
items$: Observable<any> = EMPTY;
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.items$ = this.restService.getServiceId();
}
then, in your html it would change to the following:
<ngx-wheel #wheel *ngIf="items$ | async as items" [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>
I am trying to show and hide HTML elements according to the service result. I am using *ngIf="messageService.getData() | async" but it isn't working to show or hide the element. I am using async, otherwise "Failure Message" is shown for a short time and then "Successful Message" is shown.
I have 2 tags like these:
<div *ngIf="messageService.getData() | async">Successful Message</div>
<div *ngIf="!messageService.getData() | async">Failure Message</div>
In the service I have a code like this:
export class MessageService {
constructor(private http: HttpClient) { }
public getData() {
return this.http.get("https://jsonplaceholder.typicode.com/todos/1")
.pipe(
map((response) => {
console.log("success");
}),
catchError(this.handleError)
)
}
private handleError(error: Response) {
console.log("handleError")
let errMsg: string;
errMsg = "error"
return Observable.throw(errMsg);
}
}
Here is the source code: https://stackblitz.com/edit/angular-iqr6az
in your service:
public getData() {
return this.http.get("https://jsonplaceholder.typicode.com/todos/1")
.pipe(
map((response) => {
return response; // return res
}),
catchError(this.handleError)
)
}
in your component:
export class MessageComponent implements OnInit {
isServiceAPIWorking: boolean;
todos;
loadingError$ = new Subject<boolean>();
constructor(private messageService: MessageService) { }
ngOnInit() {
this.todos = this.messageService.getData().pipe(
catchError((error) => {
// it's important that we log an error here.
// Otherwise you won't see an error in the console.
console.error('error loading the list of users', error);
this.loadingError$.next(true);
return of();
})
);
}
}
in your html:
<div>Show Message:</div>
<div *ngIf="todos | async">Successfull Message</div>
<div *ngIf="loadingError$ | async">Failure Message</div>
DEMO 🚀🚀.
Why bother with async pipes when you can just assign the data in your component?
// message.component.ts
class MessageComponent implements OnInit {
isServiceAPIWorking: boolean;
data: any;
constructor(private messageService: MessageService) { }
ngOnInit() {
this.messageService.getData()
.subscribe(response => {
this.isServiceAPIWorking = true;
// Assign the data
this.data = response;
},
error => {
this.isServiceAPIWorking = false;
})
}
}
// message.component.html
<div>Show Message:</div>
<div *ngIf="data">Successfull Message</div>
<div *ngIf="!data">Failure Message</div>
And there is a mistake in your service. If you use map without returning anything, you won't get any data in the end. Use tap if you want to do logging:
// message.service.ts
public getData() {
return this.http.get("https://jsonplaceholder.typicode.com/todos/1")
.pipe(
tap((response) => {
console.log("success");
}),
catchError(this.handleError)
)
}
Updated Stackblitz
This is a wrong usage of async pipe not in syntactical but in semantic way. Every time a change detection triggered, you are making an HTTP request.
Rather than checking with async pipe, you can store two flags (boolean variables) or one boolean variable for HTTP request and one variable for response.
Below example is using two flags.
export class MessageService {
isLoaded = false;
hasError = false;
constructor(private http: HttpClient) { }
public getData() {
this.isLoaded = false;
this.http.get("https://jsonplaceholder.typicode.com/todos/1")
.subscribe(
(response) => {
this.hasError = false;
this.isLoaded = true;
},
(error) => {
this.hasError = true;
this.isLoaded = true;
},
)
}
}
And in the template:
<ng-container *ngIf="isLoaded">
<div *ngIf="!hasError">Successfull Message</div>
<div *ngIf="hasError">Failure Message</div>
</ng-container>
I send the content of the "Galleries" constant to the API using "exportGalleries ()" but I do not know how to change the display of the server response instead of the local content in the "gallery.component" component
I will be ready for any help
gallery.component
#Component({
selector: 'app-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.scss']
})
export class GalleryComponent implements OnInit {
private galleryId: string;
private gallery: IGallery;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.galleryId = this.route.snapshot.paramMap.get('galleryId');
this.gallery = Galleries.find((item: IGallery) => item.galleryId === this.galleryId);
console.log("GALL");
console.log(this.gallery);
}
}
constructor(private http: HttpClient) {
this.title='Galeria';
this.description='Wakacje';
this.galleries = [];
this.http.get('http://project.usagi.pl/gallery',
this.httpOptions).toPromise().then((response: IGallery[]) => {
console.log(response);
this.galleries = response;
});
this.searchValue ='';
}
ngOnInit() {
}
setSearchValue($event) {
console.log($event);
this.searchValue=$event;
}
exportGalleries() {
Galleries.forEach((gallery: IGallery) => {
delete(gallery.galleryId);
this.http.post('http://project.usagi.pl/gallery', gallery,
this.httpOptions).toPromise().then((response: IGallery) => {
console.log('success', response);
this.galleries.push(response);
}, (errResponse) => {
console.log('error', errResponse);
});
});
}
I've tried to implement mat autocomplete (Angular Material) and the .TS code works and return Data from my Back end API.
BUT the autocomplete doesn't show the object automatically on the drop down list.
Follow my code:
export class VendaComponent implements OnInit {
public produtoAutoComplete: Observable<Produtos> = null;
public vendaForm = new FormControl();
vendas: Venda[] = [];
produtos:Produtos;
isLoading = false;
constructor(private vendasService: VendaService, private produtoService: ProdutoService, private toastr: ToastrService) { }
lookup(value: string): Observable<Produtos> {
return this.produtoService.search(value.toLowerCase()).pipe(
// map the item property of the github results as our return object
map(results => results.produtos),
// catch errors
catchError(_ => {
return of(null);
})
);
}
ngOnInit() {
this.produtoAutoComplete = this.vendaForm.valueChanges.pipe(
startWith(''),
// delay emits
debounceTime(300),
//map(options => options ? this.filter(options) : this.produtos.slice())
switchMap(value => {
if (value !== '') {
// lookup from github
return this.lookup(value);
} else {
// if no value is pressent, return null
return of(null);
}
})
);
}
Service code:
const API_URL = environment.apiUrl;
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }), responseType: 'text' as 'json'
};
#Injectable({
providedIn: 'root'
})
export class ProdutoService {
private produtoUrl = API_URL + 'produto/'; // URL to web api
dataChange: BehaviorSubject<Produto[]> = new BehaviorSubject<Produto[]>([]);
// Temporarily stores data from dialogs
dialogData: any;
produtos: Produto[] = [];
constructor(private httpClient: HttpClient) { }
And my html:
<form class="example-form">
<mat-form-field floatLabel="never">
<input matInput type="text" aria-label="Number" matInput [formControl]="vendaForm" [matAutocomplete]="auto">
<mat-placeholder class="placeholder">Search</mat-placeholder>
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let option of produtoAutoComplete | async" [value]="option.descProduto">
{{option.descProduto}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
I've tried everything, follow thousands of examples and nothing works. Like i said the service works and return my JSON, my problem is when I try to display the result.
after a lot of hours I found a solution for this question.
TS of Component:
export class VendaComponent implements OnInit {
public vendaForm = new FormControl();
vendas: Venda[] = [];
results: any[];
isLoading = false;
constructor(private vendasService: VendaService, private produtoService: ProdutoService, private toastr: ToastrService) { }
ngOnInit() {
this.vendaForm.valueChanges.subscribe(
term => {
if (term != '') {
this.produtoService.search1(term).subscribe(
data => {
this.results = data as any[];
console.log(data);
})
}
})
}
Data Service:
search1(term) {
const params = new HttpParams()
.set('descProduto', term)
.set('codigoBarras', '123');//Hardcode parameter just for tests
var listOfBooks= this.httpClient.get(this.produtoUrl + 'busca/', { params })
.pipe(
debounceTime(500), // WAIT FOR 500 MILISECONDS ATER EACH KEY STROKE.
map(
(data: any) => {
return (
data.length != 0 ? data as any[] : [{ "Produto": "No Record Found" } as any]
);
}
));
return listOfBooks;
}
HTML to display the Material AutoComplete:
<form>
<mat-form-field class="container">
<!-- ADD AN INPUT BOX OF TYPE TEXT AND LINK IT TO THE AUTO COMPLETE PANEL. -->
<input type="text" placeholder="Procure Produtos ..." matInput [formControl]="vendaForm" [matAutocomplete]="auto">
<!-- CREATE AUTO COMPLETE PANEL WITH OPTIONS. -->
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let result of results" [value]="result.descProduto">
{{ result.descProduto }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</form>
I hope it help somebody.
I have an Observable stream that obviously outputs an array into the subscribe block. I am trying to assign the response to an array variable to render the autocompletion results. Everything works fine except for the typescript error.
Autocomplete Component:
#Component({
selector: 'auto-complete',
styleUrls: ['auto-complete.component.scss'],
template: `
<div class="ac-container">
<div class="ac-input">
<input type="text" [(ngModel)]="query" (keyup)="filter()">
</div>
<div class="ac-results" *ngIf="results.length > 0">
<ul *ngFor="let item of results">
<li>
<a (click)="select(item)">{{item}}</a>
</li>
</ul>
</div>
</div>
`
})
export class AutoCompleteComponent {
#Input() fn: Function;
public query = '';
public results = [];
filter() {
let value = Observable
.from(this.query)
.throttleTime(20)
.map(value => value)
.distinctUntilChanged()
.map(search => this.fn(search))
.switch()
.subscribe(response => {
this.results = response;
}, error => {}
);
}
}
Parent Component:
#Component({
selector: 'auto-completor',
template: `<auto-complete [fn]="apiSearch"></auto-complete>`
})
export class AppComponent implements OnInit {
public results: any;
constructor(
private service: AppService
) {}
public apiSearch(term) {
return this.service.getSearchData(term);
}
ngOnInit() {
this.apiSearch = this.apiSearch.bind(this);
}
}
Error:
IED Error Indication:
I wish I could show examples of things I tried but all I did was googled. I have no idea. Thanks.
Edit / Additions
Fake DB/Http Response
import { Injectable } from '#angular/core';
#Injectable()
export class Database {
private FAILURE_COEFF = 10;
private MAX_SERVER_LATENCY = 200;
private getRandomBool(n) {
var maxRandomCoeff = 1000;
if (n > maxRandomCoeff) n = maxRandomCoeff;
return Math.floor(Math.random() * maxRandomCoeff) % n === 0;
}
public getSuggestions(text) {
var pre = 'pre';
var post = 'post';
var results = [];
if (this.getRandomBool(2)) {
results.push(pre + text);
}
if (this.getRandomBool(2)) {
results.push(text);
}
if (this.getRandomBool(2)) {
results.push(text + post);
}
if (this.getRandomBool(2)) {
results.push(pre + text + post);
}
return new Promise((resolve, reject) => {
var randomTimeout = Math.random() * this.MAX_SERVER_LATENCY;
setTimeout(() => {
if (this.getRandomBool(this.FAILURE_COEFF)) {
reject();
} else {
resolve(results);
}
}, randomTimeout);
});
}
}
App Service Converting the promise response to Observable
export class AppService {
constructor(
private database: Database
) {}
public getSearchData(term) {
return Observable.defer(() => {
return Observable.fromPromise(this.database.getSuggestions(term)
.then(function(res) {
return res;
})
);
})
}
}
The problem is that the Observable is not typed, because your function fn has no call signature. I cannot check it at the moment, but if you would give fn a lambda expression call signature, the observable will probably take over it's types, enabling you to assign it to results.
#Input() fn: (string) => string[];
Alternatively, you could type results as any, but that's just a quick and very dirty workaround to remove types altogether.