Angular Template not updating after http request - javascript

I am can't get my Angular template to update after i sent out a postrequest,
Here's the component:
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'example',
templateUrl: './example',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ExampleComponent implements AfterViewInit, OnInit {
public mailResult: String;
constructor(private apiService: ApiService) {
this.mailResult = 'notsent'; //updating in template
}
onSubmit() {
this.mailResult = 'pending'; // updating in template
this.apiService.testPostRequest().subscribe((res)=>{
console.log("res", res); // working fine loging res
this.mailResult = 'requestsucess'; // not update in template
});
}
}
and that's the template:
<div class="example-component">
<h1>stateTest {{this.mailResult}}</h1>
</div>
and the apiService
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { of } from 'rxjs';
#Injectable()
export class ApiService {
constructor(private http: HttpClient) {}
testPostRequest () {
return this.http.post('http://localhost:1337/email', {});
}
}
All I need is to update this.mailResult in my Template after the request was successful, all is working fine, but the value in the template just won't update after the request. Any Ideas where the Issue might be hidden?

It's not working because you have your component's change detection set to 'onPush' and mailResult is not decorated with #Input (which it obviously shouldn't).
So, to fix this, you first need to add a changeDetector to your class:
constructor(private apiService: ApiService, private changeDetector: ChangeDetectorRef) {
this.mailResult = 'notsent'; //updating in template
}
And then you use that changeDetector's markForCheck function to let angular know that something has changed and it needs to update the view:
this.apiService.testPostRequest().subscribe((res)=>{
console.log("res", res); // working fine loging res
this.mailResult = 'requestsucess'; // not update in template
this.changeDetector.markForCheck();
});

Related

Call JS function after HTTP request is completed and rendered in Angular

I would like to call a function from external JS file to handle elements which are in a component (content coming from an HTTP request). The script is loading fine but way before the HTTP response so I cannot use my function. Here is my code:
import { Component, OnInit } from '#angular/core';
import { HttpService } from '../http.service'
import * as $ from 'jquery';
declare const myFunc: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
items: object;
constructor(private _http: HttpService) { }
ngOnInit(): void {
this._http.myList().subscribe(
data =>{
this.items= data;
console.log(this.items);
},
myFunc(),
)
}
}
Any idea how I can load my script after all the data of the component is loaded?
thanks!
If I understood your problem correctly, you should leverage ngAfterViewInit. This method is called once your view is initialized, and by this time data returned from api is used in html.
export class HomeComponent implements OnInit, AfterViewInit {
items: object;
constructor(private _http: HttpService) { }
ngOnInit(): void {
this._http.myList().subscribe(
data => {
this.items = data;
console.log(this.items);
}
)
}
ngAfterViewInit() {
myFunc();
}
}
Your current func call doesn't make sense as it will work as the error callback function of subscribe as pointed by #Robin.
ngAfterViewInit

Share service API data between initial components in Angular 6

