Angular2 view not updating after model is set from another component - javascript

I have a simple web app that has an app model and a couple of components. When I update an array that I have in my model the view where that model is being consumed does not update. When I console log the model array I can see the model being updated just not the view. Any help would be greatly appreciated. Below please have a look at what I currently have.
overview.component.ts
import { Component, OnInit } from '#angular/core';
import { AppModel } from '../models/app-model';
#Component({
selector: 'app-overview',
templateUrl: './overview.component.html',
providers: [AppModel]
})
export class OverviewComponent implements OnInit {
constructor(public AppModel:AppModel) { }
ngOnInit() {}
}
app-model.ts
export class AppModel {
myArray:Array<any> = [];
constructor(){}
}
overview.component.html (This is the view that is not being updated when the model gets updated)
<td *ngFor="let dataItem of AppModel.myArray">
<span{{ dataItem }}</span>
</td>
This is how I am updating the array in the app model from another component
import { Component, OnInit } from '#angular/core';
import { Http, Response } from '#angular/http';
import { AppService } from '../services/app.service';
import { AppModel } from '../models/app-model';
#Component({
selector: 'other-component',
templateUrl: './other-component.component.html',
providers: [AppService, AppModel]
})
export class OtherComponent implements OnInit {
constructor(private http: Http, public AppService:AppService,public AppModel:AppModel) {}
private updateModel() :void {
this.AppModel.myArray = someArray;
}
ngOnInit() {}
}

For a service you get an instance per provider.
If you add a provider to a component, you get as many service instances as you have component instances. Only the component itself and it's children can inject a provider from a component.
You either need to provide the service on a common parent component or in #NgModule().
With providers only in #NgModule() you get a single instance for your whole application

Related

How do I pass data from one component to another (New Browser Tab) in angular?

I'm new to angular and I don't know how to pass data between two components using routers. This is my first component view,
when I press view report button I need to call another component with the first component data. This is my first component view report click button code.
<button type="button" (click)="onFuelViewReport()" class="btn btn-success ">
<b>view Report</b>
</button>
when clicking the button it calls onFuelViewReport() function in the first component and using this function it opens the second component view with a new browser window (tab). What I want is to pass data from the first component to the second component from here. Please help me to do this.
onFuelViewReport() {
this.router.navigate([]).then(result => {
window.open("/pages/view-report", "_blank");
});
}
If you want to share data from child component to parent component, you can use #Output event emitter or if your are trying to share data within unrelated components, you can use BehaviourSubject (This also works in case of parent to child component communication and vice versa).
Child to Parent: Sharing Data via Output() and EventEmitter
parent.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
child.component.ts
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "Hola Mundo!"
#Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
Unrelated Components: Sharing Data with a Service
data.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
parent.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
sibling.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}
The window.open looks absolutely awful. Use this.router.navigate(['/heroes']);.
So if I understand correctly you have a list of items and when you click on one of the items, the details page of that item should open?
Best practice is to allow the detail route to have a property to set. the Angular Routing & Navigation page is very complete. It shows that you should use :id - { path: 'hero/:id', component: HeroDetailComponent }. When you open the detail page, you get the id variable and then get the data for it.

Angular 8: send event from one component to sibling component

I have a sidebar component and a page component.
The sidebar component has a #ViewChild which is an ngbAccordion from Angular Boostrap. I want to trigger its collapseAll method from the page component.
So the sidebar has
#ViewChild('webAccordion', { static: false })
webAccordion: NgbAccordion;
#ViewChild('pageAccordion', { static: false })
pageAccordion: NgbAccordion;
collapseAllAccordions() {
this.webAccordion.collapseAll();
this.pageAccordion.collapseAll();
}
When the "page" component loads, I want to emit an event to the "sidebar" component that triggers my collapseAllAccordions function.
I know how to do this with parent/child components, and most of the stuff I can find with Google and here on SO discusses parent/child situations. Except in my case they are sibling components. I'm not sure how to hand siblings.
You can use a service:
Inject a service into two sibling components.
Add an emitter or an Observable to the service.
Add a function in the service to change the value of the Observable / emit a new value if your using an emitter.
Use the function in your "page" component.
Subscribe to the emitter or the Observable in your "sidebar" component and trigger collapseAllAccordions.
You could use intermediate singleton service between these components and share the data/actions. For example,
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.scss']
})
export class SideBarComponent implements OnInit {
constructor(private service: AppService) { }
onClick() {
this.service.collapse();
}
ngOnInit(): void { }
}
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss']
})
export class PageComponent implements OnInit {
constructor(private service: AppService) {
// Create a observable object which listens to service and
// change the behaviour of current page component and vice versa
}
ngOnInit(): void { }
}
If you require further assistance please create stackblitz or codesandbox to replicate this issue.

Is there a possibility to load services before components?

