0

I have a list of labels in NativeScript app representing a status. I have method that changes the status of each label after tap. After I tap the label it should change the status value (number) and show new value on the screen right after the tap.

What I've done is partially working. I have to tap it twice to see the label changed, or tap once and pull to refresh to see the label change. Also update in the service is working as expected after tap. Any ideas how to implement this behavior properly?

Views: parent component:

<StackLayout *ngFor="let phase of (training$ | async)?.phases; let i = index">
    <StackLayout *ngIf="(training$ | async)?.phases">
        <StackLayout>
            <ns-phase-item [phase]="phase" [training$]="training$" [i]="i"></ns-phase-item>
        </StackLayout>
    </StackLayout>
</StackLayout> 

First child (ns-phase-item):

<StackLayout>
    <Label [text]="phase.phaseTitle" [textWrap]="true" class="title-wrap"></Label>
    <StackLayout *ngFor="let unit of (training$ | async).phases[i].units">
         <ns-unit-item [unit]="unit" class="unit"></ns-unit-item>
    </StackLayout>
</StackLayout>

Child of first child view (ns-unit-item) (where I call the update method):

<StackLayout [ngClass]="{ 'unit-completed': unit.status === 1, 'unit-not-completed': unit.status !== 1 }">
 <GridLayout columns="4*, *" rows="auto">
    <FlexboxLayout col="0" flexDirection="row" (tap)="onView(unit._id)">
        <Label [text]="(unit.date | date: 'd. MMM') + ' - ' + unit.unitTitle"></Label>
        <Label *ngIf="unit.duration || unit.distance" text=" ("></Label>
        <Label *ngIf="unit.duration" [text]="' ' + unit.duration + ' min'"></Label>
        <Label *ngIf="unit.duration && unit.distance" text=" / "></Label>
        <Label *ngIf="unit.distance" [text]="unit.distance + ' km'"></Label>
        <Label *ngIf="unit.duration || unit.distance" text=")"></Label>
        <Label *ngIf="unit$" [text]="(unit$ | async).status"></Label>
    </FlexboxLayout>
    <StackLayout col="1">
        <StackLayout *ngIf="(unit$ | async).status == 1">
  // HERE I CALL UPDATE - it should change the value and icon after tap
            <Image src="res://check" height="20" stretch="aspectFit" (tap)="onStatusUpdate(0)"></Image>
        </StackLayout>
        <StackLayout *ngIf="(unit$ | async).status == 0">
            <Image src="res://checkgray" height="20" stretch="aspectFit" (tap)="onStatusUpdate(1)"></Image>
        </StackLayout>
    </StackLayout>
  </GridLayout>
</StackLayout>

Child of first child controller (ns-unit-item component)

private _unit: BehaviorSubject<Unit> = new BehaviorSubject<Unit>(null)


getUnitObservable(): Observable<Unit> {
    return this._unit.asObservable()
}

getUnitValue(): Unit {
    return this._unit.getValue()
}

unitChanged(unit: Unit) {
    this._unit.next(unit)
}

ngOnInit(): void {
    this.unitChanged(this.unit)
    this.unit = this.getUnitValue()
    this.unit$ = this.getUnitObservable()
}

onStatusUpdate(status: number) {
    this.trainingService.updateTrainingUnitStatus(this.unit._id, status).subscribe((unit) => {
        this.unitChanged(unit)
        this.unit$ = this.getUnitObservable()
    })
}

Service where I call API

updateTrainingUnitStatus(id: string, status: number) {
    return this.http.post<Unit>(`${this.API_URL_UNITS}/${id}/status`, { status: status })
}
luk
  • 205
  • 3
  • 14
  • It looks like you're not working with Observables the right way. Can you provide a link to the code (repo) so that we can help? – Lorraine R. Jan 22 '22 at 10:00
  • thanks I will prepare public repo with part of the code I listed above – luk Jan 26 '22 at 17:30

1 Answers1

0

I used RxJS BehaviorSubject to accomplish that. I have simplified the solution, and used BehaviorSubject with number only and not full object.

in the service:

public unitStatus: BehaviorSubject<Number> = new BehaviorSubject<Number>(0)

in component before calling the API to update I call next on BehaviorSubject

onStatusUpdate(status: number) {
    console.log(status)
    this.trainingService.unitStatus.next(status)
    this.subscription = this.trainingService
        .updateTrainingUnitStatus(this.unitId, status)
        .pipe(tap((data) => console.log(data.status)))
        .subscribe(() => {
            this.status$ = this.trainingService.unitStatus.asObservable()
            this.status = this.trainingService.unitStatus.getValue()
            console.log(this.status)
        })
}

In the view I use "status" property which is changing after button tap (and is inline with persistent data from DB)

<FlexboxLayout margin="0" padding="0">
            <Button
                *ngIf="status === 0"
                text="MARK AS DONE"
                (tap)="onStatusUpdate(1)"
                class="-rounded -primary"
            ></Button>
            <Button
                *ngIf="status === 1"
                text="MARK AS NOT DONE"
                (tap)="onStatusUpdate(0)"
                class="-rounded -primary"
            ></Button>
        </FlexboxLayout>
luk
  • 205
  • 3
  • 14