I'm trying to have a navbar with categories and a home component that also uses those categories. I don't want to have to call my API twice and I will use that same categories variable in other places. I tried doing the following:
Data Service
This service gets the data from the api url and returns the subscribable object.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) { }
getCategories(){
return this.http.get(this.api_url+'/categorylisting/?z=1');
}
getZones(){
return this.http.get(this.api_url+'/zones/');
}
}
Navbar Component
The Navbar component makes use of the categories variable to show the different options, this works fine since the subscribe is in this component.
import { Component, OnInit } from '#angular/core';
import { trigger, state, transition, animate, style } from '#angular/animations';
import { DataService } from '../data.service';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss'],
animations: [
trigger('slideInOut', [
state('in', style({
overflow: 'hidden',
height: '*'
})),
state('out', style({
overflow: 'hidden',
height: '0px'
})),
transition('in => out', animate('400ms ease-in-out')),
transition('out => in', animate('400ms ease-in-out'))
])
]
})
export class NavbarComponent implements OnInit {
categories: Object;
constructor(private data:DataService) { }
ngOnInit() {
this.data.getCategories().subscribe( data => {
this.categories = data
for(let category in this.categories){
this.categories[category].productsOpen='out';
for(let product in this.categories[category].product){
this.categories[category].products[product].active = false;
}
}
this.data.categories = this.categories;
});
}
openProducts(index){
this.categories[index].productsOpen = this.categories[index].productsOpen === 'out' ? 'in' : 'out';
}
setActiveProduct(index, productIndex){
for(let category in this.categories){
for(let product in this.categories[category].products){
this.categories[category].products[product].active = false;
}
}
this.categories[index].products[productIndex].active = true;
}
}
Home Component
My Home component also makes use of the categories variable, so I want to know how I can get it here since it is always undefined even if it changes in the service.
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
categories: Object;
constructor(private data:DataService) { }
ngOnInit() {
this.categories = this.data.categories;
}
}
Am I doing this right? I'm used to react and redux and in there the render method would run every time a setState was called to change the state, when does angular know when the component's variables have changed? I just want to save a global variable with my data so I can reuse it without calling the API every time. Thank You.
You can cache the observable in your service like:
export class DataService {
someProperty;
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) { }
getCategories(){
if(!this.someProperty) {
this.someProperty = this.http.get(this.api_url+'/categorylisting/?z=1');
}
return this.someProperty;
}
}
You can also go for angular Http interceptors else you can also opt for rxjs operator shareReplay
You could try calling your API in the constructor of the DataService
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) {
this.getCategories().subscribe(data => this.categories = data);
}
getCategories(){
return this.http.get(this.api_url+'/categorylisting/?z=1');
}
getZones(){
return this.http.get(this.api_url+'/zones/');
}
}
and then just get the categories in the NavBar component just as you did in the Home component.
ngOnInit() {
this.categories = this.data.categories;
}

What's the correct way to load and open a component inside a modal dialog in Angular4 --

I have a component called NewCustomerComponent and I want to load and display it through a modal popup in another page/component when a button is clicked. I have written the relevant bit of code [or so it seems]. But I am getting the following error --
this._childInstance.dialogInit is not a function
at ModalDialogComponent.dialogInit (modal-dialog.component.js:65)
at ModalDialogService.openDialog (modal-dialog.service.js:26)
at OrderNewComponent.newCl (order-new.component.ts:85)
My code is pretty simple too, in the component where I am trying to open the modal popup.
I'll just post the relevant portions --
import { Component, Inject, ViewContainerRef, ComponentRef } from
'#angular/core';
import { Http, Headers } from '#angular/http';
import { Router } from '#angular/router';
import { Observable, Subject } from 'rxjs';
import 'rxjs/add/operator/map';
import { CustomerSearchService } from '../../../shared/services/customer-
search.service';
import { ICustomer, Customer, CustomerD } from
'../../../shared/models/customer';
import { ModalDialogModule, ModalDialogService, IModalDialog } from 'ngx-
modal-dialog';
import { NewCustomerComponent } from
'../../../components/popups/customer/customer-new.component';
#Component({
selector: 'order-new',
templateUrl: './order-new.component.html'
})
export class OrderNewComponent {
public reference: ComponentRef<IModalDialog>;
constructor(private cusService: CustomerSearchService, private http:
Http, private modalService: ModalDialogService, private viewRef:
ViewContainerRef) {
}
ngOnInit(): void {
}
** this is where I am trying to load the newcustomercomponent and open it
in the popup. not working.
newCl() {
this.newC = true;
this.exiC = false;
this.modalService.openDialog(this.viewRef, {
title: 'Add New Customer',
childComponent: NewCustomerComponent
});
}
}
** edits. NewCustomerComponent code added for reference.
import { Component, Input, Output, EventEmitter, OnInit,
ChangeDetectorRef, Directive, ElementRef, Renderer, AfterViewInit }
from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { NgFor } from '#angular/common';
import { Observable } from 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/Rx';
import { PlatformLocation } from '#angular/common';
import { Http } from '#angular/http';
import { ICustomer, Customer } from '../../../shared/models/customer';
import { UserService } from '../../../shared/services/UserService';
import { IModalDialog, IModalDialogOptions, IModalDialogButton } from
'ngx-modal-dialog';
#Component({
selector: 'new-customer',
templateUrl: './customer-new.component.html'
})
export class NewCustomerComponent implements IModalDialog {
model: Customer = new Customer();
errors: any;
submitResponse: any;
actionButtons: IModalDialogButton[];
constructor(private userService: UserService, private http: Http) {
this.actionButtons = [
{ text: 'Close', onAction: () => true }
];
}
ngOnInit() {
}
dialogInit(reference: ComponentRef<IModalDialog>, options:
Partial<IModalDialogOptions<any>>)
{
// no processing needed
}
createCustomer() {
this.userService.createCustomer(this.model)
.take(1)
.subscribe(
(response: any) => {
this.submitResponse = response;
if (response.success) {
console.log('New customer added!');
}
else {
console.log('Unable to add customer!');
}
},
(errors: any) => this.errors = errors
);
return false;
}
cancelClicked() {
}
}
What did I do wrong here? Has it got something to do with the element reference I added in terms of the viewRef? Which portion is erroneous? What about that child component? Does it require to have some specific configuration/markup/component for this to work? I am very new to angular; I am not sure whatever the reason is.
Kindly help me rectify this scenario.
Thanks in advance,
Can you please ensure that the NewCustomerComponent is implementing the IModalDialoginterface. Also, if this is not the case can you please share the code of NewCustomerComponent as well.
edits
Looks like you have not defined the dialogInit method in the NewCustomerComponent and it didn't pop up before as you have not implemented the interface IModalDialog. I would request you to define the dialogInit method in the component class as suggested on the link.

