嗨,我是Angular2的新手,还有Typescript(我也是StackOverflow.com的新手),我想知道你是否可以帮我解决以下问题: 我在单击按钮时使用 ngOnChanges()在按钮上创建了自己的折叠动画。现在,我的目标是当用户点击页面上的任何位置时(在我显示的菜单之外或管理菜单的按钮之外的其他地方),菜单会折叠为隐藏。
要做到这一点,我希望我的collapse.animation.ts在使用clickOutsideEvent()更新来自我的header.component.ts的collapse属性时使用ngDoCheck()进行检测。
我一直试图让(blur)="onBlur()"
或(blur)="expression"
这样的活动发挥作用,但遗憾的是由于专注于单个html元素,它无法正常工作。或者我没有成功地做到这一点......
这是我得到的例外情况: angular2.dev.js:23730 EXCEPTION:尝试区分' true'时出错在[isCollapsed in HeaderComponent @ 16:59]
中很抱歉,如果帖子太长或者说不够清楚,我们将非常感谢您的帮助。
collapse.animation.ts :我在这个文件中管理了崩溃动画,并且我尝试检测崩溃变化并相应地隐藏/显示。
import {Directive, DoCheck, KeyValueDiffers , OnChanges, ElementRef, Input } from 'angular2/core';
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {CssAnimationBuilder} from 'angular2/src/animate/css_animation_builder';
@Directive({
selector: '[collapse]',
host: {
'[attr.aria-expanded]': '!collapse',
'[attr.aria-hidden]': 'collapse'
}
})
export class Collapse implements OnChanges, DoCheck {
@Input() duration: number = 200; // Vitesse de l'animation en ms. (750 = 0.75 sec)
@Input() collapse: any; // Booléen définissant l'état collapse ou non
private _animation: CssAnimationBuilder; // Animation CSS
differ: any; // Tracker de propriété / attribut.
constructor(private _animationBuilder: AnimationBuilder, private _element: ElementRef, private differs: KeyValueDiffers) {
// Initialisation de l'animation css.
this._animation = _animationBuilder.css();
// Initialisation du tracker
this.differ = differs.find({}).create(null);
}
// trying to make this work...
ngDoCheck() {
var changes = this.differ.diff(this.collapse);
if (changes) {
changes.forEachChangedItem((elt) => {
if (elt.key === 'isCollapsed') {
this.show();
}
else {
this.hide();
}
});
}
}
// Manage property collapse
// This works when I click on my button
ngOnChanges(changes) {
if (changes.collapse) {
if (this.collapse) {
this.hide();
} else {
this.show();
}
}
}
header.component.ts :这是我的标题组件,其中包含我的菜单
import { Component, Input } from 'angular2/core';
import { RouteConfig, RouterOutlet, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from "angular2/router";
import { Collapse } from './../../css/animations/collapse.animation';
import { ClickOutside } from './../directives/clickOutside.directive';
@Component({
selector: 'headerComponent',
templateUrl: 'app/master/header.component.html',
styleUrls: ['app/master/header.component.css', 'app/master/menuNavigation.component.css', 'css/animations/collapse.animation.css'],
directives: [
ROUTER_DIRECTIVES,
Collapse,
ClickOutside
],
providers: [
ROUTER_PROVIDERS
],
})
export class HeaderComponent {
@Input() collapse: boolean = false;
private headerMenuClass: string = "header_menu";
// clickOutside linked method
handleClickOutside(className) {
// I'm detecting the "click" on my button linked to my menu with it's class "header_menu". No need to collapse the menu when I click on my button.
let FirstClassName: string = className.split(" ")[0];
if (FirstClassName != this.headerMenuClass) {
console.log(this.collapse);
// Trying to make my collapse.animation.ts detect this !!
this.collapse = !this.collapse;
}
}
}
header.component.html :我的组件的HTML模板
<div id="top_bar" class="top_bar">
<!-- This is the button that shows the menu -->
<button type="button" class="header_menu nav_icon root_navitem" id="btn_menu_switch" (click)="isCollapsed = !isCollapsed">
<img class="header_menu" src="css/App_Themes/VAL/48x48/m_bars.png" alt="" />
</button>
<button type="button" class="nav_icon" id="btn_home" [routerLink]="['Accueil']">
<img src="css/App_Themes/VAL/48x48/m_home.png" alt="" />
</button>
</div>
<!-- This is the menu I'm trying to collapse at will -->
<div id="left_bar" tabindex="-1" class="left_bar collapse" [collapse]="isCollapsed"
(clickOutside)="handleClickOutside( $event.target.className )">
<div id="left_bar_menu" class="left_bar_menu">
<button type="button" class="left_bar_icon" id="btn_left_histo">
<img src="css/App_Themes/VAL/48x48/m_history.png" alt="" />
</button>
<hr class="sep" />
<button type="button" class="left_bar_icon" id="btn_left_board" [routerLink]="['Dashboard']">
<img src="css/App_Themes/VAL/48x48/m_board.png" alt="" />
</button>
<hr class="sep" />
</div>
</div>
clickOutside.directive.ts :此文件允许我检测何时点击组件外部并获取一些信息。我认为这不是管理我的问题的一种干净方式,但我稍后会对此进行处理。
import {Directive, EventEmitter, Input, Output } from 'angular2/core';
// Variable globale à la classe
var localEvent: any = null;
@Directive({
selector: '[clickOutside]',
host: {
"(click)": "trackEvent( $event )",
"(document: click)": "compareEvent( $event )"
}
})
export class ClickOutside {
@Output() clickOutside;
constructor() {
this.clickOutside = new EventEmitter();
}
// If the event at the document root is the same reference as the
// event at the target, it means that the event originated from
// within the target and bubbled all the way to the root. As such,
// if the event at the document root does NOT MATCH the last known
// event at the target, the event must have originated from
// outside of the target.
compareEvent(event) {
if (event !== localEvent) {
this.clickOutside.emit(event);
}
localEvent = null;
}
// I track the click event on the bound target.
trackEvent(event) {
// When the user clicks inside the bound target, we need to start
// tracking the event as it bubbles up the DOM tree. This way,
// when a click event hits the document root, we can determine if
// the event originated from within the target.
localEvent = event;
}
}
答案 0 :(得分:0)
我不认为ngDoCheck
就是你需要的。事实上,Angular2通过基元类型的值和对象的引用来检测更改,但是没有检测到数组或对象的更改。
对于这种情况(不是默认检测),您需要使用ngDoCheck
来实现检测这些更新的自定义方式。它依赖于对象的类KeyValueDiffers
和对于数组的IterableDiffers
。但是你不能使用布尔值的KeyValueDiffers
类。它没有做到这一点。
在您的情况下,您可以依赖collapse
输入属性的默认检测。更新其值后,将调用ngOnChanges
并触发您自己的处理。
话虽这么说,如果要在应用程序的任何位置实现折叠功能和触发器,您应该考虑使用共享服务,例如MenuService
。您可以在菜单状态中放置一个布尔属性,并在更新时通知observable / subject:
export class MenuService {
collapse:boolean;
collapse$:Subject<boolean> = new Subject();
toggleMenu() {
this.collapse = !this.collapse;
this.collapse$.next(this.collapse);
}
}
在引导您的应用程序时不要忘记它:
bootstrap(AppComponent, [ MenuService ]);
并且不要在组件的providers
属性中再次定义它。
然后,您可以将其注入组件/指令,以更新状态或在状态更新时得到通知。
以下是您的指令中的示例:
@Directive({
selector: '[collapse]',
host: {
'[attr.aria-expanded]': '!collapse',
'[attr.aria-hidden]': 'collapse'
}
})
export class Collapse implements OnChanges {
@Input() duration: number = 200;
@Input() collapse: any;
private _animation: CssAnimationBuilder; // Animation CSS
constructor(private _animationBuilder: AnimationBuilder,
private _element: ElementRef, private service:MenuService) {
this._animation = _animationBuilder.css();
this.service.this.collapse$.subscribe((collapse) => {
this.updateMenu(collapse);
});
}
ngOnChanges(changes) {
if (changes.collapse) {
this.updateMenu(changes.collapse);
}
}
updateMenu(collapse:boolean) {
if (this.collapse) {
this.hide();
} else {
this.show();
}
}
}
可以使用MenuService
从应用程序的任何位置更新状态。例如,在与菜单没有任何关系的组件中:
@Component({
(...)
template: `
<div (click)="toggleMenu()">Toggle menu</div>
`
})
export class SomeComponent {
constructor(private service:MenuService) {
}
toggleMenu() {
this.service.toggleMenu();
}
}