【问题标题】:Angular 1.5 component $postLink gets triggered too soon in when using templateUrlAngular 1.5 组件 $postLink 在使用 templateUrl 时过早触发
【发布时间】:2017-05-24 22:51:12
【问题描述】:

我关注this post 了解了 Angular 的 1.5 组件 postLink 事件。

我在plunker 中得到了这个工作。下面是标签组件的代码:

controller: function () {
this.$onInit = function () {
  console.log("$onInit");
  this.tabs = [];
};
this.addTab = function addTab(tab) {
  console.log("addTab");
  this.tabs.push(tab);
};
this.selectTab = function selectTab(index) {
  for (var i = 0; i < this.tabs.length; i++) {
    this.tabs[i].selected = false;
  }
  this.tabs[index].selected = true;
};
this.$postLink = function () {
  console.log("$postLink. nr of tabs added: " + this.tabs.length);

  this.selectTab(this.selected);
};
}

控制台输出:

  • $onInit
  • 添加标签
  • 添加标签
  • 添加标签
  • $postLink。添加的标签数量:3

但是,当我尝试在 typescript 中执行相同操作时,postLink 事件会过早触发。它在标签可以添加到标签组件之前被触发。

以下是部分代码: /tabs/tab/tab.component.ts

namespace MainApp {
const mainApp = angular.module("mainApp");

class TabComponent implements ng.IComponentOptions {
    public templateUrl: string | ng.Injectable<(...args: any[]) => string>;
    public controller: any;
    public controllerAs: string;
    public transclude: boolean;
    public bindings: any;
    public require: any;

    constructor() {
        this.templateUrl = ["rootUrl", (rootUrl) => rootUrl +  "app/uitrijregelingBerekening/tabs/tab/tab.html"];
        this.controller = TabController;
        this.transclude = true;
        this.bindings = {
            label: "@",
        };
        this.require = {
            tabs: "^^",
        };
    }
}

mainApp.component("tab", new TabComponent());

}

/tabs/tab/tab.controller.ts

namespace MainApp {
interface ITabBindings {
    label: string;
}

export class TabController implements ITabBindings {
    public label: string;
    private tabs: TabsController;

    public tab: any;

    constructor() {
    }

    public $onInit() {
        this.tab = {
            label: this.label,
            selected: false
        };
        this.tabs.addTab(this.tab);
    }
}
}

/tabs/tabs.component.ts

namespace MainApp {
const mainApp = angular.module("mainApp");

class TabsComponent implements ng.IComponentOptions{
    public templateUrl: string | ng.Injectable<(...args: any[]) => string>;
    public controller: any;
    public controllerAs: string;
    public bindings: any;
    public transclude: boolean;

    constructor() {
        this.templateUrl = ["rootUrl", (rootUrl) => rootUrl +  "app/uitrijregelingBerekening/tabs/tabs.html"];
        this.controller = TabsController;
        this.bindings = {
            selected:"@",
        };
        this.transclude = true;
    }
}

mainApp.component("tabs", new TabsComponent());

}

/tabs/tabs.controller.ts

namespace MainApp {
export interface ITabsBindings {
    selected: number;
}

export class TabsController implements ITabsBindings {
    public selected: number;
    public tabs: Array<any>;

    private scope: any;

    static $inject = ["$scope"];
    constructor($scope: ng.IScope) {
        this.scope = $scope;

    }

    public $onInit() {
        console.log("$onInit");
        this.tabs = new Array<any>();
    }

    public addTab(tab: any) {
        console.log("addTab");

        this.tabs.push(tab);
    }

    public selectTab(index: number) {
        for (var i = 0; i < this.tabs.length; i++) {
            this.tabs[i].selected = false;
        }
            this.tabs[index].selected = true;
    }

    public $postLink() {
        console.log("$postLink. nr of tabs added: " + this.tabs.length);

        this.selectTab(this.selected);
    }

}
}

模板是一样的。

现在控制台输出是:

  • $onInit
  • $postLink。添加的标签数量:0
  • angular.js:13920 TypeError: 无法设置未定义的“选定”属性
  • 添加标签
  • 添加标签
  • 添加标签

我错过了什么吗?

【问题讨论】:

    标签: angularjs components lifecycle


    【解决方案1】:

    嗯,您现在使用的是不同的方法。在您将其推入一个控制器中的阵列之前。现在您有两个组件和控制器。

    根据 Typescript 文档,这是您的问题。

        /**
         * Called after this controller's element and its children have been linked. Similar to the post-link function this
         * hook can be used to set up DOM event handlers and do direct DOM manipulation. Note that child elements that contain
         * templateUrl directives will not have been compiled and linked since they are waiting for their template to load
         * asynchronously and their own compilation and linking has been suspended until that occurs. This hook can be considered
         * analogous to the ngAfterViewInit and ngAfterContentInit hooks in Angular 2. Since the compilation process is rather
         * different in Angular 1 there is no direct mapping and care should be taken when upgrading.
         */
        $postLink?(): void;
    

    请注意,包含 templateUrl 指令的子元素不会 已经编译和链接,因为他们正在等待他们的 模板异步加载和他们自己的编译和链接 在此之前已被暂停。

    您应该将tabs 数组绑定到孩子,而不是使用require

    【讨论】:

    • 在 javascript 代码中,我还使用了 2 个组件和控制器。设置基本相同。我使用 require 是因为我希望能够在选项卡中嵌入自定义内容。但是关于带有 templateUrl 的子元素被异步加载的评论就是这里的答案!事实上,如果我切换到文字模板,它就可以了!
    【解决方案2】:

    @kuhnroyal 的答案是正确的。但我想发布后续内容,因为它可能对其他有同样问题的人有用。我设法找到了一个解决方案,它允许我在单独的文件中使用模板(这提高了可维护性),但仍然使用模板属性来保证 postLink 事件的正确顺序。

    我现在使用 angular 的 $templateCache 对象。关键是在 Angular 应用启动时预加载所有模板。然后使用$templateCache.get方法填写组件上的template-property。这个post 引导我找到了那个解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-23
      • 2016-09-05
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      • 2016-11-09
      • 2017-11-01
      相关资源
      最近更新 更多