Angular - communication from child-component to parent

I don't get i, how to communicate between components and services.. :(
I have read and tried a lot about even if some examples somehow work, I do not understand why (?)
what I want to achieve:
I have one parent and two child-components:
dashboard
toolbar
graph
in the toolbar-component I have a searchfield, which gets it's result from a external source (works via service).. when the result arrives, I need to trigger the updateGraph()-Method in the graph-component
toolbar.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
import { FormControl } from '#angular/forms';
import { WebsocketsService } from '../../../services/websockets/websockets.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss'],
providers: [WebsocketsService, DataService]
})
export class ToolbarComponent implements OnInit {
#Output() newGraphData: EventEmitter<boolean> = new EventEmitter();
searchField: FormControl;
search: string;
private isNewGraph = false;
constructor(private _websocketsService: WebsocketsService, private _dataService: DataService) {
}
ngOnInit() {
this.searchField = new FormControl();
this.searchField.valueChanges
.subscribe(term => {
this.search = term;
});
}
private applySearch() {
const res = this._websocketsService.sendQuery(this.search);
this._dataService.setGraphData(res);
this.newGraphData.emit(true);
this.search = '';
this.searchField.reset();
}
}
graph-component.ts
import { Component, OnInit} from '#angular/core';
import { HttpService } from '../../../services/http/http.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'graph',
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss'],
providers: [HttpService, DataService]
})
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
ngOnInit() {
}
public renderResult() {
console.log( this._dataService.getGraphData() );
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class DataService {
private graphData: Subject<string> = new Subject<string>();
public setGraphData(data) {
this.graphData.next( data );
}
public getGraphData() {
return this.graphData;
}
constructor() { }
}
I simply want ´renderResult()´to be executed after the searchresult has been written to ´graphData´. please help i am confused.
If I understand, you want communication between components and service.
A[component] (make a information) -----(notification)-----> B[service] ----(send)----> C[component] (consume the information)
It's correct? Let's go.
You need create a subscription of graphData(data.service.ts) in GraphComponent.
import { Subscription } from 'rxjs/Subscription';
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
private subscription: Subscription;
ngOnInit() {
this.subscription = this._dataService.getGraphData().asObservable().subscribe((data) => {
console.log(data);
});
}
}
Look here to help you.
http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject
Short answer, I think you need to subscribe to the getGraphData subject, something like this (NOT RECOMMENDED):
public renderResult() {
this._dataService.getGraphData().subscribe(d => {
console.log(d)
});
}
It is not recommended as per the lead of RxJS says: https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
Better answer, create an observable in your service and subscribe to that instead.
data.service.ts
graphObservable = this.graphData.asObservable();
graph-component.ts
public renderResult() {
this._dataService.graphObservable().subscribe(d => {
console.log(d)
});
}

