简体   繁体   中英

Multiple Template Reference Variables - Angular 2 and TypeScript

I am trying to call a function in one component that emits some data to a function in another component, completes some operation and emits the data back to the original component. These components are siblings. My issue is that Angular doesn't seem to like multiple reference variables in one html file saying that one of my reference variables is undefined. Any ideas?

HTML

<docked-component-picker #compPicker (onComponentRelease)="grid.getMousePosition($event)" [(model)]="model" *ngIf="model.pageState == 1"></docked-component-picker>
<grid #grid (onGetReleaseBack)="compPicker.setNewComponentPosition($event)" [(model)]="model.layout" [editable]="model.pageState" [currentResponsiveRestriction]="model.currentResponsiveRestriction"></grid>

docked-component-picker.ts

@Component({
    selector: 'docked-component-picker',
    templateUrl: './docked-component-picker.component.html',
    providers: [ComponentService]
})

export class DockedComponentPickerComponent {
@Input()
private model: Page;

@Output()
private onComponentRelease: EventEmitter<any> = new EventEmitter();

private components;

private currComponent;

private dragging: boolean;

private parentWidth;

constructor(private _componentService: ComponentService, private _componentFactoryResolver: ComponentFactoryResolver, private _elementRef: ElementRef) {
    let promise = this._componentService.getComponents().then(response => {
        this.components = JSON.parse(response._body);
    });
}

@HostListener('window:mouseup', ['$event'])
onMouseup(event) {
    console.log(event);
    if (this.dragging) {
        this.dragging = false;

        this.onComponentRelease.emit(event);
    }
}

public setNewComponentPosition(event) {
    let pc = new PageComponent({
        component: this.currComponent.template,
        componentId: this.currComponent.id,
        pageId: this.model.id,
        responsiveRestrictions: [
            new ResponsiveRestriction({ sizeType: 0, positionX: event.x, positionY: event.y }),
            new ResponsiveRestriction({ sizeType: 1, positionX: event.x, positionY: event.y }),
            new ResponsiveRestriction({ sizeType: 2, positionX: event.x, positionY: event.y }),
        ],
    });
    this.model.layout.addPageComponent(pc, this.model.currentResponsiveRestriction);
}

/**
* Converts the given container's raw pixel position to grid co-ordinates
 * @param source - the container to convert
*/
private convertToGridPosition(x, y): Position {
    return new Position(Math.round(x / (this.parentWidth / this.model.layout.width)), Math.round(x / this.model.layout.rowHeight));
}

public createComponent(component): void {
    this.currComponent = component;

    this.dragging = true;
}

Needed parts of grid.component.ts

@Output()
    private onGetReleaseBack: EventEmitter<Position> = new EventEmitter();

public getMousePosition(event) {
    let pos = new Position(Math.round(event.offsetX / (this.parentWidth / this.model.width)), Math.round(event.offsetY / this.model.rowHeight));
    this.onGetReleaseBack.emit(pos);
}

Error

ERROR TypeError: Cannot read property 'setNewComponentPosition' of undefined at Object.eval [as handleEvent] (PageComponent.html:4) at handleEvent

docked-component-picker and grid both live inside the same component. You are trying to catch an event from the grid emitter function "onGetReleaseBack" to execute some logic inside DockedComponentPickerComponent.

If my understanding is correct, here is what I suggest : in your component DockedComponentPickerComponent replace the function setNewComponentPosition by an @Input :

@Input() set componentPosition(event){
    if(event){
        //setNewComponentPosition  logic here
    }        
}

Then you will need to do some other adjustments for this to work. in the parent template, replace your

<grid #grid 
      (onGetReleaseBack)="setNewComponentPosition($event)" 
      [(model)]="model.layout" 
      [editable]="model.pageState" 
      [currentResponsiveRestriction]="model.currentResponsiveRestriction">
</grid>

In the parent component, you need to create a new function called setNewComponentPosition which will update a class property "componentPosition" that you also need to declare

setNewComponentPosition(newPosition){
    this.componentPosition = newPosition;
}

Finally in the parent template, you need to update the docked-component-picker... to include the newly created property like so :

<docked-component-picker #compPicker 
     [componentPosition]="componentPosition"
     (onComponentRelease)="grid.getMousePosition($event)" 
     [(model)]="model" 
     *ngIf="model.pageState == 1"></docked-component-picker>

The problem is because of using ngIf and #compPicker together. When the view initializes the compPicker is not yet present it seems, so the variable will be undefined.

One solution is to add ViewChild inside your parent component to access the child component which is using ngIf :

@ViewChild('compPicker') compPicker: ElementRef;

Another solution could be to use [hidden]="model.pageState != 1" instead of ngIf .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM