Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-cloud-platform/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 当输入的可观测值发生变化时,如何正确地重新计算布尔值?_Javascript_Angular_Typescript_Rxjs - Fatal编程技术网

Javascript 当输入的可观测值发生变化时,如何正确地重新计算布尔值?

Javascript 当输入的可观测值发生变化时,如何正确地重新计算布尔值?,javascript,angular,typescript,rxjs,Javascript,Angular,Typescript,Rxjs,我有一个组件,我将一个对象作为其输入传递给该组件 该对象来自API,因此无法立即使用。API返回的对象也可以是空的 在模板中,我想隐藏该值,除非它不是空对象 所以我在我的组件中有这个: 从“@angular/core”导入{Component,Input,OnChanges,SimpleChanges}; 从“rxjs”导入{BehaviorSubject,observeable,of}; 从“rxjs/operators”导入{tap}; 从“拉姆达”输入{not,isEmpty}; @组成部

我有一个组件,我将一个对象作为其输入传递给该组件

该对象来自API,因此无法立即使用。API返回的对象也可以是空的

在模板中,我想隐藏该值,除非它不是空对象

所以我在我的组件中有这个:

从“@angular/core”导入{Component,Input,OnChanges,SimpleChanges};
从“rxjs”导入{BehaviorSubject,observeable,of};
从“rxjs/operators”导入{tap};
从“拉姆达”输入{not,isEmpty};
@组成部分({
选择器:“你好”,
模板:`
{{hasName | async}}{{name | async}}
你好{{name | async}}!
`,
风格:[
`
h1{
字体系列:Lato;
}
`
]
})
导出类HelloComponent{
@Input()名称:Observable=of({});
hasName:BehaviorSubject=新的BehaviorSubject(false);
恩戈尼尼特(){
这是我的名字(
轻触(名称=>console.log(名称)),
//计算布尔值的实际逻辑相当复杂,但我
//为了简洁起见,我们在这里对其进行了简化。
点击(name=>this.hasName.next(not(isEmpty(name)))
);
}
}
问题是tap中的console.log从未打印,第二个tap也从未运行过,因此
hasName
的值始终为
false
,设置在类的末尾

我认为在OnInit中,this.name仍然是({})的
,而不是从父级通过模板传入的实际可观察对象

我正在使用({})
,因为在类中键入
this.name
作为
Observable | undefined
会导致更多的样板文件,以检查在使用它之前是否确实定义了它

我怎样才能以惯用的方式完成这项工作


完成Stackblitz:

如果要在输入值更改时做出反应,可以编写自己的设置程序:

export class HelloComponent {
  @Input() set name(name: Observable<{} | { first: string }>) {
    this.name$ = name.pipe(
      tap(name => console.log(name)),
      tap(name => this.hasName.next(not(isEmpty(name))))
    );
  }

  name$: Observable<{} | { first: string }>;

  ...
}
导出类HelloComponent{
@Input()集合名称(名称:可观察){
this.name$=name.pipe(
轻触(名称=>console.log(名称)),
点击(name=>this.hasName.next(not(isEmpty(name)))
);
}
name$:可观察的;
...
}
然后在模板中将
async
绑定到
name$


更新的演示:

您可以大大简化此代码

首先,TS的定义

{} | { first: string }
可以这样做

{ first?: string }
虽然看起来我在做一些整洁的挑选,但实际上我不是。如果您尝试访问变量,请使用第一个选项。首先,您将收到一个错误,说明该变量不存在,因为union无法保证这一点。用后者就可以了

然后,模拟。使用可观察对象时,请尽量不要重新指定可观察对象。以下是您如何在不重新分配任务的情况下完成所做的事情:

  public name$: Observable<{ first?: string }> = concat(
    of({}),
    interval(2000).pipe(
      map(() => ({
        first: `${Math.random()}`
      }))
    )
  );
public name$:Observable=concat(
({})的,
间隔(2000年)。管道(
地图(()=>({
第一个:`${Math.random()}`
}))
)
);
最后是子组件。在99%的情况下,将观察值作为输入传递是一种代码气味。这里您只需要一个表示组件(也称为哑组件)

这个哑组件不应该有任何逻辑(至少尽可能多),下面是您可以做的:

type Nil = null | undefined

@Component({
  selector: "hello",
  template: `
    <h1 *ngIf="name?.first">Hello {{ name.first }}!</h1>
  `
})
export class HelloComponent {
  @Input() name: { first?: string } | Nil;
}
类型Nil=null |未定义
@组成部分({
选择器:“你好”,
模板:`
你好{{name.first}}!
`
})
导出类HelloComponent{
@Input()名称:{first?:string}| Nil;
}
这意味着当你从家长那里打电话时,你只需要:



这里有一个更新的stackblitz:

我建议将您的逻辑移动到
app.component.ts
,而不是将整个可观察对象传递到输入中。这可以帮助您管理该输入的值,并防止将值分配给它两次(您在代码-父组件和子组件中已将空对象分配了两次)

使代码正常工作的最简单方法就是订阅您在
ngOnInit
中创建的可观察对象:

this.name.pipe(
  tap(name => console.log(name)),
  tap(name => this.hasName.next(not(isEmpty(name)))) 
).subscribe();
如果您不想管理它(不想担心未被破坏的观测值),并且您更喜欢使用
async
管道,您可以在
ngOnInit
内部重新分配
this.name

this.name = this.name.pipe(
  tap(name => console.log(name)),
  tap(name => this.hasName.next(not(isEmpty(name)))) 
);

您是否尝试在ngOnInit中订阅此observable?我希望避免这种情况,这样我就不必在OnDestroy中取消订阅。我记得读过一篇文章,最好只使用异步管道,避免明确订阅。好的,我建议将
app
中的
name | async
组件传递给
hello
组件,而不是传递整个可观察组件。然后您可以只检查子组件中的值。虽然这在技术上是正确的,有时也会很有用,但我相信整个代码可以简化很多,并且没有遵循好的实践。我们可以利用“安全导航操作员”(即
)来简单地执行
*ngIf=“name?”first“
,而不是执行所有这些操作。无需在子组件中使用任何可观察的内容,因为响应式编程就是在这个组件中结束的。尽可能避免在组件内订阅。组件应该处理如何检索数据,而不是它们的角色。他们应该只访问随时间提供数据的可观察对象,因此使用异步管道应该是不错的。此外,绝对没有必要在ngOnInit钩子中重新分配一个可观察的对象。只需一次性将其定义为类变量即可。Observable本质上是冷的,如果您使用异步管道,那么只有在视图准备就绪时才能订阅它。也就是说,我认为没有必要使用
hasName
变量。使用angular
中的安全导航操作符就足够了,而且非常简单。当然,我已经说过,我建议将逻辑移到父组件,但也提供了一种以这种方式实现目标的方法