Angular 1.5 组件 $postLink 在使用 templateUrl 时触发得太快

Angular 1.5 component $postLink gets triggered too soon in when using templateUrl

我跟随 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);
};
}

控制台输出:

但是,当我尝试在打字稿中执行相同操作时,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);
    }

}
}

模板相同

现在控制台输出为:

我是不是漏掉了什么?

好吧,您现在使用的是不同的方法。在您将它推入一个控制器中的数组之前。现在你有两个组件和控制器。

根据 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;

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.

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

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

我现在使用 $templateCache object of angular. The key is to preload all the templates when the angular app starts up. And then use the $templateCache.get method to fill in the template-property on the components. This post 引导我找到那个解决方案。