Я использую вложенные компоненты для создания общего меню, но получаю сообщение об ошибке

> EXCEPTION: Error: Uncaught (in promise): TypeError: Cannot read
> property 'subscribe' of undefined angular2.dev.js:23877 EXCEPTION:
> Error: Uncaught (in promise): TypeError: Cannot read property
> 'subscribe' of undefinedBrowserDomAdapter.logError @
> angular2.dev.js:23877BrowserDomAdapter.logGroup @
> angular2.dev.js:23888ExceptionHandler.call @
> angular2.dev.js:1317(anonymous function) @
> angular2.dev.js:12763schedulerFn @
> angular2.dev.js:13167SafeSubscriber.__tryOrUnsub @
> Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @
> Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @
> Rx.js:11191Subject._next @ Rx.js:11183Subject.next @
> Rx.js:11142EventEmitter.emit @
> angular2.dev.js:13148NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError
> @ angular2.dev.js:13566NgZoneImpl.inner.inner.fork.onHandleError @
> angular2.dev.js:2128ZoneDelegate.handleError @
> angular2-polyfills.js:336Zone.runGuarded @
> angular2-polyfills.js:244drainMicroTaskQueue @
> angular2-polyfills.js:495ZoneTask.invoke @ angular2-polyfills.js:434
> angular2.dev.js:23877 STACKTRACE:BrowserDomAdapter.logError @
> angular2.dev.js:23877ExceptionHandler.call @
> angular2.dev.js:1319(anonymous function) @
> angular2.dev.js:12763schedulerFn @
> angular2.dev.js:13167SafeSubscriber.__tryOrUnsub @
> Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @
> Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @
> Rx.js:11191Subject._next @ Rx.js:11183Subject.next @
> Rx.js:11142EventEmitter.emit @
> angular2.dev.js:13148NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError
> @ angular2.dev.js:13566NgZoneImpl.inner.inner.fork.onHandleError @
> angular2.dev.js:2128ZoneDelegate.handleError @
> angular2-polyfills.js:336Zone.runGuarded @
> angular2-polyfills.js:244drainMicroTaskQueue @
> angular2-polyfills.js:495ZoneTask.invoke @ angular2-polyfills.js:434
> angular2.dev.js:23877 Error: Uncaught (in promise): TypeError: Cannot
> read property 'subscribe' of undefined
>     at resolvePromise (angular2-polyfills.js:543)
>     at angular2-polyfills.js:520
>     at ZoneDelegate.invoke (angular2-polyfills.js:332)
>     at Object.NgZoneImpl.inner.inner.fork.onInvoke (angular2.dev.js:2111)
>     at ZoneDelegate.invoke (angular2-polyfills.js:331)
>     at Zone.run (angular2-polyfills.js:227)
>     at angular2-polyfills.js:576
>     at ZoneDelegate.invokeTask (angular2-polyfills.js:365)
>     at Object.NgZoneImpl.inner.inner.fork.onInvokeTask (angular2.dev.js:2103)
>     at ZoneDelegate.invokeTask (angular2-polyfills.js:364)BrowserDomAdapter.logError @
> angular2.dev.js:23877ExceptionHandler.call @
> angular2.dev.js:1320(anonymous function) @
> angular2.dev.js:12763schedulerFn @
> angular2.dev.js:13167SafeSubscriber.__tryOrUnsub @
> Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @
> Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @
> Rx.js:11191Subject._next @ Rx.js:11183Subject.next @
> Rx.js:11142EventEmitter.emit @
> angular2.dev.js:13148NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError
> @ angular2.dev.js:13566NgZoneImpl.inner.inner.fork.onHandleError @
> angular2.dev.js:2128ZoneDelegate.handleError @
> angular2-polyfills.js:336Zone.runGuarded @
> angular2-polyfills.js:244drainMicroTaskQueue @
> angular2-polyfills.js:495ZoneTask.invoke @ angular2-polyfills.js:434
> angular2-polyfills.js:469 Unhandled Promise rejection: Cannot read
> property 'subscribe' of undefined ; Zone: angular ; Task: Promise.then
> ; Value: TypeError: Cannot read property 'subscribe' of
> undefined(…)consoleError @
> angular2-polyfills.js:469drainMicroTaskQueue @
> angular2-polyfills.js:498ZoneTask.invoke @ angular2-polyfills.js:434
> angular2-polyfills.js:471 Error: Uncaught (in promise): TypeError:
> Cannot read property 'subscribe' of undefined(…)consoleError @
> angular2-polyfills.js:471drainMicroTaskQueue @
> angular2-polyfills.js:498ZoneTask.invoke @ angular2-polyfills.js:434 

