【问题标题】:Angular 2: Trouble with Custom ComponentsAngular 2:自定义组件的问题
【发布时间】:2016-05-06 17:43:54
【问题描述】:

我目前正在尝试让一个自定义组件在使用 ES6/ES7 编写并使用 Babel 转译的 Angular 2 中工作。

当我加载页面时,我得到了包含所有适当数据的组件手风琴,但是所有面板都关闭并且单击什么也不做。我正在模仿我的手风琴TypeScriptPlunker。此外,AppComponent 中的 removeDynamic() 函数会在您单击按钮时切换,但视图不会更新以显示数据的更改。在昨天和今天的大部分时间都在修补它之后,我不知所措。任何见解将不胜感激!

我的AppComponent 已定义为这样,并带有适当的模板:

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};

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>

由于 Webpack,我的 Angular 应用程序在其他地方被引导。

我有一个使用 Angular 2 和 ES6/ES7 编写的自定义 Accordion 组件:

// 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};

这是Accordion 组件的模板:

<ng-content></ng-content>

还有我的AccordionGroup 组件的模板:

<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>

顺便说一句,很多代码都来自这个特定的教程,它使用 TypeScript 演示了所有 Angular 2 的东西,所以我只是简单地将它调整到我的 es6/es7 环境和 Webpack | Migrating Directives to Angular 2

更新:我已经尝试了两个提交的答案,但都没有解决视图不更新的问题。

这是组件当前行为的屏幕截图:

另一个显示数据操作的调试日志:

伙计们,我很茫然,我真的不能使用 TypeScript :(

【问题讨论】:

    标签: angularjs angular


    【解决方案1】:

    根据马克的评论编辑

    事实上,这是 Angular2 处理变更检测的方式。 “当更改检测运行并且它对对象进行脏检查时,它会检查对象引用是否更改(它不会检查对象的内容是否更改)。” (来自马克的评论)

    (如果您对区域在 Angular2 中的使用方式感兴趣,您可以看看 Mark 的精彩回答:What is the Angular2 equivalent to an AngularJS $watch?)。

    所以你需要重构一下你的 removeDynamic 方法:

    export class AppComponent {
      (...)
    
      removeDynamic() {
        this.groups.pop();
    
        this.groups = this.groups.slice();
    
        // Debug
        console.log('removeDynamic() run');
      }
    }
    

    有关slice 方法的使用,请参阅此答案:

    以下是 Angular 团队对此行为的回答:https://github.com/angular/angular/issues/6458

    希望对你有帮助 蒂埃里

    【讨论】:

    • 虽然这可能修复了removeDynamic 功能,但单击时我的内容仍然没有,并且由于某种原因按钮仍然没有更新视图。每次调用函数时使用console.log(this.groups) 表明数据正在从数组中删除,即使使用我的原始函数,但是视图由于某种原因没有更新。无论如何,我非常感谢您的帮助。
    • “对象内的更新不会触发更改检测”——我的措辞会有所不同。更改检测始终在事件触发后运行(事件触发更改检测,因为 Zone.js 挂钩)。我会改写如下:当更改检测运行并且它对对象进行脏检查时,它会检查对象引用是否更改(它不检查对象的内容是否更改)。
    • 你绝对是对的。我的句子模棱两可!非常感谢您指出这一点 ;-) 我通过引用您更新了我的答案...
    • 假设那时没有发生变更检测是否合理?本来以为跟 DI 有关系,现在好像什么都有了,但是操作数据的时候没有检测到变化?
    • 当你只添加一个元素到一个数组时,对应的视图不会被更新...如果你重新创建数组,视图会。这是我在回答中试图解释的。
    【解决方案2】:

    我在这个 plnkr 中有一个准系统版本。

    @Component({
      selector: 'my-app',
      providers: [],
      template: `
          <div class="panel panel-default" [ngClass]="{'panel-open': isOpen}">
            <div class="panel-heading" (click)="toggleOpen($event)">
              <h4 class="panel-title">
              </h4>
            </div>
            <div class="panel-collapse" [ngClass]="{'hidden': isOpen}">
              <div class="panel-body">
                <h3>geee</h3>
              </div>
            </div>
          </div>
      `,
      directives: [NgClass]
    })
    export class App {
      public isOpen = false;
    
      public groups = [
        {
          heading: 'Dynamic 1',
          content: 'I am dynamic!'
        },
        {
          heading: 'Dynamic 2',
          content: 'I am dynamic as well'
        }
      ];
    
      constructor(){
        this.isOpen = false;
        this.groups = [
          {
            heading: 'Dynamic 1',
            content: 'I am dynamic!'
          },
          {
            heading: 'Dynamic 2',
            content: 'I am dynamic as well'
          }
        ];
      }
      toggleOpen(event) {
        event.preventDefault();
        this.isOpen = !this.isOpen;
        //this.accordion.closeOthers(this);
      }
    
    }
    

    http://plnkr.co/edit/MjQ0GDimtqi20zmrDAEB?p=preview

    【讨论】:

    • 如果我将 AccordionGroup 模板更改为此,它似乎不会呈现任何内容。
    • 不,它呈现相同的内容,单击仍然没有按应有的方式切换它们。
    • 不幸的是,您的示例看起来像是使用 TypeScript,此外,它删除了所有的自定义内容嵌入,这很重要。感谢您的帮助,但这并不能解决手头的问题。
    • 这是一个工作的 plunker,它使用 TypeScript 演示了与我尝试使用 es6 重新创建的功能完全相同的功能:plnkr.co/edit/PvKuiBon0PpM6sNSehc6?p=preview。然而,我似乎无法通过我的努力来重现这一点。我开始认为这可能与 DI 有关,因为我无法使用 @Inject 装饰器将 Accordion 注入 AccordionGroup 组件以在构造函数中使用。
    猜你喜欢
    • 1970-01-01
    • 2021-03-20
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 2017-04-11
    • 2018-04-11
    • 2019-06-13
    • 1970-01-01
    相关资源
    最近更新 更多