Angular 2:自定义组件的问题 [英] Angular 2: Trouble with Custom Components
问题描述
我目前正在尝试在 Angular 2 中使用 ES6/ES7 编写并使用 Babel
转译的自定义组件.
当我加载页面时,我得到了包含所有适当数据的组件手风琴,但所有面板都关闭了,点击什么也不做.我在这个 TypeScript
另一个显示数据操作的调试日志:
我在这里不知所措,我真的不能使用 TypeScript :(
根据 Mark 的评论进行编辑
事实上,这是 Angular2 处理变化检测的方式.当更改检测运行并且它对对象进行脏检查时,它通过检查对象引用是否更改(它不检查对象的内容是否更改)来执行此操作."(来自马克的评论)
(如果您对 Angular2 中区域的使用方式感兴趣,您可以看看 Mark 的精彩回答:Angular2 相当于一个 AngularJS $watch 是什么?).
所以你需要重构一下你的 removeDynamic
方法:
导出类 AppComponent {(……)移除动态(){this.groups.pop();this.groups = this.groups.slice();//调试console.log('removeDynamic() 运行');}}
请参阅有关使用 slice
方法的答案:
以下是 Angular 团队对此行为的回答:https://github.com/angular/angular/issues/6458.
希望对你有帮助蒂埃里
I am currently trying to get a custom component working within Angular 2 written with ES6/ES7 and transpiled with Babel
.
When I load up the page, I get the component accordion with all of the appropriate data present, but all of the panels are shut and clicking does nothing. I am modelling my accordion after this TypeScript
Plunker. Additionally, the removeDynamic() function from AppComponent
toggles when you click the button but the view does not update to show the change in data. I am at a loss after spending the better part of yesterday and today tinkering with it. Any insight would be much appreciated!
I have my AppComponent
defined as such, with an appropriate template:
import {Component, View} from 'angular2/core'; // Import Component and View constructor (for metadata)
import {HTTP_PROVIDERS} from 'angular2/http'; // We're using http in our
import {Accordion, AccordionGroup} from '../components/accordion/accordion.component';
// Import NgFor directive
import {NgFor} from 'angular2/common';
// Annotate AppComponent class with `Component`
@Component({
selector: 'my-app',
// Let Angular know about `Http`
providers: [HTTP_PROVIDERS]
})
// Define the view of our `Component` using one or more
// `View` annotations
@View({
// Link to our external template file
templateUrl: './components/app.html',
// Specify which directives our `Component` will utilize with
// the `directive` property of the `View` annotation
directives: [Accordion, AccordionGroup, NgFor]
})
export class AppComponent {
constructor() {
// Debug
console.log('AppComponent constructor() go');
this.isOpen = false;
this.groups = [
{
heading: 'Dynamic 1',
content: 'I am dynamic!'
},
{
heading: 'Dynamic 2',
content: 'I am dynamic as well'
}
];
}
removeDynamic() {
this.groups.pop();
// Debug
console.log('removeDynamic() run');
}
};
//export {AppComponent};
The template for AppComponent
:
<p>
<button type="button" class="btn btn-default" (click)="removeDynamic()">
Remove last dynamic
</button>
</p>
<accordion>
<accordion-group heading="This is the header" is-open="true">
This is the content
</accordion-group>
<accordion-group [heading]="group.heading" *ngFor="#group of groups">
{{group.content}}
</accordion-group>
<accordion-group heading="Another group" [is-open]="isOpen">
More content
</accordion-group>
</accordion>
My Angular app is bootstrapped elsewhere due to Webpack.
I have a custom Accordion
component written using Angular 2 with ES6/ES7:
// Import Inject, Component and View constructor (for metadata)
import {Inject} from 'angular2/core';
import {Component, View} from 'angular2/core';
// Import NgClass directive
import {NgClass} from 'angular2/common';
// # Accordion Component
// Annotate Accordion class with `Component`
@Component({
// Define how we can use our `Component` annotation with
// the `selector` property.
selector: 'accordion, [accordion]',
// Modify the `host` element with a css class designator
host: {
'class': 'panel-group'
}
})
// Define the view of our `Component` using one or more
// `View` annotations
@View({
// Link to our external template file
templateUrl: './components/accordion/accordion.html'
})
// Create and export `Component` class
export class Accordion {
constructor() {
// Debug
console.log('Accordion constructor() go');
this.groups = [];
}
// Function to register groups
addGroup(group) {
this.groups.push(group);
}
closeOthers(openGroup) {
this.groups.forEach((group) => {
if(group !== openGroup) {
group.isOpen = false;
}
});
}
removeGroup(group) {
let index = this.groups.indexOf(group);
if(index !== -1) {
this.groups.splice(index, 1);
}
}
}
// # AccordionGroup Component
// Annotate AccordionGroup class with `Component`
@Component({
selector: 'accordion-group, [accordion-group]',
// Specify (with the `inputs` property) that we are using a `heading`
// attribute in our component which will be mapped to a `heading`
// variable in our `Component`.
inputs: ['heading', 'isOpen'],
// Let Angular know about any `providers`
providers: []
})
// Define the view of our `Component` using one or more
// `View` annotations
@View({
// Link to our external template file
templateUrl: './components/accordion/accordion-group.html',
// Specify which directives our `Component` will utilize with
// the `directive` property of the `View` annotation
directives: [NgClass, Accordion]
})
// Create and export `Component` class
export class AccordionGroup {
constructor(accordion) {
// Debug
console.log('AccordionGroup constructor() go');
this._isOpen = false;
this.accordion = accordion;
this.accordion.addGroup(this);
}
// Angular 2 DI desugar'd
// Reference: https://stackoverflow.com/questions/33026015/how-to-inject-angular2-http-service-into-es6-7-class
/*static get parameters() {
return [[Accordion]];
}*/
toggleOpen(event) {
event.preventDefault();
this.isOpen = !this.isOpen;
this.accordion.closeOthers(this);
}
onDestroy() {
this.accordion.removeGroup(this);
}
}
// The above desugar'd Angular 2 DI should work, but doesn't. This
// line seems to accomplish what we are looking for though.
AccordionGroup.parameters = [[Accordion]];
//export {Accordion, AccordionGroup};
This is the template for the Accordion
component:
<ng-content></ng-content>
And the template for my AccordionGroup
component:
<div class="panel panel-default" [ngClass]="{'panel-open': isOpen}">
<div class="panel-heading" (click)="toggleOpen($event)">
<h4 class="panel-title">
<a href tabindex="0"><span>{{heading}}</span></a>
</h4>
</div>
<div class="panel-collapse" [hidden]="!isOpen">
<div class="panel-body">
<ng-content></ng-content>
</div>
</div>
</div>
As an aside, a lot of the code came from this particular tutorial which demonstrates all of the Angular 2 stuff with TypeScript, so I simply adapted it to my es6/es7 environment with Webpack | Migrating Directives to Angular 2
Update: I have attempted both of the answers submitted and neither has solved the problem of the view not updating.
Here is a screen capture of how the component is currently behaving:
And another of the debug logs showing data manipulation:
I am at a loss here guys, and I really can't use TypeScript :(
Edited following the Mark's comment
In fact, it's the way Angular2 handles the change detection. "When change detection runs and it dirty checks an object, it does so by checking to see if the object reference changed (it does not check to see if the contents of the object changed)." (from Mark's comment)
(If you're interested in the way Zones are used in Angular2, you could have a look at the great answer from Mark: What is the Angular2 equivalent to an AngularJS $watch?).
So you need to refactor a bit your removeDynamic
method:
export class AppComponent {
(...)
removeDynamic() {
this.groups.pop();
this.groups = this.groups.slice();
// Debug
console.log('removeDynamic() run');
}
}
See this answer regarding the use of the slice
method:
Here is the answer from the Angular team regarding this behavior: https://github.com/angular/angular/issues/6458.
Hope it helps you, Thierry
这篇关于Angular 2:自定义组件的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!