Javascript Angular 4 显示其他组件的弹出窗口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45677139/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angular 4 show popup onclick by other component
提问by SBO
i'm struggling about this problem and can't figure out. I simply need to show a popup div situated in the page clicking from a menu entry in my navbar.component.
我正在努力解决这个问题,无法弄清楚。我只需要在我的 navbar.component 中的菜单项中单击,显示位于页面中的弹出 div。
I added a property "show" in my popup which prints the "show" class on my div using the ngClass (with if) directive. I can get this working if the action button is insidemy popup component but i cannot print the show class clicking on another component. The property in the Object get updated but the class is not printed. I'm using angular 4 with ng-bootstrap. I tried both with services and with parent/child emit event.
我在弹出窗口中添加了一个属性“show”,它使用 ngClass(带 if)指令在我的 div 上打印“show”类。如果操作按钮在我的弹出组件内,我可以让它工作,但我无法打印单击另一个组件的显示类。对象中的属性得到更新,但类未打印。我在 ng-bootstrap 中使用 angular 4。我尝试了服务和父/子发出事件。
This is is my situation:
这是我的情况:
app.component.html
应用程序组件.html
<app-nav-bar></app-nav-bar>
<app-login></app-login>
<router-outlet></router-outlet>
<app-footer></app-footer>
navbar.component.html
导航栏.component.html
...
<button class="dropdown-item" (click)="showPopup()">LOGIN</button>
...
navbar.component.ts
导航栏.component.ts
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
moduleId: module.id,
selector: 'app-nav-bar',
templateUrl: 'navbar.component.html',
styleUrls: ['./navbar.component.css'],
})
export class NavbarComponent implements OnInit {
@Output() show = new EventEmitter<boolean>();
ngOnInit() {
}
showPopup() {
this.show.emit(true);
}
}
login.component.html
登录.component.html
<div id="wrapper-login-popup" class="fade-from-top" [(class.show)]="show">
<div id="container-login-popup">
<div class="row">
<div class="col-sm-12 text-center">
<img id="popup-bomb" src="assets/images/bomb.png" alt="bomb"/>
<img id="popup-close" class="close-icon" src="assets/images/close.png" alt="close"
(click)="closePopup()"/>
</div>
</div>
</div>
</div>
login.component.ts
登录.component.ts
import {Component, Input, OnInit} from '@angular/core';
import {AuthService} from '../services/auth.service';
import {IUser} from './user';
@Component({
selector: 'app-login',
templateUrl: 'login.component.html',
styleUrls: ['login.css']
})
export class LoginComponent implements OnInit {
private username: string;
private password: string;
@Input() show: boolean = false;
constructor(private AuthService: AuthService) {
}
ngOnInit() {
}
login() {
...
}
showPopup() {
console.log(this); //Show is false
this.show = true;
console.log(this); //Show is true but does not trigger the show class
}
closePopup() {
this.show = false;
}
}
回答by bryan60
The issue here is that your nav-bar and login components are siblings and can't directly communicate with each other. You have show as an output of navbar and as an input of login, but you haven't connected the dots.
这里的问题是您的导航栏和登录组件是兄弟组件,不能直接相互通信。您已显示为导航栏的输出和登录的输入,但您尚未连接点。
You need to update your app.component to connect them.
你需要更新你的 app.component 来连接它们。
export class AppComponent implements OnInit {
show = false;
onShow() { this.show = true; }
}
and in the template:
并在模板中:
<app-nav-bar (show)="onShow()"></app-nav-bar>
<app-login [(show)]="show"></app-login>
There's a lot of two way binding going on here which works for something simple liek this, but generally it's a bad idea as it leads to unmaintainable code. You should choose one owner of the show variable and force all changes to it through him. In this case the app component is the most logical owner, so I'd change the login component to emit an event that changes the show variable in app component adn remove all 2 way bindings, but in a bigger app, you may even want a separate service that manages hiding/showing pop ups. This eliminates the need for the sending a message up and down your component tree, you can inject the service where it's needed.
这里有很多双向绑定,它们适用于一些简单的事情,但通常这是一个坏主意,因为它会导致无法维护的代码。您应该选择 show 变量的一个所有者,并通过他强制对其进行所有更改。在这种情况下,应用程序组件是最合乎逻辑的所有者,因此我将更改登录组件以发出一个事件,该事件更改应用程序组件中的 show 变量并删除所有 2 路绑定,但在更大的应用程序中,您甚至可能需要一个管理隐藏/显示弹出窗口的单独服务。这消除了在组件树上下发送消息的需要,您可以在需要的地方注入服务。
As another commenter mentioned, you also should be using ngClass for class manipulation like
正如另一位评论者所提到的,您还应该使用 ngClass 进行类操作,例如
[ngClass]="{'show':show}"
a service based solution would look like
基于服务的解决方案看起来像
import {Subject} from 'rxjs/Subject';
@Injectable()
export class PopUpService {
private showPopUpSource = new Subject();
showPopUp$ = this.showPopUpSource.asObservable();
showPopUp() { this.popUpSource.next(true); }
closePopUp() { this.popUpSource.next(false); }
}
Then you provide in app module or at app component level:
然后您在应用模块或应用组件级别提供:
providers:[PopUpService]
make sure you don't re provide this later, as you only want one copy to exist so everyone shares it.
请确保您以后不要重新提供此内容,因为您只希望存在一份副本,以便每个人都共享它。
then inject into both components, and have them call the services close or show pop up methods.
然后注入两个组件,并让它们调用服务关闭或显示弹出方法。
then in the login component you bind to the popUp$ observable like
然后在登录组件中绑定到 popUp$ observable 就像
constructor(private popUpSvc:PopUpService){}
show$;
ngOnInit() { this.show$ = this.popUpSvc.showPopUp$; }
showPopUp() { this.popUpSvc.showPopUp(); }
closePopUp() { this.popUpSvc.closePopUp(); }
and in the template subscribe w async pipe like
并在模板中订阅 w 异步管道,例如
<div id="wrapper-login-popup" class="fade-from-top" [ngClass]="{'show': (show$ | async) }">
The reason for using the async pipe is garbage collection managemetn is simpler. If you don't use async, you need to garbage collect manually in ngOnDestroy by calling unsubscribe(), otherwise your subscriptions will keep stacking up. There is also a more nuanced benefit in that the async pipe triggers change detection, but this only becomes important if you start using onPush change detection for performance optimization.
使用异步管道的原因是垃圾收集管理更简单。如果不使用异步,则需要在 ngOnDestroy 中通过调用 unsubscribe() 手动进行垃圾回收,否则您的订阅将不断堆积。还有一个更细微的好处,即异步管道触发更改检测,但这只有在您开始使用 onPush 更改检测进行性能优化时才变得重要。

