【问题标题】:Pass inputs to nested component in Angular 2将输入传递给Angular 2中的嵌套组件
【发布时间】:2016-11-02 11:47:20
【问题描述】:

如何将属性从包装组件透明地转换为嵌套组件?

考虑到有

const FIRST_PARTY_OWN_INPUTS = [...];
const FIRST_PARTY_PASSTHROUGH_INPUTS = ['all', 'attrs', 'are', 'passed'];
@Component({
  selector: 'first-party',
  inputs: [...FIRST_PARTY_OWN_INPUTS, ...FIRST_PARTY_PASSTHROUGH_INPUTS],
  template: `
<div>
  <third-party [all]="all" [attrs]="attrs" [are]="are" [passed]="passed"></third-party>
  <first-party-extra></first-party-extra>
</div>
  `,
  directives: [ThirdParty]
})
export class FirstParty { ... }

输入可以批量翻译,这样就不会在模板中枚举?

上面的代码应该重新创建 Angular 1.x 指令的配方:

app.directive('firstParty', function (thirdPartyDirective) {
  const OWN_ATTRS = [...];
  const PASSTHROUGH_ATTRS = Object.keys(thirdPartyDirective[0].scope);

  return {
    scope: ...,
    template: `
<div>
  <third-party></third-party>
  <first-party-extra></first-party-extra>
</div>
    `,
    compile: function (element, attrs) {
      const nestedElement = element.find('third-party');

      for (let [normalizedAttr, attr] of Object.entries(attrs.$attr)) {
        if (PASSTHROUGH_ATTRS.includes(normalizedAttr)) {
          nestedElement.attr(attr, normalizedAttr);
        }
      }
    },
    ...
  };
});

【问题讨论】:

  • 你设置 wirg setAttribute 的什么值来得到错误?
  • @GünterZöchbauer 我猜这个错误是由 onInit 中的this.elementRef.nativeElement.querySelector('third-party').setAttribute('[attr]', 'attr') 之类的东西引起的。改用 bind-attr 不会触发错误,但也无济于事。无论如何,我很肯定有一些惯用的方式来以编程方式建立数据绑定,而且看起来这不是其中之一。
  • @olsn 这可能是大量组件/指令的典型任务。并且可以抽象为辅助函数。有时 WET 代码更可取,有时则不是,这是开发人员的选择。我认为,缺乏如何保持干燥的知识并不是 WET 的借口。是的,它们是带有重音符号的模板文字。
  • @GünterZöchbauer 这是通过添加样式/额外标记/任何东西来自定义第三方组件的经典场景。以前做过很多。 “如果你想定制,就包装它”是惯用的,如果我错了,请纠正我。我在this question 中研究了解决该问题的另一种方法,得出的结论并不乐观。
  • 我认为目前最好的选择是只转发每个输入并明确地输出。如果您需要大量使用代码生成。

标签: angularjs angular typescript


【解决方案1】:

我认为这可以归结为一个没有 Angular2 的更基本的问题。当您有一个需要大量参数的函数时,每次要使用它时都必须指定所有这些参数,这既烦人又容易出错。当有一个根本不关心这些参数的中间函数时,问题会变得更糟——你发现自己向中间函数添加了参数,以便它可以将它传递给内部函数。年!

有几种模式可以处理这个问题。我最喜欢的是完全实例化内部函数并传递已经加载的实例,其中嵌入了以前的传递参数。我认为http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/ 是一篇关于如何在 Angular 2 中使用@ViewChild@ContentChild 做到这一点的好帖子。另一种策略是将所有传递参数包装在一个对象中,这样至少只有一个参数可以传递。当您想要添加更多参数时,这也很有帮助 - 因为它们已经被打包并以不透明方式传递,所以您的传递代码不需要更改。

【讨论】:

  • 在您发布答案之前,我已经获得了奖励积分。我同意函数的例子,但我无法想象这如何适用于这个问题。 third-party 组件有一组绑定属性,first-party 应该反映它们。有一个答案涉及ViewChild,但在我有机会检查之前它就被删除了。如果您知道应该如何实现,请随时用代码说明答案。
【解决方案2】:

您可以通过在子组件上使用 @Input() 来完成此操作。

