gpt4 book ai didi

Angular 2 Material - overlay 和 portal 是如何工作的?

转载 作者:太空狗 更新时间:2023-10-29 17:15:47 24 4
gpt4 key购买 nike

我想制作一个自动完成组件,它向服务器发出请求并在屏幕上呈现接收到的值。我试图了解 portaloverlay 是如何工作的。现在这是我的自动完成组件

import {
Component, OnInit, Input, Output, EventEmitter, OnDestroy, ViewChild, ViewContainerRef,
ElementRef, Optional
} from '@angular/core';
import { ControlValueAccessor } from '@angular/forms';
import { MdOption, ConnectedOverlayDirective, Dir, transformPlaceholder, transformPanel, fadeInContent } from '@angular/material';

import { Subscription } from 'rxjs/Subscription';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subject } from 'rxjs/Subject';

import { AutocompleteConfiguration } from './autocomplete-config.model';
import { SearchState, SearchingService, IBackend } from './../../services/searching.service';
import { ISearchConfig } from './../../models/iSearch-config';
import { IHint } from './../generic-form/generic-form.service';
import { getValueAccessorProviders } from './../../models/custom-value-accessor.builder';

@Component({
selector: 'autocomplete',
templateUrl: './autocomplete.html',

providers: [
SearchingService,
getValueAccessorProviders(AutocompleteComponent)
],
animations: [
transformPlaceholder, transformPanel, fadeInContent
]
})
export class AutocompleteComponent implements OnInit, OnDestroy, ControlValueAccessor {
/** Placeholder to be shown if no value has been selected. */
@Input()
get placeholder() { return this._placeholder; }
set placeholder(value: string) {
this._placeholder = value;

// Must wait to record the trigger width to ensure placeholder width is included.
Promise.resolve(null).then(() => this._triggerWidth = this._getWidth());
}
@Input() hint: IHint = null;
@Input() iconPosition: string = ''; // 'prefix', 'suffix' or 'placeholder-prefix', 'placeholder-suffix'
@Input() autocompleteConfigurtation: AutocompleteConfiguration;
@Input() backend: IBackend;
@ViewChild(ConnectedOverlayDirective) overlayDir: ConnectedOverlayDirective;
/** Trigger that opens the select. */
@ViewChild('triggerRef', { read: ElementRef }) trigger: ElementRef;

@Output() blur: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();
@Output() focus: EventEmitter<FocusEvent> = new EventEmitter<FocusEvent>();

get value() {
return this._inputValue;
}
set value(value: any) {
if (String(value) !== String(this._inputValue)) {
this._inputValue = String(value);
}
}

/** Whether or not the overlay panel is open. */
private _panelOpen = false;
/** The currently selected option. */
private _selected: MdOption;
private _placeholder: string;
/**
* The width of the trigger. Must be saved to set the min width of the overlay panel
* and the width of the selected value.
*/
private _triggerWidth: number;

private _inputValue: string = '';
private _focused: boolean = false;
private _disabled: boolean = false;

private onSearchStateChange: BehaviorSubject<SearchState>;
private onModelChangeSubject: Subject<any> = new Subject<any>();
private subscriptions: Subscription[] = [];

private searchState: SearchState = null;

/**
* The x-offset of the overlay panel in relation to the trigger's top start corner.
* This must be adjusted to align the selected option text over the trigger text when
* the panel opens. Will change based on LTR or RTL text direction.
*/
_offsetX = 0;

/**
* The y-offset of the overlay panel in relation to the trigger's top start corner.
* This must be adjusted to align the selected option text over the trigger text.
* when the panel opens. Will change based on the y-position of the selected option.
*/
_offsetY = 0;

/** The value of the select panel's transform-origin property. */
_transformOrigin: string = 'top';

/** The animation state of the placeholder. */
_placeholderState = '';

/**
* This position config ensures that the top "start" corner of the overlay
* is aligned with with the top "start" of the origin by default (overlapping
* the trigger completely). If the panel cannot fit below the trigger, it
* will fall back to a position above the trigger.
*/
_positions = [
{
originX: 'start',
originY: 'top',
overlayX: 'start',
overlayY: 'top',
},
{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'bottom',
},
];
/** The scroll position of the overlay panel, calculated to center the selected option. */
private _scrollTop = 0;
constructor(
private searchBuilder: SearchingService,
@Optional() private _dir: Dir
) { }

ngOnInit() {
this.onSearchStateChange = this.searchBuilder
.createSearchObservable(this.onModelChangeSubject, this.autocompleteConfigurtation.searchConfig, this.backend);
this.setSearchStateChangeSubscription();
}

setSearchStateChangeSubscription() {
this.subscriptions.push(
this.onSearchStateChange.subscribe(newState => {
debugger
this.searchState = newState;
// this._calculateOverlayPosition();
this._placeholderState = this._isRtl() ? 'floating-rtl' : 'floating-ltr';
this._panelOpen = newState && newState.responseObject && newState.responseObject.length > 0;
})
);
}

onModelChange(inputValue) {
this.value = inputValue;
this._onChangeCallback(this._inputValue);
this._onTouchedCallback();
this._activateSearch(this.value);
}

// From ControlValueAccessor interface
// the ngModel init or form write value
writeValue(value: any) {
this.value = value;
}

// From ControlValueAccessor interface
registerOnChange(fn: any) {
this._onChangeCallback = fn;
}

// From ControlValueAccessor interface
registerOnTouched(fn: any) {
this._onTouchedCallback = fn;
}

setDisabledState(isDisabled: boolean) {
this._disabled = isDisabled;
}

close() {

}

ngOnDestroy() {
this.subscriptions.forEach(val => val.unsubscribe());
this.subscriptions = [];
this.onModelChangeSubject.unsubscribe();
}

/**
* Sets the scroll position of the scroll container. This must be called after
* the overlay pane is attached or the scroll container element will not yet be
* present in the DOM.
*/
_setScrollTop(): void {
const scrollContainer =
this.overlayDir.overlayRef.overlayElement.querySelector('.md-select-panel');
scrollContainer.scrollTop = this._scrollTop;
}
/** The width of the trigger element. This is necessary to match
* the overlay width to the trigger width.
*/
_getWidth(): number {
return this._getTriggerRect().width;
}

_isRtl(): boolean {
return this._dir ? this._dir.value === 'rtl' : false;
}

_onPanelDone($event) {
console.log($event);
}

private _getTriggerRect(): ClientRect {
return this.trigger.nativeElement.getBoundingClientRect();
}

private _activateSearch(value) {
if (this._disabled || !this.focus) {
return;
}

this.onModelChangeSubject.next(value);
}

private _handleFocus($event) {
this._focused = true;

if (this.autocompleteConfigurtation.activateOnFocus) {
this._activateSearch(this.value);
}
this.focus.emit($event);
}

private _handleBlur($event) {
this._focused = false;
this._onTouchedCallback();
this.blur.emit($event);
}

private _onChangeCallback(_: any) { }
private _onTouchedCallback() { }
}

