Angular page auto refresh - javascript

I have a little problem in my angular application. On the first page I have a table with a list of all the employees and a "Create New Employee" button, which opens me a new form. After form submitting, a request is sent to the database and I return to the start page. New rows appears only when I click refresh button. Please, tell me, how can I reload data automatically?. Maybe, I do something wrong.
Here is a part of my table.component.html:
<tr *ngFor="let item of getAllEmployees(); let i = index">
<td>{{i+1}}</td>
<td>{{item.firstName}}</td>
<td>{{item.lastName}}</td>
<td [ngSwitch] = "item.isActive">
<span *ngSwitchCase ="1">Yes</span>
<span *ngSwitchDefault>No</span>
</td>
<td>{{item.depName}}</td>
--------------------------------
Here is a part of my table.component.ts:
export class TableComponent{
constructor(private model: Model){
}
getAllEmployees(): Employee[]{
return this.model.getAllEmployees();
}
Here is my Model:
#Injectable()
export class Model{
private employees: Employee[] = new Array<Employee>();
private departments: Department[] = new Array<Department>();
constructor(private dataSource: DataSource){
this.dataSource.getEmployeesData().subscribe(data => this.employees = data);
this.dataSource.getDepsData().subscribe(data => this.departments = data);
}
getAllEmployees(): Employee[]{
return this.employees;
}
And, finally, a request:
export const URL = new InjectionToken("url");
#Injectable()
export class DataSource{
constructor(private http: Http, #Inject(URL) private url: string){
}
getEmployeesData(): Observable<Employee[]>{
return this.sendRequest(RequestMethod.Get, `${this.url}employeeslist`);
}
And also my Form Component:
export class FormComponent{
employee: Employee = new Employee();
constructor(private model:Model, private activeRoute: ActivatedRoute,
private router: Router){
this.editing = activeRoute.snapshot.params["mode"] == "edit";
let id = activeRoute.snapshot.params["id"];
if(id != null){
Object.assign(this.employee, this.model.getEmployee(id) || new Employee());
}
}
editing: boolean = false;
submitForm(form: NgForm) {
if (form.valid) {
this.model.saveEmployee(this.employee);
this.router.navigateByUrl('/');
}
}
resetForm() {
this.employee = new Employee();
}
getAllDeps(): Department[]{
return this.model.getAllDeps();
}
}
Thank you!

Probably your error is here:
export class FormComponent{
employee: Employee = new Employee();
constructor(private model:Model, private activeRoute: ActivatedRoute,
private router: Router){
this.editing = activeRoute.snapshot.params["mode"] == "edit";
let id = activeRoute.snapshot.params["id"];
if(id != null){
Object.assign(this.employee, this.model.getEmployee(id) || new Employee());
}
}
editing: boolean = false;
submitForm(form: NgForm) {
if (form.valid) {
this.model.saveEmployee(this.employee);
this.router.navigateByUrl('/');
}
}
resetForm() {
this.employee = new Employee();
}
getAllDeps(): Department[]{
return this.model.getAllDeps();
}
When you receive a valid form, you save the employee and then redirect to your homepage or index route with this.router.navigateByUrl('/');
Probably you should don't do that and call your getAllEmployees method again to retrieve all the records again

Related

Having trouble emitting an Angular 13 message to a component but it is SENDING

I have a SERVICE that emits the ID, TITLE and MESSAGE for a modal, hence using one modal for many faces; polymorphism, actually.
I can SEND the emitted message but getting it to show up in the generic.modal.component.ts is not happening as expected.
Here's my code (abridged version for brevity)
this is a snippet from my component.ts file
// Top of the code
#Output() setAddress: EventEmitter<any> = new EventEmitter();
#Output() setModalID: EventEmitter<any> = new EventEmitter();
#Output() setModalTitle: EventEmitter<any> = new EventEmitter();
#Output() setModalContent: EventEmitter<any> = new EventEmitter();
#Output() messageEvent = new EventEmitter<object>();
message = {
id: '',
title: '',
msg: '',
msgObj: {}
};
// ... some more code...
openModal(id: string, title: string): void {
console.log('Modal will open...' + id);
this.modalService.setID(id);
this.modalService.setTitle(id, title);
this.modalService.setMsg(id, "This is the Example Modal Body");
this.setModalAction(id, title, this.modalService.getMsg());
this.commonService.showHideGenericModal('button');
}
setModalAction(id: string, title: any, msg: any): void {
this.message.id = id;
this.message.title = title;
this.message.msg = msg;
// Send Message to emit
this.sendMessage();
}
// Send Message Object to be listened for
sendMessage(): void {
console.log('Sending Message: ', this.message);
this.messageEvent.emit(this.message);
}
common-svcs.service.ts
import { Injectable, OnInit } from '#angular/core';
import { SessionStorageService } from 'angular-web-storage';
import { Subject } from 'rxjs';
import { ModalService } from './modal.service';
import { VaForm21elementService } from './vaForm21.element.service';
declare global {
interface Window {
initAutocomplete: () => void;
}
}
#Injectable({
providedIn: 'root'
})
export class CommonSvcsService implements OnInit {
modalMsg: string = '';
modalID: string = '';
modalTitle: string = '';
private _messageDetails: Subject<any> = new Subject<any>();
public messageDetailsObs = this._messageDetails.asObservable();
... some more code
constructor(
private sessionStorageService: SessionStorageService,
private modalService: ModalService,
private vaform21Service: VaForm21elementService
) { }
ngOnInit() {
window.initAutocomplete = this.initAutocomplete;
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
}
}
ngOnDestroy() {
this.sessionStorageService.remove('isGenericModalOpen');
}
ngAfterViewInit() {
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
}
}
showHideGenericModal(fromwhere: string): void {
switch (fromwhere) {
case "voice":
case "button":
this.openModalDialog();
break;
case "close":
this.closeModalDialog();
break;
}
}
/**
* Open Generic Modal
*/
public openModalDialog(): void {
this.genericModal = document.getElementsByClassName('drag-block')[0] as HTMLElement;
this.isGenericModalOpen = this.sessionStorageService.get('isGenericModalOpen');
if (!this.isGenericModalOpen) {
this.isGenericModalOpen = true;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
console.log('Inside openGenericModal: ', this.genericModal);
this.id = this.modalService.getID();
this.modalTitle = this.modalService.getTitle();
this.modalMsg = this.modalService.getMsg();
this.genericModal.classList.add('show')
this.isGenericModalOpen = false;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
} else {
// ('The modal is already opened');
}
}
}
modal.service.ts
export class ModalService {
modals: any[] = [];
modalTitle!: string;
modalMsg!: string;
id!: string;
showModal: boolean = false;
theModal!: HTMLElement;
add(modal: any) {
// add modal to array of active modals
this.modals.push(modal);
}
remove(id: string) {
// remove modal from array of active modals
this.modals = this.modals.filter(x => x.id !== id);
}
open(id: string): boolean {
// open modal specified by id
const modal: any = this.modals.filter(x => x.id === id)[0];
return this.showModal = false;
}
close(id: string): boolean {
// close modal specified by id
const modal: any = this.modals.filter(x => x.id === id)[0];
return this.showModal = true;
}
setTitle(id: string, title: string) {
// Sets the title
const modal: any = this.modals.filter(x => x.id === id)[0];
this.modalTitle = title;
// modal.title = title;
}
setMsg(id: string, msg: string) {
// Sets the title
const modal: any = this.modals.filter(x => x.id === id)[0];
this.modalMsg = msg;
// modal.msg = msg;
}
setID(id: string): void {
this.id = id;
}
getID(): string {
return this.id;
}
// Get Modal Title
getTitle(): string {
return this.modalTitle;
}
// Get Modal Msg/Body
getMsg(): string {
return this.modalMsg;
}
}
Finally, the generic.modal.component.ts and html files respectively
import { Component, OnInit, Input, ViewChild, ElementRef } from '#angular/core';
import { SessionStorageService } from 'angular-web-storage';
import { CommonSvcsService } from 'src/app/services/common-svcs.service';
import { ModalService } from 'src/app/services/modal.service';
#Component({
selector: 'app-mymodal',
templateUrl: './generic-modal.component.html',
styleUrls: ['./generic-modal.component.css']
})
export class GenericModalComponent implements OnInit {
#Input() my_modal_title!: string;
#Input() my_modal_content!: string;
#Input() id: string = '';
#ViewChild('messageEvent') messageAction: any;
showModal: boolean = false;
#ViewChild('el') el: ElementRef | undefined;
private element: any;
private isGenericModalOpen: boolean = false;
private message!: object;
constructor(
private modalService: ModalService,
private sessionStorageService: SessionStorageService,
private commonService: CommonSvcsService
) { }
ngOnInit() {
const modal = this;
this.element = this.el?.nativeElement;
this.modalService.add(this);
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.remove('isGenericModalOpen');
}
}
ngAfterViewInit() {
this.id = this.commonService.modalID;
this.my_modal_title = this.commonService.modalTitle;
this.my_modal_content = this.commonService.modalMsg;
}
ngAfterViewChecked() {
this.commonService.messageDetailsObs.subscribe((messageDetails) => {
console.log('Message Details: ', messageDetails)
});
}
ngOnDestroy() {
this.modalService.remove(this.id);
this.element.remove();
this.sessionStorageService.remove('isGenericModalOpen');
}
// open modal
public open(): void {
// this.element.style.display = 'block';
// document.body.classList.add('modal-open');
this.showModal = this.modalService.open(this.id);
}
// close modal
public close(): void {
// this.element.style.display = 'none';
// document.body.classList.remove('modal-open');
this.showModal = false;
this.commonService.showHideGenericModal('close');
}
// Set Modal Title
public setTitle(title: string): void {
this.my_modal_title = this.modalService.getTitle();
this.modalService.setTitle(this.id, title);
}
// Set Modal Message
public setMsg(msg: string): void {
this.my_modal_content = this.modalService.getMsg();
this.modalService.setMsg(this.id, msg);
}
// Set the ID of the modal dynamically
public setID(id: string): void {
this.id = id;
}
// Get the ID of the modal set
public getID(): string {
return this.id;
}
public receiveMessage($event: any): void {
this.message = $event;
console.log('Received Message: ', this.message);
}
}
The HTML
<div class="modal fade drag-block" (messageAction)="receiveMessage($event)" id="{{id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true" [ngClass]="{'show': showModal}">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">{{my_modal_title}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true" (click)="close()">×</span>
</button>
</div>
<div class="modal-body">
{{my_modal_content}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
What I'm doing is dynamically feeding the ID, TITLE and MSG into a modal so I can send into it anything and use only one modal component. I getting is: private message!: object; is UNDEFINED
BTW: the modal OPENS and the buttons and "x" are there. Nothing is in the TITLE, MSG or ID
Thank you
UPDATE:
So, here's my minor issue after #Krenom helped me.
I'm going to reduce this for simplicity:
main-form.component.ts
main-form.component.html <-- this will fire 1 to "n" number of modals
generic-modal.component.ts
generic-modal.component.html <-- this will RECEIVE the msgObj which is outline both in my question and #Krenom's solution.
common-svc.service.ts <-- talks to any component that imports this
All of these components and service live under src/app/
The components are under /components and the services live under /services under app/
Now that that's done.
The Problem
the msgObj gets populated nicely. BUT even with me sending the messageObject via an Observable or the updateModal function that #Krenom created the fact remains, that I need to be able to change the {{title}} and {{msg}} in the generic-modal.component.ts from the common-svc.service.ts when the msgObj changes.
That's all.
I am STILL keeping #Krenoms solution, I probably just am missing something
Here is the full generic-modal.component.ts
import { Component, OnInit, Input, ViewChild, ElementRef, ChangeDetectorRef, ViewRef } from '#angular/core';
import { SessionStorageService } from 'angular-web-storage';
import { CommonSvcsService } from 'src/app/services/common-svcs.service';
import { ModalService } from 'src/app/services/modal.service';
import { GenericModalObj } from '../va-form-21/vaForm21.interface';
#Component({
selector: 'app-mymodal',
templateUrl: './generic-modal.component.html',
styleUrls: ['./generic-modal.component.css']
})
export class GenericModalComponent implements OnInit {
#Input() my_modal_title!: string;
#Input() my_modal_content!: string;
#Input() id: string = '';
#ViewChild('messageEvent') messageAction: any;
showModal: boolean = false;
#Input() public set data(value: any) {
this.message = value;
this.data();
}
#ViewChild('el') el: ElementRef | undefined;
private element: any;
private isGenericModalOpen: boolean = false;
public message!: object;
constructor(
private modalService: ModalService,
private sessionStorageService: SessionStorageService,
private commonService: CommonSvcsService,
private cdref: ChangeDetectorRef
) { }
msgObj!: GenericModalObj;
ngOnInit() {
const modal = this;
this.element = this.el?.nativeElement;
this.modalService.add(this);
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.remove('isGenericModalOpen');
}
this.commonService.messageDetailsObs.subscribe(message => {
if (message !== this.message) {
this.message = message;
this.setMsgObj();
}
});
}
ngAfterViewInit() {
this.id = this.commonService.modalID;
this.my_modal_title = this.commonService.modalTitle;
this.my_modal_content = this.commonService.modalMsg;
}
ngAfterViewChecked() {
this.commonService.messageDetailsObs.subscribe((messageDetails) => {
console.log('Message Details: ', messageDetails)
});
}
ngAfterContentChecked() {
this.cdref.detectChanges();
}
ngOnDestroy() {
this.modalService.remove(this.id);
this.element.remove();
this.sessionStorageService.remove('isGenericModalOpen');
this.cdref.detach();
}
public checkRefDetectChanged(): void {
if(!(<ViewRef> this.cdref).destroyed) {
this.cdref.detectChanges();
}
}
// open modal
public open(): void {
// this.element.style.display = 'block';
// document.body.classList.add('modal-open');
this.showModal = this.modalService.open(this.id);
}
// close modal
public close(): void {
// this.element.style.display = 'none';
// document.body.classList.remove('modal-open');
this.showModal = false;
this.commonService.showHideGenericModal('close');
}
// Set Modal Title
public setTitle(title: string): void {
this.my_modal_title = this.modalService.getTitle();
this.modalService.setTitle(this.id, title);
this.msgObj.title = this.my_modal_title;
this.cdref.detectChanges();
}
// Set Modal Message
public setMsg(msg: string): void {
this.my_modal_content = this.modalService.getMsg();
this.modalService.setMsg(this.id, msg);
this.msgObj.msg = this.my_modal_content;
this.cdref.detectChanges();
}
// Set the ID of the modal dynamically
public setID(id: string): void {
this.id = id;
this.msgObj.id = this.id;
this.cdref.detectChanges();
}
// Get the ID of the modal set
public getID(): string {
return this.id;
}
public receiveMessage(event: any): void {
this.message = event;
console.log('Received Message: ', this.message);
this.my_modal_title = this.commonService.modalTitle;
this.my_modal_content = this.commonService.modalMsg;
this.msgObj.id = this.commonService.id;
this.msgObj.title = this.my_modal_title;
this.msgObj.msg = this.my_modal_content;
this.modalService.updateModal(this.msgObj);
this.cdref.detectChanges();
}
public get data(): any {
return this.msgObj;
}
public setMsgObj(): void {
this.message = this.msgObj;
}
}
Here's the GenericModalObj from the interface file that #Krenom explained
export interface GenericModalObj {
id: string;
title: string;
msg: string;
msgObj: {}
}
Finally, this is my modal.service.ts
export class ModalService {
modals: any[] = [];
modalTitle!: string;
modalMsg!: string;
id!: string;
showModal: boolean = false;
theModal!: HTMLElement;
add(modal: any) {
// add modal to array of active modals
this.modals.push(modal);
}
remove(id: string) {
// remove modal from array of active modals
this.modals = this.modals.filter(x => x.id !== id);
}
open(id: string): boolean {
// open modal specified by id
const modal: any = this.modals.filter(x => x.id === id)[0];
return this.showModal = false;
}
close(id: string): boolean {
// close modal specified by id
const modal: any = this.modals.filter(x => x.id === id)[0];
return this.showModal = true;
}
setTitle(id: string, title: string) {
// Sets the title
const modal: any = this.modals.filter(x => x.id === id)[0];
this.modalTitle = title;
// modal.title = title;
}
setMsg(id: string, msg: string) {
// Sets the title
const modal: any = this.modals.filter(x => x.id === id)[0];
this.modalMsg = msg;
// modal.msg = msg;
}
setID(id: string): void {
this.id = id;
}
getID(): string {
return this.id;
}
// Get Modal Title
getTitle(): string {
return this.modalTitle;
}
// Get Modal Msg/Body
getMsg(): string {
return this.modalMsg;
}
// In the Stackoverflow solution the options is options:
updateModal(options: GenericModalObj): void {
const modal = this.modals.find(x => x.id === options.id);
if (!modal) return;
if (options.title) modal.title = options.title;
if (options.message) modal.message = options.message;
}
}
Here's where I send the modal msgObj from the FORM COMPONENT
setModalAction(id: string, title: any, msg: any): void {
// Send Message to emit
this.messageOBJ.id = id;
this.messageOBJ.title = title;
this.messageOBJ.msg = msg;
this.sendMessage(this.messageOBJ);
}
// Send Message Object to be listened for
sendMessage(msgObj: Object): void {
console.log('Sending Message: ', msgObj);
this.messageEvent.emit(msgObj);
}
and my settings at the top of this component
#Output() setModalID: EventEmitter<any> = new EventEmitter();
#Output() setModalTitle: EventEmitter<any> = new EventEmitter();
#Output() setModalContent: EventEmitter<any> = new EventEmitter();
#Output() messageEvent = new EventEmitter<object>();
and the HTML for generic-modal.component.html
<div class="modal fade drag-block" (messageAction)="receiveMessage($event)" id="genericmodal" tabindex="-1"
role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" [ngClass]="{'show': showModal}">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fxModalTitle">{{my_modal_title}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body fxModalBody">{{my_modal_content}}</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="close()">Close</button>
</div>
</div>
</div>
</div>
</div>
Ok, not nearly enough time in a day to try and figure out how exactly that is all hooked up (you should consider refactoring and simplifying), but I suspect it's due to your inputs.
In a typical #Input() usage, you have some values set and ready, and then you show your component. The values are ready and you have them available at ngOnInit() time.
When dealing with opening dialogs, and using services and observables and all kinds of things that make things non-synchronous, it's quite possible that you don't have them at component init time.
The most basic fix is to wrap all your template parts with *ngIf="someData", that way, if they're undefined to start with, nothing will show (and nothing will attempt to use the un-set variable) and you'll get no errors.
Once the value comes in, it'll show and all shall be good.
A slightly more advanced method, for use with the above, because you still need to prevent the template from accessing properties that aren't yet set - is to use setters.
private _data: any;
#Input() public set data(value: any) {
this._data = value;
this.onDataReceived();
}
public get data(): any { return this._data; }
private onDataReceived(): void {
//... anything you might need to do once you get some input - sorting, processing, etc.
}
Typically though, only really need this if you plan on doing something with the value when it comes in, but always handy to know you can do this.
On the topic of refactoring, I think the simplest thing you can do is remove the split of id/title/message. It's tripling the amount of code that's there making it much harder to read and understand.
All three of those things are closely coupled and related to making one of your dialogs work. Keep them together, then. You already do, in your component.ts:
message = {
id: '',
title: '',
msg: '',
msgObj: {}
};
All methods can be reduced to one update method with this one object as a param instead of several, and you only need to handle the update in one fashion.
public updateModal(options: ModalDataDto): void {
const modal = this.modals.find(x => x.id === options.id);
if (!modal) return;
modal.title = options.title;
modal.message = options.message;
}
If you don't keep track of that options object and anything could be making arbitrary guesswork updates to individual properties, then you can do checks before assigning so you don't accidentally overwrite:
public updateModal(options: ModalDataDto): void {
const modal = this.modals.find(x => x.id === options.id);
if (!modal) return;
if (options.title) modal.title = options.title;
if (options.message) modal.message = options.message;
}
That way, anything could update only a part of it:
...
this.service.updateModal({id: 'asdf', title: 'New title'});
...
I notice that you can dynamically re-set the id as well. Not a fan of changing IDs because they're identifiers, and changing them is weird and not generally done, but if you are going to, I'd then suggest an additional property in your new options:
if (options.newId) modal.id = options.newId;
Update for your update:
One thing I can see is, wrong, ish, but mostly pointless:
#Input() public set data(value: any) {
this.message = value;
this.data();
}
It's setting the message, huzzah, but then calling this.data() - which I can't see as a method in there anywhere. I can see a getter, but then usage would be this.data rather than as a method call. And it's just a getter, so that line wouldn't be doing anything.
I'll say the same thing as before - it's still too complicated to follow, especially at this time of the morning, so I'll guess.
It looks like it works this way, from what I can see:
The modals proper in ModalService is a list of your modals, right? Hard to tell without types. I assume by 'modal', we also mean actual GenericModalComponent instances? It looks like it.
You have the method to affect updates against these modals:
updateModal(options: GenericModalObj): void {
const modal = this.modals.find(x => x.id === options.id);
if (!modal) return;
if (options.title) modal.title = options.title;
if (options.message) modal.message = options.message;
}
Where it does modal.title = .... However, I can't see any public properties for title or message in GenericModalComponent.
You have a my_modal_title as an input, and you have setTitle(...) as a method to do such, but you are using neither.
Presumably, if you used if (options.title) modal.setTitle(options.title); it would work.
Though maybe not... given the weird steps that method is doing.
If I'm reading it right, when you call setTitle it does the following:
Assume the service title is currently 'Service Title', and the modal title is currently 'Modal Title' - and we're going to call the modal's setTitle('New Title') method.
Sets its #Input title (my_modal_title ) to whatever value is currently stored in the ModalService - so that is now 'Service Title'.
It calls the ModalService's setTitle method, which finds the modal, ignores it, and sets its own stored title to be the one given it - the service title is now 'New Title'.
It then sets the msgObj title to be the new one - ok, fine, but that msgObj doesn't look like it's used anywhere?
Then does detect changes - ok, fine, but we'll get to that.
So you've got a few compounding structural issues here that need to be sorted out.
Lets roll with change detection to start with, because I don't think you should ever generally need to worry about change detection unless you're doing something specific, which this doesn't feel like it is.
Assume a basic setup:
thing.component.ts
export class ThingComponent {
public valueValue: string;
public refValue: SomeObject;
public arrayValue: SomeObject[];
}
and a basic template:
<div>Value type: {{valueValue}}</div>
<div>Reference type: {{refValue}} - {{refValue.someProperty}}</div>
<div>
Array type: {{arrayValue}}
<ul>
<li *ngFor="let item of arrayValue">{{item.someProperty}}</li>
</ul>
</div>
When filled, you should see something like:
Value type: Whatever value was entered
Reference type: [Object object] - Whatever ref value was entered
Array type: [Object object, maybe Object array or whatever, I don't remember]
- Whatever ref value 1 was entered
- Whatever ref value 2 was entered
Now, any updates valueValue should cause the view to update. Value types are immutable and Angular handles checking these nps.
Any updates to refValue is different, depending on how you use it. Angular won't, generally, be checking the internals of your object, so any change to refValue.someProperty may not actually be picked up. Guaranteed to be picked up is if refValue itself is changed.
If you had a getter, e.g. public getRefValue(): string { return this.refValue.someProperty; } then changes to someProperty should be picked up if that's used in the view instead, e.g. <div>Value type: {{getRefValue}}</div>.
This is because Angular generally hits the getters during its update cycles and at that point, it's just a string, not an object.
For anything referencing that object, however (e.g. any component #Input()s that you pass it in to), they will be looking at the object reference.
In that case, you can cheat and just create a new object (beware of shallow/deep clone issues) by using the shorthand: this.refValue = {...this.refValue}; - now refValue is a copy of itself, but in a new object, so Angular's change detection will re-evaluate it.
This will trigger setter hits to your #Inputs whereas doing this.refValue.someProperty = 'asdf'; wouldn't have.
Arrays are more fun, because they're more or less the same as objects. You can change the contents of an array, but that doesn't change the array object itself. Similar to objects, you can short-hand for copying, e.g. this.arrayValue = [...this.arrayValue]; or, I think you can do this.arrayValue = this.arrayValue.splice() which also returns a copy.
Finally, after that essay, it my intended usage of the GenericModalObj object. It should be used in place of the multiple lines.
You can replace:
public id: string;
public title: string;
public message: string;
with:
public data: GenericModalObj;
And you don't need all those setId, setTitle, setMessage, and reduce down to the one update method that you, sort of, have in place. Also, any method that deals with more than one of those doesn't need multiple params, just the one. It's about reducing the code.
Id, title, message - all of these are closely coupled; you can't have a modal without them all so it suggests that, structurally, they belong together. So put them together in a type, and make use of the typing available with TS.
private doSomething(): string {...}
Tells everyone so much more than
doSomething() {...}
It's private, so I know it's not available to the template (if this is a component), or generally not available/used outside whatever class it's in.
I can see it's going to return a string, so I know it's not just a random processor method that will (possibly) alter the state of something. I mean, it might, but hopefully the naming will suggest that, but ideally it won't (one method does one thing...).
Making proper use of context like this helps to understand and follow what is happening with your code. Never write code that you understand because you wrote it, always write code so that idiots like me can read it. Others in the future, and Future You, will thank you for it.
I was able to finally get the SUBSCRIBE and SEND MESSAGE to work! This solution is a combination of what Krenom did and what I was relentless in doing.
I'm keeping what Krenom has as a solution but I'm expanding on it.
Here's what I did.
I kept the HTML for generic modal
I've modified the Generic Modal Component (SEE BELOW)
I've modified the Common Service (SEE BELOW)
I've modified the call in the FORM component slightly (SEE BELOW)
Generic Modal Component
import { Component, OnInit, Input, ViewChild, ElementRef, ChangeDetectorRef, ViewRef } from '#angular/core';
import { SessionStorageService } from 'angular-web-storage';
import { CommonSvcsService } from 'src/app/services/common-svcs.service';
import { ModalService } from 'src/app/services/modal.service';
import { GenericModalObj } from '../va-form-21/vaForm21.interface';
#Component({
selector: 'app-mymodal',
templateUrl: './generic-modal.component.html',
styleUrls: ['./generic-modal.component.css']
})
export class GenericModalComponent implements OnInit {
#Input() my_modal_title!: string;
#Input() my_modal_content!: string;
#Input() id: string = '';
#ViewChild('messageEvent') messageAction: any;
showModal: boolean = false;
#Input() public set data(value: any) {
this.message = value;
this.data();
}
#ViewChild('el') el: ElementRef | undefined;
private element: any;
private isGenericModalOpen: boolean = false;
public message!: object;
constructor(
private modalService: ModalService,
private sessionStorageService: SessionStorageService,
private commonService: CommonSvcsService,
private cdref: ChangeDetectorRef
) { }
msgObj!: GenericModalObj;
ngOnInit() {
const modal = this;
this.element = this.el?.nativeElement;
this.modalService.add(this);
if (this.my_modal_content === undefined && this.my_modal_title === undefined) {
this.my_modal_content = '';
this.my_modal_title = '';
} else {
this.my_modal_title = this.modalService.getTitle();
this.my_modal_content = this.modalService.getMsg();
}
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.remove('isGenericModalOpen');
}
this.commonService.messageDetailsObs.subscribe(message => {
if (message !== this.message) {
this.message = message;
this.setMsgObj();
}
});
this.checkRefDetectChanged();
}
ngAfterViewInit() {
this.id = this.commonService.modalID;
if (this.my_modal_title && this.my_modal_title.length > 0) {
this.my_modal_title = this.modalService.getTitle();
}
if (this.my_modal_content && this.my_modal_content.length > 0) {
this.my_modal_content = this.modalService.getMsg();
}
this.checkRefDetectChanged();
}
ngAfterViewChecked() {
this.commonService.messageDetailsObs.subscribe((messageDetails) => {
console.log('Message Details: ', messageDetails);
if (this.modalService.id.length > 0 &&
this.modalService.modalTitle.length > 0 &&
this.modalService.modalMsg.length > 0) {
this.msgObj.id = this.modalService.id;
this.my_modal_title = this.modalService.modalTitle;
this.my_modal_content = this.modalService.modalMsg;
}
this.checkRefDetectChanged();
});
}
ngAfterContentChecked() {
if (this.modalService.id.length > 0 &&
this.modalService.modalTitle.length > 0 &&
this.modalService.modalMsg.length > 0) {
this.id = this.modalService.id;
this.my_modal_title = this.modalService.modalTitle;
this.my_modal_content = this.modalService.modalMsg;
}
this.cdref.detectChanges();
}
ngOnDestroy() {
this.modalService.remove(this.id);
this.element.remove();
this.sessionStorageService.remove('isGenericModalOpen');
this.cdref.detach();
this.my_modal_content = '';
this.my_modal_title = '';
this.id = '';
}
public checkRefDetectChanged(): void {
if (!(<ViewRef>this.cdref).destroyed) {
this.cdref.detectChanges();
}
}
// open modal
public open(): void {
// this.element.style.display = 'block';
// document.body.classList.add('modal-open');
this.showModal = this.modalService.open(this.id);
}
// close modal
public close(): void {
// this.element.style.display = 'none';
// document.body.classList.remove('modal-open');
this.showModal = false;
this.commonService.showHideGenericModal('close');
}
// Set Modal Title
public setTitle(title: string): void {
this.my_modal_title = this.modalService.getTitle();
this.modalService.setTitle(this.id, title);
this.msgObj.title = this.my_modal_title;
this.checkRefDetectChanged();
}
// Set Modal Message
public setMsg(msg: string): void {
this.my_modal_content = this.modalService.getMsg();
this.modalService.setMsg(this.id, msg);
this.msgObj.msg = this.my_modal_content;
this.checkRefDetectChanged();
}
// Set the ID of the modal dynamically
public setID(id: string): void {
this.id = id;
this.msgObj.id = this.id;
this.checkRefDetectChanged();
}
// Get the ID of the modal set
public getID(): string {
return this.id;
}
public receiveMessage(event: any): void {
this.message = event;
console.log('Received Message: ', this.message);
this.my_modal_title = this.commonService.modalTitle;
this.my_modal_content = this.commonService.modalMsg;
this.msgObj.id = this.commonService.id;
this.msgObj.title = this.my_modal_title;
this.msgObj.msg = this.my_modal_content;
this.checkRefDetectChanged();
}
public get data(): any {
return this.msgObj;
}
public setMsgObj(): void {
this.message = this.msgObj;
}
}
Common Service
import { Injectable, OnInit } from '#angular/core';
import { SessionStorageService } from 'angular-web-storage';
import { Subject } from 'rxjs';
import { GenericModalObj } from '../components/form/Form.interface';
import { ModalService } from './modal.service';
import { FormelementService } from './Form.element.service';
declare global {
interface Window {
initAutocomplete: () => void;
}
}
#Injectable({
providedIn: 'root'
})
export class CommonSvcsService implements OnInit {
modals: any[] = [];
modalMsg: string = '';
modalID: string = '';
modalTitle: string = '';
private _messageDetails: Subject<any> = new Subject<any>();
public messageDetailsObs = this._messageDetails.asObservable();
startDate!: Date;
endDate!: Date;
currDate!: Date;
vaFormData: any;
showModal: boolean = true;
autocomplete!: google.maps.places.Autocomplete;
address1Field!: HTMLInputElement;
address2Field!: HTMLInputElement;
postalField!: HTMLInputElement;
genericModal!: HTMLElement;
id!: string;
isGenericModalOpen: boolean = false;
constructor(
private sessionStorageService: SessionStorageService,
private modalService: ModalService,
private formService: FormelementService
) { }
ngOnInit() {
window.initAutocomplete = this.initAutocomplete;
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
}
this.formService.regForSettingModalID(this.regForSettingModalID.bind(this));
this.formService.regForSettingModalTitle(this.regForSettingModalTitle.bind(this));
this.formService.regForSettingModalMsg(this.regForSettingModalMsg.bind(this));
}
ngOnDestroy() {
this.sessionStorageService.remove('isGenericModalOpen');
}
ngAfterViewInit() {
if (this.isGenericModalOpen) {
this.isGenericModalOpen = false;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
}
}
checkDate(date1: Date, date2: Date): string {
let retVal = "";
this.currDate = new Date();
this.startDate = new Date(date1);
if (this.startDate.getTime() > this.currDate.getTime()) {
retVal = "Your Entry Date cannot be greater than today's Date.";
}
return retVal;
}
initAutocomplete() {
this.address1Field = document.querySelector("#homeaddress") as HTMLInputElement;
this.address2Field = document.querySelector("#hmapt") as HTMLInputElement;
this.postalField = document.querySelector("#hmpostal") as HTMLInputElement;
// Create the autocomplete object, restricting the search predictions to
// addresses in the US and Canada.
this.autocomplete = new google.maps.places.Autocomplete(this.address1Field, {
componentRestrictions: { country: ["us", "ca"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
this.address1Field.focus();
// When the user selects an address from the drop-down, populate the
// address fields in the form.
this.autocomplete.addListener("place_changed", this.fillInAddress);
}
fillInAddress() {
// Get the place details from the autocomplete object.
const place = this.autocomplete.getPlace();
let address1 = "";
let postcode = "";
// Get each component of the address from the place details,
// and then fill-in the corresponding field on the form.
// place.address_components are google.maps.GeocoderAddressComponent objects
// which are documented at http://goo.gle/3l5i5Mr
for (const component of place.address_components as google.maps.GeocoderAddressComponent[]) {
// #ts-ignore remove once typings fixed
const componentType = component.types[0];
switch (componentType) {
case "hmaddy":
case "bzaddy": {
address1 = `${component.long_name} ${address1}`;
break;
}
case "route": {
address1 += component.short_name;
break;
}
case "postal_code": {
postcode = `${component.long_name}${postcode}`;
break;
}
case "postal_code_suffix": {
postcode = `${postcode}-${component.long_name}`;
break;
}
case "hmcity":
case "bzcity":
(document.querySelector("#hmcity") as HTMLInputElement).value =
component.long_name;
break;
case "administrative_area_level_1": {
(document.querySelector("#hmstate") as HTMLInputElement).value =
component.short_name;
break;
}
case "hmcountry":
case "bzcountry":
(document.querySelector("#hmcountry") as HTMLInputElement).value =
component.long_name;
break;
}
}
}
regForCheckDate(checkDateCallback: Function): void {
console.log('Check Date Callback: ', checkDateCallback);
}
regForfillInAddress(checkfillInAddressCallback: Function): void {
console.log('Check fillInAddress Callback: ', checkfillInAddressCallback);
}
regForSettingModalID(checkSettingModalIDCallback: Function): void {
console.log('Check for Modal ID', checkSettingModalIDCallback);
this.formService.regForSettingModalID(checkSettingModalIDCallback);
}
regForSettingModalTitle(checkSettingModalTitleCallback: Function): void {
console.log('Check for Modal Title', checkSettingModalTitleCallback);
this.formService.regForSettingModalTitle(checkSettingModalTitleCallback);
}
regForSettingModalMsg(checkSettingModalMsgCallback: Function): void {
console.log('Check for Modal ID', checkSettingModalMsgCallback);
this.formService.regForSettingModalMsg(checkSettingModalMsgCallback);
}
showHideGenericModal(fromwhere: string): void {
switch (fromwhere) {
case "voice":
case "button":
this.openModalDialog();
break;
case "close":
this.closeModalDialog();
break;
}
}
/**
* Open Generic Modal
*/
public openModalDialog(): void {
this.genericModal = document.getElementsByClassName('drag-block')[0] as HTMLElement;
this.isGenericModalOpen = this.sessionStorageService.get('isGenericModalOpen');
let id = '', title = '', msg = '', msgObj = {};
if (!this.isGenericModalOpen) {
this.isGenericModalOpen = true;
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
console.log('Inside openGenericModal: ', this.genericModal);
this.id = this.modalService.getID();
this.modalTitle = this.modalService.getTitle();
this.modalMsg = this.modalService.getMsg();
this.genericModal.classList.add('show');
this.isGenericModalOpen = false;
id = 'genericmodal';
title = this.modalTitle;
msg = this.modalMsg;
msgObj = this.modalService.getMsgObj();
// Send Message
this.messageDetails({id, title, msg, msgObj});
this.updateModal({
id, title, msg,
msgObj: {}
});
this.sessionStorageService.set('isGenericModalOpen', this.isGenericModalOpen);
} else {
// ('The modal is already opened');
}
}
public closeModalDialog() {
let isGenericModalOpen;
isGenericModalOpen = false;
this.genericModal = document.getElementsByClassName('drag-block')[0] as HTMLElement;
this.sessionStorageService.set('isDebugModalOpen', isGenericModalOpen);
// console.log('Inside closeDebugModal: ', isDebugModalOpen);
this.genericModal.classList.remove('show')
}
public updateModal(options: GenericModalObj): void {
const modal = this.modals.find(x => x.id === options.id);
if (!modal) return;
modal.title = options.title;
modal.message = options.msg;
modal.msgObj = options.msgObj;
}
messageDetails(message: Object): void {
this._messageDetails.next(message);
}
}
Form Component
Snippets from the full code
...
showHideGenericModal(event: any, key: any, showmodal: boolean): void {
this.showModal = showmodal;
if (event.target.id !== 'btnDemoModal1' && event.target.id !== 'btnDemoModal2') {
return;
} else {
this.genericModalDialog = true;
setTimeout(() => {
this.genericModalDialog = false;
}, 5000)
this.openModal(key, "Opening " + key);
}
}
openModal(id: string, title: string): void {
console.log('Opening the ' + id);
let msgObj: Object = {};
this.modalService.setID(id);
this.modalService.setTitle(id, title);
this.modalService.setMsgObj(id, msgObj)
this.modalService.setMsg(id, this.getMsgBody(id));
this.setModalAction(id, title, this.modalService.getMsg(), this.modalService.getMsgObj());
// Make sure we set isGenericModalOpen to FALSE just in case it's still TRUE
this.sessionStorageService.set('isGenericModalOpen', false);
this.commonService.showHideGenericModal('button');
}
closeModal(id: string, modalname: string): void {
console.log('Modal will close...' + id);
this.modalService.id = modalname;
this.modalService.setTitle(id, "");
this.modalService.setMsg(id, "");
this.commonService.showHideGenericModal('close');
}
setModalAction(id: string, title: string, msg: string, msgobj: object): void {
// Send Message to emit
this.messageOBJ.id = id;
this.messageOBJ.title = title;
this.messageOBJ.msg = msg;
this.messageOBJ.msgObj = msgobj;
this.sendMessage(this.messageOBJ);
}
// Send Message Object to be listened for
sendMessage(msgObj: Object): void {
console.log('Sending Message: ', msgObj);
this.messageEvent.emit(msgObj);
}
getMsgBody(id: string): string {
let msgBody = '';
switch (id) {
case "genericmodal1":
msgBody = "This is the body text for modal 1";
break;
case "genericmodal2":
msgBody = "This is the body text for modal 2";
break;
default:
msgBody = "This is the default modal body text!"
break;
}
return msgBody;
}
...
Finally, you can pull the data for the modal body from a JSON object or database and modify your code accordingly.
The ngOnDestroy clears out the MODAL BODY and MODAL TITLE
ngOnDestroy() {
this.modalService.remove(this.id);
this.element.remove();
this.sessionStorageService.remove('isGenericModalOpen');
this.cdref.detach();
this.my_modal_content = '';
this.my_modal_title = '';
this.id = '';
}
NOTE: the this.id is ONLY the ID of the modal body and title NOT the id of the generic modal component. Krenom said not to dynamically change the modal ID and I heeded his warning.
Happy Coding!

Using ReplaySubject in Angular at PUT Request it is losing the data

I am having an issue with the ReplaySubject, I don't know what I have done wrong but the problem it is that when I change something and save it in backend, the ReplaySubject it calls new data but it is hiding them to show me see the picture.
I tried to use detectChanges but without any luck, but as i know the detectChanges will work only when we have parent -> child iterration.
This happens on update
This happens when I reload the page
If I remove the the this.data.next(res) at this line of the Put Request then it works but I need again to reload page to get new data.
return this.http.put(api, dataModel).subscribe(res => this.data.next(res));
This is the code of the service.
#Injectable({
providedIn: "root"
})
export class ModelDataService {
public baseUrl = environment.backend;
private data = new ReplaySubject<any>();
public userID = this.authService.userID;
constructor(private http: HttpClient, private authService: AuthService) {
}
public getJSON() {
return this.http.get(`${this.baseUrl}/data/${this.userID}`).subscribe(res => this.data.next(res));
}
public dataModel(): Observable<any> {
return this.data.asObservable();
}
public setData(data: Model) {
const api = `${this.baseUrl}/data`;
const user_id = this.authService.userID;
this.http.post(api, data, {
headers: {user_id}
}).subscribe(res => this.data.next(res));
}
public updateDate(id: string, dataModel: Model) {
const api = `${this.baseUrl}/data/${id}`;
return this.http.put(api, dataModel).subscribe(res => this.data.next(res));
}
}
This is the component.
public model$: Observable<Model>;
public model: Model;
constructor(
public authService: AuthService,
private modelDataService: ModelDataService,
public dialog: MatDialog,
public cd: ChangeDetectorRef) {
}
ngOnInit() {
this.authService.getUserProfile(this.userID).subscribe((res) => {
this.currentUser = res.msg;
});
this.modelDataService.getJSON();
this.model$ = this.modelDataService.dataModel();
this.model$.subscribe((data) => {
this.model = data; // here if i try to console log then I have the array but the page it will be blank
console.log(data);
});
}
And then for example I try to show data like this.
<ng-container class="Unit-unit-unitGroup" *ngFor="let personalData of model.personalData; let id = index">
</ng-container>
In the put Request I have added an console log and this is what I am getting.
But on reload I am getting I think another way of strucuter of data.
See picture
Use ChangeDetectorRef to detect changes again in the view when the data comes back.
this.model$.subscribe((data) => {
this.model = data;
this.cd.detectChanges();
});
<ng-container class="Unit-unit-unitGroup" *ngFor="let personalData of model.personalData; let id = index">
</ng-container>
this.model$.subscribe((data) => {
this.model = data; // here if i try to console log then I have the array but the page it will be blank
console.log(data);
});
public updateDate(id: string, dataModel: Model) {
const api = `${this.baseUrl}/data/${id}`;
return this.http.put(api, dataModel).subscribe(res => this.data.next(res));
}
You expect personalData to be a property of your model. However, as seen in the console log your res is an object including data as an object between. All you need to do is mapping res to it's data.
public updateDate(id: string, dataModel: Model) {
const api = `${this.baseUrl}/data/${id}`;
return this.http.put(api, dataModel).subscribe(res => this.data.next(res.data));
}

Losing values outside function

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("--------------");
});
}