I have a component SurveyComponent and a service SurveyService. The SurveyService loads some data form JSON.
I add the SurveyService into the constructor from SurveyComponent.
constructor(public surveyService: SurveyService) {}
In the constructor from SurveyService I load some data.
constructor(private storageService: StorageService) {
this.finishedSurveys = [];
this.loadCurrentSurvey();
this.loadFinishedSurveys();
}
I put also the SurveyService into the app.module:
.....providers: [SurveyService].....
But the component loads before service so I haven no data in my component. Why does it happen? Can I solve this issue?
you can use resolve for the angular router. E.g.
Make a class APIResolver like
import { Injectable } from '#angular/core';
import { APIService } from './api.service';
import { Resolve } from '#angular/router';
import { ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class APIResolver implements Resolve<any> {
constructor(private apiService: APIService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.apiService.getItems(route.params.date);
}
}
Then, resolve your corresponding router like
{
path: 'items',
component: ItemsComponent,
resolve: { items: APIResolver }
}
You can refer to this link to use OnInit
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnInit {
ngOnInit() {
you call the service that you want to execute
}
}

wait till the request completes in angular

In my angular application, I have one parent component i.e. Dashboard Component having 2 Sub Components i.e. Analytics Component & Stats Component. My dashboard.component.html looks like this
<div class="row">
<div class="col-lg-6"><app-analytics></app-analytics></div>
<div class="col-lg-6"><app-stats></app-stats></div>
</div>
I am also using a Global Component which is available to all the components works like a global storage. Now in the dashboard.component.ts. I am making a HTTP call to the server, getting the data and saving it into the Global component.
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
constructor(private http : Http){
};
ngOnInit(){
this.http.get('/api/getUserPreferences').subscribe(response=>{
var data = response.json();
Global.userPreferences = data.preferences;
})
}
User preferences I am using in the sub components i.e. Analytics Component and Stats Component.
analytics.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-analytics',
templateUrl: './analytics.component.html',
styleUrls: ['./analytics.component.css']
})
export class AnalyticsComponent implements OnInit {
public analyticsUserPreferences : any;
constructor(private http : Http){
};
ngOnInit(){
this.analyticsUserPreferences = Global.userPreferences.analytics;
// Printing just for the question purpose
console.log(this.analyticsUserPreferences);
}
}
stats.component.ts
import { Component, OnInit } from '#angular/core';
import { Global } from 'app/shared/global';
import { Http } from '#angular/http';
#Component({
selector: 'app-stats',
templateUrl: './stats.component.html',
styleUrls: ['./stats.component.css']
})
export class StatsComponent implements OnInit {
public statsUserPreferences : any;
constructor(private http : Http){
};
ngOnInit(){
this.statsUserPreferences = Global.userPreferences.stats;
// Printing just for the question purpose
console.log(this.statsUserPreferences);
}
}
Now, in these sub components. I am getting undefined every time in the console. Is there any way that it should wait till the Global.userPreferences doesn't contain the values. Or is there other way to do the same. I just want that it should wait till the http request is completed and print whenever the values are store inside the Global.userPreferences.
You can use the async pipe and an *ngIf to wait for the http request to be completed before rendering the child components. Then use binding to pass the data down to the child component and receive it with an #Input().
dashboard.component.ts
public userPreferences$: Observable<any>;
ngOnInit(){
this.userPreferences$ = this.http.get('/api/getUserPreferences').subscribe();
})
dashboard.html
<app-analytics *ngIf="userPreferences$ | async as userPreferences" [userPreferences]="userPreferences"></app-analytics>

Angular 4+ send service from child to parent via interface

I was searching for answer several hours..
Is possible in angular to send from child to parent service via interface?
parent component
child component (extends parent)
interface for service
service (e.g. locationService) (implementing iface above)
Child extends from Parent
constructor(public locationService: LocationService) {
super(locationService); //parent
}
And parent looks like:
constructor(generalService?: IService) {
this.myService = generalService;
}
and than I want to do something like: this.myService.doLogic();
I got runtime error: Error: Uncaught (in promise): Error: Can't resolve all parameters for ParentComponent: (?).
Thanks for any hint or help..
The best way to design component inheritance in Angular framework is passing Injector instance to base component and injecting dependencies in the base component.
Base component class implementation:
export class BaseComponent {
protected locationService: LocationService;
constructor(injector: Injector) {
this.locationService = this.injector.get(LocationService);
}
}
Child component:
import { Component, Inject, Injector } from "#angular/core"; // Import injector from #angular/core
#Component({
selector: "child-component",
templateUrl: "child-component-template.html",
styleUrls: [
"./child-component-styles.scss"
]
})
export class ChildComponent extends BaseComponent{
constructor(
#Inject(Injector) private injector: Injector
) {
// Pass injector instance to base class implementation
super(injector);
}
}
Now in the child component you can use LocationService by calling this.locationService.doSomethind();
You should not have to extend Component, By extending component it brings only class property. So change parent from Component to simple class.
interface IService {
doLogic();
}
#Injectable()
export class LocationService implements IService {
doLogic() {
console.log('service goes here...');
}
}
export class ParentComponent {
constructor(public locationService?: IService) {
}
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent extends ParentComponent {
constructor(locationService: LocationService) {
super(locationService);
this.locationService!.doLogic();
}
}

Categories

Resources