(Angular2) JSON data (http.get()) is undefined, and data is not updated in the component

My http-data.service accepts json for output in the component template. Initially, the console shows that the first few calls are given undefined, and the following calls are already taking json, but also if you check the component, then the component shows that the method that outputs the data to the component is called only once and since the data has not yet arrived it writes undefined , But not updated after the arrival of json. Help please understand why? Thank you
My http-data.service:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import {Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class HttpService{
constructor(private http: Http) {}
getDataOrganizations(): Observable<any[]>{
return this.http.get('http://localhost:3010/data')
.map((resp:Response)=>{
let dataOrganizations = resp.json().organization;
return dataOrganizations;
});
}
getDataModules(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModules = resp.json().modules;
return dataModules;
});
}
getDataPresets(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataPresets = resp.json().presets;
return dataPresets;
});
}
getDataModuleItems(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModuleItems = resp.json().module_items;
return dataModuleItems;
});
}
}
My data-all.service
import { Injectable, EventEmitter } from '#angular/core';
import {Response} from '#angular/http';
import { ModuleModel } from './model-module';
import { ModuleItemsModel } from './model-module-items';
import data from '../data/data-all';
import { PriceService } from './price.service';
import { HttpService } from './http-data.service';
#Injectable()
export class ModuleDataService {
constructor(private priceService: PriceService, private httpService: HttpService){
this.dataMinMaxSum = {minSum: 0, maxSum: 0}
}
private currentPopupView: EventEmitter<any> = new EventEmitter<any>();
private dataModules: ModuleModel[] = this.getDataModules();
private dataMinMaxSum: {};
private dataCalculateVariationOrg: any[];
private dataChangeExecutor: any[];
subscribe(generatorOrNext?: any, error?: any, complete?: any) {
this.currentPopupView.subscribe(generatorOrNext, error, complete);
}
calculte(){
return this.priceService.getDataPrice();
}
getDataModules(){
this.httpService.getDataModules().subscribe(((modules)=>{this.dataModules = modules; console.log(this.dataModules);}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
---------------------------------------------------------------------------
}
My left-block.component
import { Component, OnInit} from '#angular/core';
import { ModuleDataService } from '../../service/data-all.service';
import { ModuleModel } from '../../service/model-module';
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit{
modules: ModuleModel[];
constructor(private modulesAll: ModuleDataService){}
ngOnInit(){
this.modules = this.modulesAll.getDataModules();
console.log("view");
console.log(this.modulesAll.getDataModules());
}
onToggle(module: any){
this.modulesAll.toggleModules(module);
}
}
My left-block.component.html
<div class="modules-all">
<div class="modules-all-title">Все модули</div>
<div class="module-item" *ngFor="let module of modules" [ngClass]="{ 'active': module.completed }" (click)="onToggle(module)">{{module?.title}}</div>
</div>
In the component this.modulesAll.getDataModules () method is why it is executed only once without updating (write in console => undefined), if there are any thoughts, write, thanks.
This behaviour is due to the .subscribe() method does not wait for the data to arrive and I'm guessing you already know this. The problem you're facing is because, you have .subscribe to the getDataModules() service in the wron place. You shouldn't subscribe to a service in another service (at leat in this case). Move the subscribe method to the left-block.component and it should work.
getDataModules() {
this.httpService.getDataModules().subscribe(((modules) => {
this.dataModules = modules;
console.log(this.dataModules);
}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
It should look somethig like this:
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit {
modules: ModuleModel[] = new ModuleModel();
constructor(private modulesAll: ModuleDataService, private httpService: HttpService) {}
ngOnInit() {
this.getDataModles();
//this.modules = this.modulesAll.getDataModules();
console.log("view");
//console.log(this.modulesAll.getDataModules());
}
onToggle(module: any) {
this.modulesAll.toggleModules(module);
}
getDataModules(): void {
this.httpService.getDataModules().subscribe(((modules) => {
this.modules = modules;
console.log(this.dataModules);
}));
}
}

Categories

Resources