How to solve time raise problems in Angular?

I am writing an Angular Service to prove a Users permissions. In the constructor I want to get the current logged in user from an API. The current User which is created is used in other methods of this Service. This Methods are called from components to check which things can be shown and so on.
The problem is that the methods in the service are called faster than the current user is available.
Are there any possibilities solving this issue?
permission.service.ts
#Injectable()
export class PermissionService {
currUser = {
'id': "",
'permission': ""
};
apiService: AlfrescoApiService;
authService: AuthenticationService;
constructor(apiService: AlfrescoApiService, authService: AuthenticationService) {
this.apiService = apiService;
this.authService = authService;
this.init();
}
init() {
let userId: string = this.authService.getEcmUsername();
this.currUser.id = userId;
//API call
this.apiService.sitesApi.getSiteMember(SITENAME, userId).then(resp => {
this.currUser.permission = resp.entry.role;
})
}
isSiteManager(): boolean {
console.log(this.currUser.permission, this.currUser);
if(this.currUser.permission === "SiteManager"){
return true;
}else{
return false;
}
}
}
method call
export class AppLayoutComponent {
constructor(permissionService:PermissionService) {
permissionService.isSiteManager();
}
}
output in Google Chrome
{id: "admin", permission: ""}
id: "admin"permission: "SiteManager"
You should use promise in your getEcmUsername() handle this; after that you can code like this
`
this.authService.getEcmUsername().then((userID) => {
this.apiService.sitesApi.getSiteMember(SITENAME, userId).then(resp => {
this.currUser.permission = resp.entry.role;
})
});
`
In my opinion better solution is to use Observable here and rxjs. In service you can create Subject and subscribe it inside your compnent, to be sure data is already there. E.g.:
#Injectable()
export class PermissionService {
public Subject<bool> userFetched= new Subject<bool>();
currUser: IUser = {
'id': "",
'permission': ""
};
apiService: AlfrescoApiService;
authService: AuthenticationService;
constructor(apiService: AlfrescoApiService, authService: AuthenticationService) {
this.apiService = apiService;
this.authService = authService;
this.init();
}
init() {
let userId: string = this.authService.getEcmUsername();
this.currUser.id = userId;
//API call
this.apiService.sitesApi.getSiteMember(SITENAME, userId).subscribe((data:IUser)=>
{
this.user=data;
this.userFetched.next(true);
})
}
isSiteManager(): boolean {
console.log(this.currUser.permission, this.currUser);
if(this.currUser.permission === "SiteManager"){
return true;
}else{
return false;
}
}
}
After that in your component:
export class AppLayoutComponent {
constructor(permissionService:PermissionService) {
permissionService.userFetched.subscribe((data)=>{
permissionService.isSiteManager();
});
}
}
It's better approach. You need to consider if better is Subject or BehaviourSubject.
Thanks to everybody who answered. I have found a solution. I have changed the isSiteManager() method to a method which checks all four permissiontypes. This method is executed in the then() block and affects four variables for each permissiontype. These variables i can reach from other components.
Looks like this:
#Injectable()
export class PermissionService {
isSiteManager: boolean;
isSiteConsumer: boolean;
isSiteContributor: boolean;
isSiteCollaborator: boolean;
userId : string;
constructor(private apiService: AlfrescoApiService, private authService: AuthenticationService) {
this.init();
}
init() {
this.isSiteCollaborator = false;
this.isSiteConsumer = false;
this.isSiteContributor = false;
this.isSiteManager = false;
this.userId = localStorage.USER_PROFILE;
//proof permission of user
this.apiService.sitesApi.getSiteMember(SITENAME, this.userId).then(resp=>{
if(resp.entry.role === "SiteManager"){
this.isSiteManager = true;
}else if(resp.entry.role === "SiteConsumer"){
this.isSiteConsumer = true;
}else if(resp.entry.role === "SiteContributor"){
this.isSiteContributor = true;
}else{
this.isSiteCollaborator = true;
}
});
}
}
Now i can ask for the variables in other components like this:
export class AppLayoutComponent {
constructor(private permissionService : PermissionService) {
if(permissionService.isSiteManager){
console.log("You are Boss!");
}
}
}
You should call your service method synchrony. To do so, you have to map your response from the service:
your component code:
constructor() {
...
permissionService.isSiteManager().map(
response => {
isManager = response;
}
);
}
Something like this.
To call map operator import it before:
import 'rxjs/add/operator/map';

