【问题标题】:Change @Input field of @Component after sending an Http Request (Angular2)发送 Http 请求后更改 @Component 的 @Input 字段(Angular2)
【发布时间】:2017-05-02 12:25:05
【问题描述】:

我正在开发一个Angular2 应用程序,其中一个@Components 有一个按钮,单击该按钮将向我的服务器发送post 请求,该请求将以Ok(string) 或BadRequest(string) 响应。

从服务器获得答案后,​​我无法更新我的 @Component 之一的 @Input 字段。

以下是我的一些课程的简化版本。

我的组件类

@Component({
    moduleId: module.id,
    selector: 'model-comp',
    templateUrl: './model.component.html',
    styleUrls: ['./model.component.css']
})
export class MyComponent{
    @Input() model: Model;
    @Output() emitter: EventEmitter<Model> = new EventEmitter<Model>();

    public constructor(private service: MyService){}

    public toggle(): void {
        this.service.send(model.id, model.name){
            .subscribe(
                result  => this.onSuccess(result)),
                error   => this.onError(error),
                ()      => this.onComplete());
    }

    public onSuccess(result: string): void {
        if(result.inculdes("Some Text")) this.model.flag = true;
        else this.model.flag = false;
        this.emitter.emit(this.model);
    }

    public onError(error: any): void {
        //notification using bootstrap-notify
    }

    public onComplete(): void {
        //currently empty
    }
}

我的服务类

export class MyService{

    public send(id: string, name: string){
        return <Observable<string>>this.http
            .post('url', new Dto(id, name))
            .map(result => this.getData<string>(result))
            .catch(this.catchBadResponse);
    }

    private getData<E>(result: Response): E {
        //checking if result.status is ok
        var body = result.json ? res.json(): null;
        return  <E>(body || {});
    }

    private catchBadRespomse: (error: any) => Observable<any> = (error: any) => {
        var response = <Response>error;
        var json = response.json();
        var msg = json.Message;
        var errormsg = json?
            (json.error ? json.error: JSON.stringify(msg?msg:json)) :
            (response.statusText || 'Error?');
        return Obserable.of(errormsg);
    }

}

MyComponent 的模板

<button (click)="toggle()"
[ngClass]="{'class1': true, 'class2': model.flag}">Text</button>

父组件模板

<div *ngFor="let model of getList()">
    <model-comp [model]="model" (emitter)="onEmit($event)"></model-comp>
</div>

onEmit 函数

onEmit(evt: any): void{
    if(evt instanceof Model){
        var evtModel = evt as Model;
        this.list.find(search => search.id == evtModel.id)
            .isFav = evtModel.isFav;
    }
}

问题是,即使我发布了我的数据并收到了响应,我的model 的属性flag 并没有改变。

我认为点击事件会重新加载组件,从而移除EventEmitter 的观察者。

那么有没有什么办法可以取消重载,不丢失EventEmitter的观察者或者任何其他方式来更新根对象或者元素类呢?

【问题讨论】:

  • 如果你有一个输入和输出,你应该把它命名为@Input() model:Model;和@Output() modelChange:EventEmitter&lt;Model&gt; = ...,然后你可以使用“双向”绑定,比如[(model)]="someProp"
  • @GünterZöchbauer 我如何命名输出字段重要吗?
  • 名称总是很重要 ;-) 如果它是关于相同的属性,那么它们应该根据我提到的模式来命名。只是一个提示 - 与您的实际问题无关。
  • 为什么订阅会重新加载组件?我认为情况并非如此。为什么你认为model.flag 没有改变——因为class2 没有添加?
  • 您可以尝试添加一个类似&lt;button (click)="model.flag = !model.flag"&gt;click me&lt;/button&gt; 的按钮并检查这是否会导致预期的行为?

标签: javascript angular typescript input observable


【解决方案1】:

我认为您不应该在组件内更改 @input 属性。 它假设监听父组件的变化并采取行动。

【讨论】:

  • 那么有没有办法改变父组件的模型呢?或者添加/删除按钮类的方法?
  • 当然,您应该阅读以下内容:blog.thoughtram.io/angular/2016/10/13/…
  • 不太确定我应该在哪里使用 2 路数据绑定?您可以将其添加到您的答案中吗?我应该像&lt;my-comp [(model)] = "model.subModel"&gt;&lt;/my-comp&gt; 一样添加它吗?
  • 完全正确。父组件向子组件推送数据,并监听来自子组件的变化。
  • 我尝试使用EventEmitter,但是当我在onSuccess() 方法中调用emit 时,观察者被移除(从toggle 调用时有效,但在那里有点没用)。
【解决方案2】:

更新(见问题下方的 cmets)

如果getList()(*ngFor 绑定到的对象)每次调用时都返回一个新列表,*ngFor 将永远忙于重新呈现项目,因为更改检测会导致getList() 被一次又一次地调用。

绑定到每次直接调用时都会返回新对象或数组的函数会导致严重问题,例如异常和性能大幅下降。

通常强烈建议不要在视图中使用方法/函数调用。而是将列表分配给一个字段并绑定到该字段而不是方法。

ngOnInit() 适用于初始化列表,也适用于初始化或更新列表的任何事件处理程序。

原创

如果您修改从父级传入的model 值,则父级也会看到更改。将值作为事件发出可能是多余的。

我猜你正在修改onEmit() 中的list(来自&lt;div *ngFor="let model of list"&gt;),这会导致*ngFor 重新呈现列表。

【讨论】:

  • 我无法想象这会起作用。您版本中的语法没有任何意义。我不完全确定class1 和class2 是否需要'。我认为它们仅在 Dart 中是必需的,但我可能混淆了一些东西。但我确定= 是错误的。
【解决方案3】:

我的组件.ts

export class MyComponent{
    @Input() model: Model;
    //@Output() emitter: EventEmitter<Model> = new EventEmitter<Model>();

    public constructor(private service: MyService){}

    public toggle(): void {
        this.service.send(model.id, model.name){
            .subscribe(
                result  => this.onSuccess(result)),
                error   => this.onError(error),
                ()      => this.onComplete());
    }

    public onSuccess(result: string): void {
        if(result.inculdes("Some Text")) this.model.flag = true;
        else this.model.flag = false;
        //this.emitter.emit(this.model);
        this.service.emitter.next(false);
    }

    public onError(error: any): void {
        //notification using bootstrap-notify
    }

    public onComplete(): void {
        //currently empty
    }
}

服务

@Injectable // important
export class MyService{
    public emitter: Subject<any> = new Subject();
    public send(id: string, name: string){
        return <Observable<string>>this.http
            .post('url', new Dto(id, name))
            .map(result => this.getData<string>(result))
            .catch(this.catchBadResponse);
    }

    private getData<E>(result: Response): E {
        //checking if result.status is ok
        var body = result.json ? res.json(): null;
        return  <E>(body || {});
    }

    private catchBadRespomse: (error: any) => Observable<any> = (error: any) => {
        var response = <Response>error;
        var json = response.json();
        var msg = json.Message;
        var errormsg = json?
            (json.error ? json.error: JSON.stringify(msg?msg:json)) :
            (response.statusText || 'Error?');
        return Obserable.of(errormsg);
    }

}

现在您可以在应用中的任何位置收听Service.emitter

【讨论】:

猜你喜欢
  • 2023-03-11
  • 2019-05-09
  • 2017-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-12
  • 2016-12-03
  • 1970-01-01
相关资源
最近更新 更多