I have a dashboard, where I can type in data in a component and the data is going in a mongoDB. Then I get that data in other components and display it at the same site. Now I just have an update button under each component, that I can manually update every component. But I want to do that in this moment, where I submit my data, that is going to mongodb.
I tried it with a service, but it didn't worked out very well
This should be updated
import { UpdateServicenter code heree} from "../../../#core/data/update.service";
constructor(private http: HttpClient, private updateService: UpdateService,) {
//load it the first time if you refreshing the site
this.updateOrders();
//this should call updateOrders again if button is clicked
this.updateService.updateData()
}
updateOrders() {
//here I get the data from mongoDB and set it in my variables
}
Button (This function is called, if I push the button)
import { UpdateService} from "../../../#core/data/update.service";
constructor(private http: HttpClient,private updateService: UpdateService){}
submitButton(){
this.updateService.updateData()
}
Service
#Injectable()
export class UpdateService {
updateData(){
//this should call the function updateOrders()
}
}
I want to create a service, that can call every function or can be called, if the button is pushed, because I have more than one component which should be updated.
the method updateOrders() is an http call where you receive an observable object?
Great. Move this method into the service, save your data into variables in the service and then pass the data as input to each component, like this the ngOnChanges lifecycle hook will fire each time your data gets updated. e.g.
#Injectable()
export class UpdateService {
public obj; //variable you save your data
updateOrders() {
this.someObj = this.http<>.....
}
getObj():{
// do your stuff here if you need so
return this.obj;
}
}
<someComp [obj]="updateService.getObj()"></someComp>
export class SomeComponent {
#Input()
public obj;
ngOnChanges(changes:SimpleChanges){
//do your stuff here if you need so, or hust send them to html
}
}
Related
in home component, a line like ...<app-root [message]="hii"> is opening that app-root comp , with value to app-root component which has #input and {{message}} in html is working..
But i need to redirect to that app-root component instead of opening in current component.
any ways like "button onclick to redirect to that with [message]="hi" as data?
the best way to shared data between an unrelated component in the angular is, use the "BehaviorSubject" from rxjs library.
we imagine that we have two components that there are completely unrelated and we want to shared data between them.
first of all, you should create a service file.
Imagine that we have a service with the name of sharing-data.serice.ts :
import { BehaviorSubject, Subject } from 'rxjs';
import { Injectable } from '#angular/core';
#Injectable({
providedIn:'root'
})
export class SharingDataService {
public subject = new BehaviorSubject("");
getDataFromFirstComponent(x){
this.subject.next(x);
}
sharedDataWithSecondComponent(){
return this.subject.asObservable();
}
}
here we use BehaviorSubject class to make a pipe between unrelated components for transferring data.
in the first component.ts we inject the service by dependency injection, then make a method to use this service and fill the pipe by data :
constructor(
private service : SharingDataService,
) { }
sendDataOutOftheComponent(){
this.service.getDataFromFirstComponent(this.dataFormFirst);
}
now in the second component we just need to use this service again and after that subscribe to the method to get the data.
in the second component.ts we have the following code :
constructor(
private service : SharingDataService,
) { }
getDataFromFirstComponent(){
this.service.sharedDataWithSecondComponent()
.subscribe(data=>{
this.dataFromFirst= data;
})
}
with this method, you can easily shared data between unrelated components in the angular
I have a UserService that when my user signs on my application, I populate it with the data from the user to show in all my components.
But I'm facing a little problem When I reference the object like this:
ngOnInit() {
this.userModel = this.userService.getUserModel();
}
He works fine for the first load, but I have a page that changing the user data and I want to be reflected for all my application, so when I change this object this.userModel, the changes don't reflect all my components is like just a copy from the object from Service.
The solution that I make is Putting this object directly in my HTML files this.userService.getUserModel() but it is too large and I want to avoid that, just for a good syntax.
The question is how I can make a real reference to this variable without putting this service method directly on my components.
You could try and use observable, then subscribe to it.
For example:
public function getUserModel(): Observable<IUser> {
return this.apiService.getUser(); // This is an observable
}
Then on your component, instead of doing this:
this.userModel = this.userService.getUserModel();
You should instead do this:
this.userService.getUserModel().pipe(takeUntil(this._onDestroy$)).subscribe(user => {
this.userModel = user;
}
Pointers
this._onDestroy$ is a really easy way to unsubscribe when the component is destroyed, helps minimize memory leaks.
So you can add:
private _onDestroy$: Subject<any> = new Subject<any>();
Then in your ngOnDestroy
ngOnDestroy() {
this._onDestroy$.next();
this._onDestroy$.complete();
}
So, this should update the value in your HTML automatically.
I hope this helps.
You can create an EventEmitter that emits the userModel every time it changes. And then, eu get that observable on the classes that you want to be notified, and subscribe to the changes. Like this.
class UserService {
userModelEvent: EventEmitter<UserModel> = new EventEmitter();
}
class AnotherClass {
userModel: UserModel;
constructor(userService: UserService) {}
onInit() {
this.userService.userModelEvent.subscribe(userModel => this.userModel = userModel);
}
}
I'd like to refresh my card set from navigation bar which is part of app.component.html so I prepared refresh() function.
When it is called it does update variable Cards but doesn't render it in ngFor on html element in mainView.html.
It does render updated set if I call from html element in mainView.html (as (click)="loadCards()") but not if the same ((click)="refresh()") is done in app.component.html.
export class MainView implements OnInit {
constructor(private mMainController: MainController) {}
Cards: any = [];
ngOnInit() {
this.loadCards();
}
loadCards() {
this.mMainController.getAllCards().subscribe(
(data) => {this.Cards = data); },
(error) => {},
() => {console.log(this.Cards));
}
...
}
export class AppComponent {
...
constructor(private router: Router, private mMainView: MainView) {}
refresh(){
console.log('done');
this.mMainView.loadCards();
}
...
}
Update
Tried with #Input() but couldn't get it work. I implemented RefreshService as explained in accepted answer and now I'm able to refresh content from other components.
Thank you all for quick response.
FIST WAY: USING A SHARED SERVICE
You need to introduce a service that manage the state of your car.
In this case it may be usefull to introduce for this a BehaviorSubject like this:
Your Service:
private refresh: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public getRefresh(): Observable<boolean> {
return this.refresh.asObservable();
}
public setRefresh(value: boolean): void {
this.refresh.next(value);
}
Inside your MainView class
First: inject your service as dependency
Second: Subscribe to your observable inside OnInit hook e.g like this:
this.myService.getRefresh().subscribe((value: boolean) => {
if(value) {
this.loadCards()
}
})
Inside your AppComponent class
First: inject your service as dependency
Second: Set the value of your observable inside your refresh method.
e.g something like this:
public refresh(){
this.myService.setRefresh(true);
}
SECOND WAY: USING #Input Decorator to pass value down.
You're attempting to use MainView as a dependency but it's not an injectable dependency. Try to use inputs/outputs between app component and MainView, if possible. If MainView isn't a child of AppComponent then abstract the logic for loading cards into a service and inject it into both.
You can implement the component interaction in two ways
(i) If the components are related to each other use the common and straightforward method of sharing data. It works by using the #Input() decorator to allow data to be passed via the template.
(ii) If the components are not related to each other you can use a shared service using subject to communicate between the two components
In my angular component I have ngAfterViewInit method which contains some statements that I want to execute after the view is initialized cause it contains some DOM manupulations.
My question is when some parameter is changed I want to run the code inside ngAfterViewInit.
You could define an EventEmitter in the constructor, subscribe to it in the ngAfterViewInit() function to update those values, emit it in the ngAfterViewInit() function, and then emit it again every single time you want to call it in subsequent areas of the component. Here is an example:
import { EventEmitter } from '#angular/core';
export class MyComponent implements AfterViewInit {
public myEvent: EventEmitter<void>;
public constructor() {
this.myEvent = new EventEmitter<void>();
}
public ngAfterViewInit(): void {
// This is how you call the function to do what you want to do with your DOM manipulations below. You can also call this exact function even from the HTML, if you wish to do so (see HTML example).
this.myEvent.emit();
this.myEvent.subscribe(
() => {
// Do whatever actions that you need to do here to perform your DOM manipulations.
},
(err: Error) => console.error(err);
);
}
public emitMyEvent(): void {
this.myEvent.emit();
}
}
<my-component (click)="myEvent.emit()"></my-component>
<!-- or -->
<my-component (click)="emitMyEvent()"></my-component>
If you want to execute those statements on every changes than it is better to write those in ngAfterViewChecked().From the docs:
A lifecycle hook that is called after the default change detector has completed checking a component's view for changes
A callback method that is invoked immediately after the default change detector has completed one change-check cycle for a component's view.
So it will be called on every subsequent changes.
More information can also be found on the Lifecycle Hooks#AfterView docs
If your parameter is available as an Observable, you can just subscribe to it in the ngAfterViewInitmethod. If your parameter is not yet available as Observable, I suggest you take a look at the BehaviourSubject class. With this, you can control when the Observable will emit a new value + It will be triggered with the last value when you subscribe to it
You declare ordinary class methords in the class body, and later define them in the context of ngAfterviewInit.
here's a simple use case example:
export class ViewtestComponent implements OnInit, AfterViewInit{
#ViewChild('someElementMewantedToDoAction') elRef: ElementRef;
constructor() { }
ngOnInit(): void {
}
changeVal;
ngAfterViewInit() {
this.changeVal= (useME) => {
// some action
this.elRef.nativeElement.innerText++;
}
}
Later, use the method in template as
// Do Action= Button value will increment 11, 12 13 ... on each button click.
<button class="button success" (click)="changeVal($emit)>10</button>
I have solved my problem by implementing OnChanges and call ngAfterviewInit for changes other than the firstchange.This way I will make sure that the view is initiallized. By the way the variable subjected to change(changed_var) holds data used in DOM manipulation.
ngOnChanges(changes: SimpleChanges){
if(!changes.changed_var.isFirstChange()){
this.ngAfterViewInit();
}
}
In my Angular2 app, on UI input a component is loaded which pulls data from a web service.
I want to reload the aptSearchComponent when the user input changes. Although the new data is fetched from the service base on the input, the component is not reloaded.
The input is in the headerComponent, when the user inputs some search criteria and hits enter, data is passed to the sharedService and routed to aptSearchComponent, where data is pulled from the shared service and results are displayed.
The headerComponent template stays at the top and the aptSearchcomponent template is displayed below it.
#Component({
selector: 'app-header',
template: `
<div class="mdl-textfield__expandable-holder">
<input class="mdl-textfield__input" type="text" id="search" (keyup.enter)="Search($event)">
</div>
`,
})
export class HeaderComponent {
public apartments: Object[];
constructor(private apartmentService: ApartmentService,private router: Router,private sharedService: SharedService) {
this.apartmentService=apartmentService;
this.sharedService=sharedService;
}
Search(event){
this.apartmentService.searchApt2(event.target.value).subscribe(res => {this.sharedService.temp = res
this.router.navigate(['AptSearch'])});
}
}
How can I reload the component in Angular 2. Basically the data in this.aptDetails is changed, but template is still shows the old data.
export class AptSearchComponent implements OnInit {
aptDetails: any;
constructor(private apartmentService: ApartmentService, private sharedService: SharedService,private zone:NgZone) {
this.apartmentService = apartmentService;
}
ngOnInit(){
this.aptDetails = this.sharedService.temp;
JSON.stringify(console.log(this.aptDetails)); //the data here is changed based on input, but the template is not refreshed and I still see the previous result.
}
}
I tried the following in constructor() but no luck
this.zone.run(()=>this.aptDetails=this.sharedService.temp);
I am using RC4, and polyfills in not imported.
I resolved this by using #Input and ngOnChanges() hook in the child component. will share the detailed answer if anybody needs it.
To reload it you can remove it with a simple trick.
Put an *ngIf on the component and set it to true initially.
When you want to remove it set it to false, and then using setTimeout flick it back to true instantly. This will remove it and then recreate it.
When you recreate it pass the new parameters you want to pass in from the parent component.
(Angular2 used to use this trick to reset a form, I'm not sure if a better way is available now but during RC this was the correct approach).
Change detection only work if the property reference changed.
You must reset aptDetails before updating it.
this.aptDetails = {} // or whatever type it is
this.aptDetails = this.sharedService.temp;