Why is an attribute of the page undefined while building a form

I get an error that my this.worldCities is undefined and that I cannot apply find on it with the following code
export class SelectCityModalPage {
worldCities : Array<City>;
chosenCity:City;
myForm;
constructor(public navCtrl: NavController, public navParams: NavParams,
public appCitiesProvider:AppCitiesProvider, public viewCtrl:ViewController,
public formBuilder:FormBuilder) {
this.worldCities = this.appCitiesProvider.worldCities;
this.chosenCity = new City();
this.chosenCity.name = "";
console.log("In modal the world cities are " + this.worldCities);
this.myForm = formBuilder.group({
cityControl: ['Start typing...', this.checkIfValidCity]
//cityControl:['']
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad SelectCityModalPage');
}
closeModal(){
this.chosenCity = this.worldCities.find((element) =>{
return element.name == this.chosenCity.name;
});
this.viewCtrl.dismiss(this.chosenCity);
}
checkIfValidCity(control : FormControl){
let validation = this.worldCities.find((element) => {
return element.name == control.value;
});
return validation != undefined;
}
}
And my provider is like that :
export class AppCitiesProvider {
errorMessage;
worldCities:Array<City>;
constructor(public http: Http, errorHandler:ErrorHandler) {
console.log('Hello Cities Provider');
this.getWorldCities2()
.subscribe(
(cities) => {
this.worldCities = cities;
console.log("in provider the worldCities are " + this.worldCities);
},
(error: any) => this.errorMessage = <any>error);
;
}
getWorldCities2() {
return this.http.get('../assets/city.list.json')
.map(res => res.json());
}
}
What I don't understand is that the appCitiesProvider.worldCities is initialized when called on a previous page, so this should not be the issue.
Also when I don't have the formbuilder in the code, I don't have any issue. The issue is really appearing because of the checkIfValidCity function.
Do you know where that comes from and how to solve it ?
Thanks a lot!
Methods outside your constructor loses the context of the class, therefore using 'this' will mean the method and not necessarily the class. If you want your methods to use the context of the class you will need to bind it in your constructor.
Add
this.checkIfValidCity = this.checkIfValidCity.bind(this)
to your constructor.

Categories