0
votes

This seems like a pretty simple case to me, but I'm obviously missing something. I have a Model to be bound to the View. I then load the Model with an Http call. Why doesn't the View update? I thought that was the whole point of one-way binding.

I have verified that I'm getting back the data I'm expecting from the http call.

Update I added a button to the screen and databinding will actually update the screen with the http loaded data for both fields on button push, even though the button method only sets one of the values. So either there's a bug in NativeScript or I'm not doing something incorrectly.

Update 2 Just the act of clicking the button will trigger the binding to happen. I've modified the code to have an empty tap handler, and just clicking the button makes it bind.

typescript

import { Component, ChangeDetectionStrategy, OnInit } from "@angular/core";
import { Job } from "../../shared/customer/job";
import { Http, Headers, Response } from "@angular/http";
import { Observable } from "rxjs/Rx";

@Component({
    selector: "my-app",
    templateUrl: "pages/job-details/job-details.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})

export class JobDetailsComponent implements OnInit {
    job: Job;
    salesAssociateName: string = "x";

    constructor(private http: Http) {
        this.job = new Job();
    }

    ngOnInit() {
        this.getJob(1234);
    }

    getJob(leadId: number) {
        var url = "https://url-not-for-you/job?franchiseeid=48&leadid=" + leadId;
        var headers = this.createRequestHeader();

        this.http.get(url, { headers: headers }).map(response => response.json())
            .do(data => this.setData(data[0]))
            .subscribe(
                () => this.success(),
                (error) => this.error()
            );
    }

    private createRequestHeader() {
        let headers = new Headers();
        headers.append("AuthKey","blah");
        headers.append("AuthToken", "blee");

        return headers;
    }

    setData(job) {
        this.job.FullName = job["FullName"];
        this.job.SalesAssociateName = job["SalesAssociateName"];
        this.salesAssociateName = this.job.SalesAssociateName;

        console.log("Found job for customer: " + job["FullName"]);
    }

    success() {
        // nothing useful
    }

    error() {
        alert("There was a problem retrieving your customer job.");
    }

    changeSA() {
    }
}

html

<StackLayout>
    <Label [text]="job.FullName"></Label>
    <Label [text]="salesAssociateName"></Label>
    <Button text="Push" (tap)="changeSA()"></Button>
</StackLayout>
1

1 Answers

2
votes

Your code will work as expected with the default ChangeDetectionStrategy. however, you have changed the strategy to onPush

In order to make your binding work as expected in the default changeStrategy delete the following line

changeDetection: ChangeDetectionStrategy.OnPush

or change it to

changeDetection: ChangeDetectionStrategy.Default

More about the Angular-2 ChangeDetectionStrategy here and here

If you still want to use onPush instead of the default strategy then your properties should be declared as @input() and once the change is made (in your case in setData) marked with markForCheck()

The reason your binding is working when triggered from Button tap is because application state change can be triggered by:

  • Events - tap, swipe,
  • XHR - Fetching data from a remote server
  • Timers - e.g. setTimeout()

For testing purposes and if someone is interested of how to implement the scenario with onPush here is a sample code:

import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, NgZone, Input } from "@angular/core";
import { Http, Headers, Response } from "@angular/http";
import { Observable as RxObservable } from "rxjs/Rx";
import "rxjs/add/operator/map";
import "rxjs/add/operator/do";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
    @Input() public job: any = { salesAssociateName: "default job" };
    @Input() public salesAssociateName: string = "default name";


    constructor(private http: Http, private change:ChangeDetectorRef) { }

    ngOnInit() {
        this.getJob();
    }

    getJob() {
        var url = "http://httpbin.org/get";
        var headers = this.createRequestHeader();

        this.http.get(url, { headers: headers })
            .map(response => response.json())
            .do(data => {
                this.setData();
            }).subscribe(
                () => this.success(),
                (error) => this.error()
            );
    }

    private createRequestHeader() {
        let headers = new Headers();
        return headers;
    }

    setData() {

        this.job.salesAssociateName = "NEW job SalesAssociateName";
        this.salesAssociateName = "NEW job FullName";
        this.change.markForCheck();
    }

    success() {
        alert("success");
    }

    error() {
        alert("There was a problem retrieving your customer job.");
    }
}