это HTML, используемый для общего меню, и здесь я хочу иметь больше меню ang для управления меню, используя только импорт в виде дерева {Component, Input, Output, EventEmitter, OnInit} из 'angular2 / core'; импортировать {BaseMenuComponent} из './baseMenuComponent/baseMenuComponent';

 <div class="menu-tree" *ngIf="menuTreeView.length>0">
        <div class="col-md-4" *ngFor="#menu of menuTreeView; #i = index" >
            <base-menu 
                [menu-items-list]="menu"
                [menu-layer]="i"
                (select-menu-item)="selectMenuItem($event)"
                (add-new-item)="addMenuItem($event)">
            </base-menu>
        </div>
     </div>


@Component({
    selector: 'menu-component',
    templateUrl: '/app/components/menuComponent/menuTreeComponent.html',
    directives:[BaseMenuComponent]
}) 

export class MenuTreeComponent implements OnInit {
    @Input('menu-tree-data') menuDictionary;
    //menuDictionary;
    @Output('item-selected') selectItem:EventEmitter<Object>;
    @Output('add-new-item') broadcastNewItem: EventEmitter<Object>;
    ROOT_ID:number = 0; 
    ROOT_LAYER:number = 0;

    menuTreeView:Array<Object> = new Array<Object>();
    //TODO implement menuService
    constructor() {
        this.menuDictionary = new Array<Object>(
            {id:12, layer:0, name:'asd'},
            {id:13, layer:0, name:'asda'},
            {id:14, layer:0, name:'asdd'},
            {id:15, layer:1, parentId:13, name:'asds'},
            {id:16, layer:1, parentId:13, name:'asdg'},
            {id:17, layer:1, parentId:13, name:'asdxz'},
            {id:18, layer:1, parentId:14, name:'asd1e'},
            {id:19, layer:2, parentId:17, name:'asd1e1'},
            {id:20, layer:2, parentId:17, name:'asd1e2'},
            {id:21, layer:2, parentId:17, name:'asd1e3'});
    }

    ngOnInit(){
        this.selectItem = new EventEmitter<Object>();
        this.broadcastNewItem = new EventEmitter<Object>(); 
        this.menuDictionary = this.mapManuTree(this.menuDictionary);    
        this.menuTreeView.push(this.getRootLayer());
    }

    mapManuTree(menuTree){
        for (let i = 0; i < menuTree.length; i++) {
            menuTree[i].hasChildrens = this.checkIfMenuItemHasChildrens(menuTree[i], menuTree);
        }
    }

    checkIfMenuItemHasChildrens(menuItem, menuTree){
        for (let i = 0; i < menuTree.length; i++) {
            if(menuItem.id === menuTree[i].parentId){
                return true;
            }
        }
        return false;
    }

    selectMenuItem(menuItem){
        if(!menuItem.hasChildrens){
            this.selectItem.emit(menuItem);
            return;
        }
        this.menuTreeView = this.getTreeViewForMenuItem(menuItem);
    }

    getTreeViewForMenuItem(menuItem){
        let menuView = this.menuTreeView;
        var nextLayer = menuItem.layer + 1;

        menuView[nextLayer] = new Array<Object>();

        for (var i = 0; i < this.menuDictionary.length; i++) {
            if (this.menuDictionary[i].parentId <= menuItem.id){
                menuView[nextLayer].push(this.menuDictionary[i]);
            }
        }

        return menuView;
    }   