http://plnkr.co/edit/9iyEsnyEPZ4hBmf2E0ri?p=preview

父组件:

import {Component} from '@angular/core';
import {ChildComponent} from './child.component';

@Component({
  selector: 'my-parent',
  directives: [ChildComponent],
  template: `
    <div>
      <h2>I am the parent.</h2>
      My name is {{firstName}} {{lastName}}.

        <my-child firstName="{{firstName}}" 
                  lastName="{{lastName}}">

        </my-child>

    </div>
  `
})
export class ParentComponent {
  public firstName:string;
  public lastName: string;
  constructor() {
    this.firstName = 'Bob';
    this.lastName = 'Smith';
  }
}

子组件:

import {Component, Input} from '@angular/core';

@Component({
  selector: 'my-child',
  template: `
    <div>
      <h3>I am the child.</h3>
      My name is {{firstName}} {{lastName}} Jr.
      <br/>
     The name I got from my parent was: {{firstName}} {{lastName}}

    </div>
  `
})
export class ChildComponent {
  @Input() firstName: string;
  @Input() lastName: string;
}

应用组件:

//our root app component
import {Component} from '@angular/core';
import {ParentComponent} from './parent.component';

@Component({
  selector: 'my-app',
  directives: [ParentComponent],
  template: `
    <div>
      <my-parent></my-parent>
    </div>
  `
})
export class App {

  constructor() {
  }
}

【讨论】:

  • 这是在原始代码中已经完成的。问题是如何避免在父模板中枚举子输入。
  • 你的意思是像使用对象而不是字符串?
  • 这个想法是避免在模板中使用 WET 代码,例如 &lt;my-child firstName="{{firstName}}" lastName="{{lastName}}"&gt; 并动态添加属性,就像它可以在 AngularJS 中通过 compile 函数完成一样。我希望问题能清楚地说明这一点。
【解决方案3】:

我不确定我是否做对了,但这是我的实现 (PLUNKER)


const FIRST_PARTY_OWN_INPUTS = ['not', 'passthrough'];
const FIRST_PARTY_PASSTHROUGH_INPUTS = ['all', 'attrs', 'are', 'passed'];

const generateAttributes(arr) {
   return arr.map(att => '[' + att + '] = "' + att + '"').join(' ');
}


//-------------------------------------------------------//////////////////
import {Component} from '@angular/core'

@Component({
  selector: 'third-party',
  inputs: [...FIRST_PARTY_PASSTHROUGH_INPUTS],
  template: `
<div>
  {{all}} , {{attrs}} ,  {{are}} ,  {{passed}}
</div>
  `
})
export class ThirdParty {
}

@Component({
  selector: 'first-party',
  inputs: [...FIRST_PARTY_OWN_INPUTS, ...FIRST_PARTY_PASSTHROUGH_INPUTS],
  template: `
<div>
  <div>
    {{not}} , {{passthrough}}
  </div>
  <third-party ${generateAttributes(FIRST_PARTY_PASSTHROUGH_INPUTS)}></third-party>
  <first-party-extra></first-party-extra>
</div>
  `,
  directives: [ThirdParty]
})
export class FirstParty {
}

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <first-party [not]="'not'" [passthrough]="'passthrough'"  
                   [all]="'all'" [attrs]="'attrs'" [are]="'are'" [passed]="'passed'">
      </first-party>
    </div>
  `,
  directives: [FirstParty]
})
export class App {
  constructor() {
    this.name = 'Angular2 (Release Candidate!)'
  }
}

希望对你有帮助:)

【讨论】:

  • 我猜是function generateAttributes。谢谢,我希望组件级的解决方案,但现在我想我会坚持这样的助手。仍然不确定离线模板编译是好是坏。
  • 很高兴我能帮上忙 :) 我还没有达到那个水平,所以。我想如果你在这里和那里有几乎相同的模板代码,只有一些更改,这应该很好,我自己使用它,但不确定是好是坏。
猜你喜欢
  • 2016-07-12
  • 1970-01-01
  • 2017-11-17
  • 2018-02-23
  • 2021-04-21
  • 2020-09-06
  • 1970-01-01
  • 2017-05-17
  • 1970-01-01
相关资源
最近更新 更多