这是html

<md-input type="text" 
#triggerRef
#origin="cdkOverlayOrigin"
cdk-overlay-origin
[disabled]="_disabled"
[(ngModel)]="value"
(blur)="_handleBlur($event)"
(focus)="_handleFocus($event)"
(ngModelChange)="onModelChange($event)">

<md-placeholder *ngIf="placeholder || iconPosition.indexOf('placeholder') !== -1">
<i *ngIf="iconPosition === 'placeholder-prefix'" class="material-icons app-input-icon">{{field.icon}}</i>
{{placeholder}}
<i *ngIf="iconPosition === 'placeholder-suffix'" class="material-icons app-input-icon">{{field.icon}}</i>
</md-placeholder>
<span md-prefix>
<md-icon *ngIf="iconPosition === 'prefix'">field.icon</md-icon>
</span>
<span md-suffix>
<md-icon *ngIf="iconPosition === 'suffix'">field.icon</md-icon>
</span>
<md-hint *ngIf="hint" [align]="hint.align">
{{hint.value}}
</md-hint>
</md-input>
<template
cdk-connected-overlay
hasBackdrop
backdropClass="cdk-overlay-transparent-backdrop"
[origin]="origin"
[open]="_panelOpen"
[positions]="_positions"
[minWidth]="_triggerWidth"
[offsetY]="_offsetY"
[offsetX]="_offsetX"
(backdropClick)="close()"
(attach)="_setScrollTop()">
<div class="md-select-panel"
[@transformPanel]="'showing'"
[style.transformOrigin]="_transformOrigin"
[class.md-select-panel-done-animating]="_panelDoneAnimating"
(@transformPanel.done)="_onPanelDone()"
(keydown)="log($event)">
<div class="md-select-content" [@fadeInContent]="'showing'">
<md-option *ngFor="let option of searchState?.responseObject"
[value]="option.value">
{{ option?.text }}
</md-option>
</div>
</div>
</template>

该组件可以正常工作,并按应有的方式呈现叠加层。覆盖容器呈现选项唯一的问题是宽度。我阅读了门户网站和叠加层的核心概念,但我想了解它是如何真正与 Angular 一起工作的。 Here is how it renders right now

谁能解释一下它是如何工作的?或者至少我如何控制它的宽度?

最佳答案

I managed to use the overlay component if anyone wants an example you can try to have a look here I will try to add comments about how I think it works. Also some documentation about it and how to use it will be more then welcomed from @angular/material. I think is very usable to create third party components which needs to be rendered in the dome tree to not have problems with overflow hidden or translate3d

cdk文档发布 cdk docs还有here是有用的叠加指南。

关于Angular 2 Material - overlay 和 portal 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41749269/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com