    getRootLayer(){
        let firstLayer = new Array<Object>();

        for (var i = 0; i < this.menuDictionary.length; i++) {          
            if(this.menuDictionary[i].layer === this.ROOT_LAYER)
            {
                firstLayer.push(this.menuDictionary[i]);
            } 
        }

        return firstLayer
    }
}

Вот общий список для базового меню

    import {Component, Input, Output, EventEmitter, OnInit} from 'angular2/core';

@Component({
    selector: 'base-menu',
    //templateUrl: '/app/components/menuComponent/baseMenuComponent/baseMenuComponent.html'
    template:`
    <div class="base-menu-component">
        <ul class="nav nav-pills nav-stacked">
            <li class="form-group row">
                <input class="form-control" [(ngModel)]="newMenuItem">
                <button class="btn btn-success" (click)="addMenuItem()">
                    <span class="glyphicon glyphicon-plus"></span>
                </button>
                <button class="btn btn-success" (click)="clearMenuItem()">
                    <span class="glyphicon glyphicon-remove"></span>
                </button>
            </li>
            <li *ngFor="#item of menuItems" [class]="isItemSelected(item) ? 'active':''" (click)="selectItem(item)">
                <a>{{item.name}}</a>
            </li>
        </ul>
    </div>
    `,
    //inputs:['menu-items-list',],
})

export class BaseMenuComponent implements OnInit {
    @Input('menu-items-list') menuItemsList:Array<MenuItem>;
    @Input('menu-layer') menuLayer:number;
    @Output('select-menu-item') broadcastMenuItem: EventEmitter<MenuData> = new EventEmitter<MenuData>();
    @Output('add-new-item') broadcastNewItem: EventEmitter<MenuData> = new EventEmitter<MenuData>();

    selectedItem:MenuItem;
    newMenuItem:string;

    constructor() { }

    ngOnInit(){
        this.selectedItem = this.menuItemsList && this.menuItemsList.length > 0 ? this.menuItemsList[0] : null;
        this.newMenuItem = "";
    }

    isItemSelected(menuItem){
        return this.selectedItem == menuItem;
    }

    selectItem(menuItem){
        this.selectedItem = menuItem;
        this.broadcastMenuItem.emit({menuItem:menuItem, menuLayer:this.menuLayer});
    }

    addMenuItem(){
        this.broadcastNewItem.emit({menuItem:this.newMenuItem, menuLayer:this.menuLayer});
        this.clearMenuItem();
    }

    clearMenuItem(){
        this.newMenuItem = "";
    }
}

interface MenuItem{
    id;
    layer;
    parentId;
    name;
}

interface MenuData{
    menuItem;
    menuLayer;
}

person Nicu    schedule 08.04.2016    source источник
comment
Было бы полезно, если бы вы могли загрузить небольшой образец и plnkr, или jsfiddle, или codepen.   -  person surfmuggle    schedule 08.04.2016
comment
вот код plunkr   -  person Nicu    schedule 08.04.2016
comment
это не работает, я имею в виду, что не инициализируется.   -  person micronyks    schedule 08.04.2016


Ответы (1)


Я думаю это

ngOnInit(){
    this.selectItem = new EventEmitter<Object>();
    this.broadcastNewItem = new EventEmitter<Object>(); 

вызывает ошибку.

Почему бы тебе не настроить их как

@Output('item-selected') selectItem:EventEmitter<Object> = new EventEmitter<Object>();
@Output('add-new-item') broadcastNewItem:EventEmitter<Object>  = new EventEmitter<Object>();

или в конструкторе

constructor(){
    this.selectItem = new EventEmitter<Object>();
    this.broadcastNewItem = new EventEmitter<Object>();
person Günter Zöchbauer    schedule 08.04.2016
comment
Большое спасибо, вы можете объяснить, почему эмиттер нужно инициализировать в конструкторе или когда вы его объявляете? - person Nicu; 08.04.2016
comment
Angular вызывает ngOnInit() после устранения привязок. @Output() предназначен для привязки от дочернего к родительскому. Поэтому он пытается подписаться на ваши выходы, но они еще не инициализированы и выкидывает. - person Günter Zöchbauer; 08.04.2016