Fill form after http response in angular2

2019-07-27 03:10发布

I'm wondering what is a best way to load a form after getting the response from server. I wrote some code where it is getting data from server and in my component I am subscribing to the response, but My UI is loading before even I get the response.

I want to use this component for both adding and editing.

Component:

@Component({
selector: 'gate',
templateUrl: '/public/app/views/gate.html',
directives: [GateFormComponent, StrategyComponent],
providers : [MyService]
})

export class MyComponent {

private id:any;

constructor(private _routeParams:RouteParams, @Inject(MyModel) private myModel,
            private myService : MyService) {
}

ngOnInit() {
    this.id = this._routeParams.get("id");
    if (this.id) {
        this.gateDataModel.unique_display_id = parseInt(this.id);
        this.myService.loadData(this.id)
            .subscribe(response => console.log(response));
    }
}

In my component, I am loading 2 components one of which has a form into which I have to load data once I get the response. And all this should only happen if I have an id available.

Service:

@Injectable()
export class MyService extends HTTPServices {

constructor(http:Http) {
    super(http);
}


loadData(id:number) {
    return this.query(url)
        .map(res => res.json())
        .catch(this.handleError)
}


private handleError(error:Response) {
    console.log("Error : ", error);
    return Observable.throw(error.text());
}

HTTPServices

export class HTTPServices {

private headers:Headers;
private http:Http;

defaultOptionsArgs:RequestOptionsArgs;

constructor(http:Http) {
    this.http = http;
    this.headers = new Headers();
    this.headers.append('Content-Type', 'application/json');
    this.defaultOptionsArgs = {
        'headers': this.headers
    };
}



create(servicePath:string, model:any, options?:RequestOptionsArgs) {
    var url = this.getUrl(servicePath);
    var options = options ? options : this.defaultOptionsArgs;
    return this.http.post(url, JSON.stringify(model), options);
}

query(servicePath:string, options?:RequestOptionsArgs) {
    var options = options ? options : this.defaultOptionsArgs;
    return this.http.get(servicePath, options);
}

}

----Edited-----

Finally, I was able to add @CanActivate and it is working.

@Component({
selector: 'gate',
templateUrl: '/public/app/views/gate.html',
directives: [ROUTER_DIRECTIVES, GateFormComponent, StrategyComponent]
})

@CanActivate(
(next: ComponentInstruction, prev: ComponentInstruction) => {
    return new Promise((resolve) => {
        let id = next.params["id"];
        let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
        let http = injector.get(Http);
        if(id){
            http.get(URL)
                .subscribe((response) => {
                    console.log(response)
                    next.routeData.data["response"] = response;
                    // continue
                    resolve(true);
                }, (error) => {
                    resolve(false);
                });
        } else {
            resolve(true);
        }
    });
}

)

export class MyComponent{

private id:any;

constructor(private _routeParams:RouteParams, @Inject(MyModel) private myModel, routeData: RouteData) {
   console.log(routeData.get("response"));
}

}

The component is loading up and then I am getting the response

Thanks

4条回答
姐就是有狂的资本
2楼-- · 2019-07-27 03:24

If you leverage Angular2 routing (and it seems to be the case), you could use leverage the OnActivate interface and its routerOnActivate:

Defines route lifecycle method routerOnActivate, which is called by the router at the end of a successful route navigation.

For a single component's navigation, only one of either OnActivate or OnReuse will be called depending on the result of CanReuse.

The routerOnActivate hook is called with two ComponentInstructions as parameters, the first representing the current route being navigated to, and the second parameter representing the previous route or null.

If routerOnActivate returns a promise, the route change will wait until the promise settles to instantiate and activate child components.

You could return a promise that will be resolved when your data will be there. Here is a sample:

@Component({ ... })
export class MyComponent {
  private id:any;

  constructor(private _routeParams:RouteParams,
              @Inject(MyModel) private myModel,
              private myService : MyService) {
  }

  routerOnActivate() {
    this.id = this._routeParams.get("id");
    return new Promise((resolve, reject) => {
      if (this.id) {
        this.gateDataModel.unique_display_id = parseInt(this.id);
        this.myService.loadData(this.id)
        .subscribe(response => {
          console.log(response);
          resolve();
        });
      } else {
        resolve();
      }
    });
  }

  (...)
}
查看更多
啃猪蹄的小仙女
3楼-- · 2019-07-27 03:43

I had a similar question, but since the solution can be used for your usecase too, i would recommend to have a look at the accepted answer: How to manipulate a component on specific routes in Angular2

The basic idea is to extend the router-outlet directive and override the activate() function which will be called before the next route is activated and waits for a promise to resolve.

For example you could do something like this:

@Directive({
    selector: 'custom-router-outlet'
})
export class CustomRouterOutlet extends RouterOutlet {
    private parentRouter:Router;

    constructor(_elementRef: ElementRef,
            _loader: DynamicComponentLoader,
            _parentRouter: Router,
            @Attribute('name') nameAttr: string,
            private _myRoutingService:MyRoutingService) {
        super(_elementRef, _loader, _parentRouter, nameAttr);
        this.parentRouter = _parentRouter;
    }

    activate(nextInstruction: ComponentInstruction): Promise<any> {
        let someRouteSpecificData = nextInstruction.routeData.data['someRouteData'];
        if(someRouteSpecificData) {
             _myRoutingService.beforeRoute(someRouteSpecificData).subscribe( () => {

                 // go on after this has been resolved
                 return super.activate(nextInstruction);

                 // or maybe cancel the route:
                 return false;

                 // or maybe do something crazy:
                 nextInstruction.componentType = MyOtherComponent;
                 return super.activate(nextInstruction);
             }

        }
        return super.activate(nextInstruction);
    }
}

I think you could easily change this for your purposes. You could utilize your @RouteConfig for example to hold some information on what should happen or be checked on a route change.

Another approach would be to use the @CanActivate decorator like mentioned here already, but its a bit harder to accomplish. It just feels a bit hacky at this point. I could add this later if you're interested.

查看更多
贪生不怕死
4楼-- · 2019-07-27 03:47

I have been able to implement this using the resolve functions of the router (https://angular.io/docs/ts/latest/guide/router.html#!#resolve-guard). This enables the http calls to be made and the route only completes when the http call observable returns.

There are good examples here: https://angular.io/resources/live-examples/router/ts/plnkr.html

查看更多
放荡不羁爱自由
5楼-- · 2019-07-27 03:50

In you component you can just use

template: `
 <div *ngIf="data">
  <!-- form goes here -->
 </div>
`

where data is a property that is set to some value when the response from the server arrived.

查看更多
登录 后发表回答