lundi 20 juillet 2020

Instantiate component again after an Observable completes

I am working on a Angular Application where the user has to select a server on which he want to operate, depending on his selection data will be received over an http get request. The user should be able to change the server on runtime to receive other servers information and he should also be able to edit the server list on runtime.

At begining I made an JSON to simulate the server informations. The JSON has following structure:

{
    "server": [
        {
            "name": "serverNameOne",
            "host": "serverHostOne",
            "enabled": true
        },
        {
            "name": "serverNameTwo",
            "host": "serverHostTwo",
            "enabled": true
        }
}

In a service I am assigning the information in this json to an property:

...
import { server as serverJSON } from 'src/assets/configs/servers.json';

@Injectable({
    providedIn: 'root'
})
export class ServerService {
    public serverSource: IServer[];
    public server: IServer;
    public serverIsSet: boolean;

    constructor(@Inject('BASE_URL') private baseUrl: string, provate http: HttpClient) {
        this.serverSource = serverJSON.filter(serverEntry => serverEntry.enabled === true)
    }
...
    public setServerStateAccordingLocalStorage(): void {
        this.server = this.serverSource.find(serverEntry => 
        serverEntry.name === localStorage.getItem('server'));
        this.serverIsSet = true;
    }
...
}

In the AppComponent, I check has the user selected one server in the past. In this case the information from the previous selection, which was stored in the localStorage, will be used to display the corresponding server information otherwise he can choose an server and this selection will be stored in the localStorage for future processment.

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    constructor(public serverService: ServerService) { }

    ngOnInit(): void {
        if (localStorage.getItem('server') === null) {
            this.serverService.serverIsSet= false;
        } else {
            this.serverService.setServerStateAccordingLocalStorage();
        }
    }
}

The Template will display either the initializer component or the serverSelector component, depending on the value of the serverService.serverIsSet property:

<!-- app.component.html -->
<ng-container *ngIf="serverService.serverIsSet; else setServer">
    <app-initializer></app-initializer>
</ng-container>

<ng-template #setServer>
    <app-server-selector [connectionWarning]="false" [selectorInDialog]="false"></app-server-selector>
</ng-template>

This works good when the server information is stored in an JSON, as soon as I want to replace the serverSource to information from the database the logic brakes. I want to give the user the opportunity to change the server information like the host value for an server, that's why I want to put all information regarding the server in a database and use CRUD operations to manage the server source handling. I have tried to change the server source as follows:

// in ServerService
constructor(@Inject('BASE_URL') private baseUrl: string, provate http: HttpClient) {
    this.getServers().subscribe(
        result => this.serverSource = result
    );
}

private getServers(): Observable<IServer[]> {
    return this.http.get<IServer[]>(this.baseUrl + 'api/getServers');
}

Unfortunately after this change the if statement in the AppComponent doesn't work as it should. I think the reason for this could be that the serverSource GET request in ServerService is asynchronous and the value response from the request is not yet avaiable when the if statement will be checked. If localStorage.getItem('server') is not null, that means the user has choosen an server in the past, so we jump to the else statement and call setServerStateAccordingLocalStorage(), this call will not succeess, because the serverSource can not be processsed since it is not available yet. How could I instantiate the AppComponent again as soon the getServers() call completes? Or should I consider another way to acomplish my goal? My goal is to use the get request to receive the server information and look is one of the server names already stored in the localStorage, if that is true go on with the initializer component, if the localStorage value is not set, load the serverSelector component. Currently I get the serverSelector all time although the localStorage value is available. Thank you in advance.

EDIT: I have moved the call for getServers() to the AppComponent and now it works:

export class AppComponent implements OnInit {
    constructor(public serverService: ServerService) { }

    ngOnInit(): void {
        this.serverService.getServers().subscribe(
            result => {
                this.serverService.serverSource = result;
                if (localStorage.getItem('server') === null) {
                    this.serverService.serverIsSet = false;
                } else {
                    this.serverService.setServertStateAccordingLocalStorage();
                }
            }
        );
    }
}

Can this considered to be an good solution or not?

Aucun commentaire:

Enregistrer un commentaire