Angular组件(一) 分割面板ShrinkSplitter

这篇具有很好参考价值的文章主要介绍了Angular组件(一) 分割面板ShrinkSplitter。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Angular组件(一) 分割面板ShrinkSplitter

前言

分割面板在日常开发中经常使用,可将一片区域,分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件,用angular实现该功能,支持拖拽和[(ngModel)]双向绑定的方式控制区域的展示收起和拖拽功能。

module.ts

import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { TlShrinkSplitterComponent } from "./shrink-splitter.component";
import{NzToolTipModule} from "ng-zorro-antd/tooltip"

const COMMENT = [TlShrinkSplitterComponent];

@NgModule({
  declarations: [...COMMENT],
  exports: [...COMMENT],
  imports: [
    CommonModule,
    NzToolTipModule,
  ]
})
export class TlShrinkSplitterModule {}

component.ts

import { AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, ContentChildren, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, QueryList, TemplateRef, ViewChild } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { TlTemplateDirective } from "topdsm-lib/common"
import { isFalsy } from "topdsm-lib/core/util";
import { off, on } from "./util";

@Component({
    selector: "tl-shrink-splitter",
    templateUrl: "./shrink-splitter.component.html",
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => TlShrinkSplitterComponent),
            multi: true
        }
    ],
    host: {
        class: "tl-shrink-splitter",
        '[class.expand]': 'tlExpand',
        '[class.contract]': '!tlExpand',
        '[class.contract-left]': 'tlColsedMode === "left"',
        '[class.contract-right]': 'tlColsedMode === "right"',
        '[class.contract-top]': 'tlColsedMode === "top"',
        '[class.contract-bottom]': 'tlColsedMode === "bottom"',
        '[style.z-index]': 'tlZIndex',
    }
})
export class TlShrinkSplitterComponent implements OnInit, AfterContentInit, AfterViewInit, ControlValueAccessor {

    prefix = "tl-shrink-splitter"
    offset = 0
    oldOffset: number | string = 0
    isMoving = false
    initOffset = 0

    _value: number | string = 0.5
    isOpen = true

    @Input()
    tlZIndex = 10

    // @Input()
    // tlMode: "horizontal" | "vertical" = "horizontal"

    /** 是否展示收起icon */
    @Input()
    tlShowExpandIcon = true

    /** 收起容器模式,上下左右哪一个容器应用收起展开的状态 */
    @Input()
    tlColsedMode: "left" | "right" | "top" | "bottom" = "left"

    @Input()
    tlMin = "40px"

    @Input()
    tlMax = "40px"

    @Input()
    tlExpandTooltipContent = ""

    @Input()
    tlContractTooltipContent = ""

    get value() {
        return this._value
    }

    set value(val: number | string) {
        this._value = val
        this.onChange(val)
        this.computeOffset()
    }

    expandValueCache: string | number = 0

    /** 展开状态 */
    get tlExpand() {
        return this.isOpen;
    }

    @Input()
    set tlExpand(val: boolean) {
        if (val !== this.isOpen) {
            this.isOpen = val;
            this.tlExpandChange.emit(val);
            this.changeExpand(val)
        }
    }

    /** 容器展开状态切换 */
    changeExpand(status: boolean) {
        if (!status) {
            // 收起
            this.expandValueCache = this.value
            if (this.tlColsedMode === "left") {
                this.value = 0
            } else if (this.tlColsedMode === "right") {
                this.value = 1
            } else if (this.tlColsedMode === "top") {
                this.value = 0
            } else if (this.tlColsedMode === "bottom") {
                this.value = 1
            }
        } else {
            // 展开
            this.value = this.expandValueCache
            this.expandValueCache = 0
        }
    }

    /** 展开收缩切换事件 */
    @Output() readonly tlExpandChange = new EventEmitter<boolean>();

    @Output() readonly onMoveStart = new EventEmitter();
    @Output() readonly onMoving = new EventEmitter<MouseEvent>();
    @Output() readonly onMoveEnd = new EventEmitter();

    expandChange(e: MouseEvent) {
        e.stopPropagation();
        e.preventDefault()
        this.tlExpand = !this.isOpen
    }


    @ContentChildren(TlTemplateDirective)
    templates?: QueryList<TlTemplateDirective>

    leftTemplate?: TemplateRef<void> | null = null

    rightTemplate?: TemplateRef<void> | null = null

    topTemplate?: TemplateRef<void> | null = null

    bottomTemplate?: TemplateRef<void> | null = null

    @ViewChild('outerWrapper')
    outerWrapper: ElementRef;


    get isHorizontal() {
        //return this.tlMode === 'horizontal';
        return this.tlColsedMode === "left" || this.tlColsedMode === "right"
    }
    get computedMin() {
        return this.getComputedThresholdValue('tlMin');
    }
    get computedMax() {
        return this.getComputedThresholdValue('tlMax');
    }
    get anotherOffset() {
        return 100 - this.offset;
    }

    get valueIsPx() {
        return typeof this.value === 'string';
    }
    get offsetSize() {
        return this.isHorizontal ? 'offsetWidth' : 'offsetHeight';
    }

    get paneClasses() {
        let classes = {}
        classes[`${this.prefix}-pane`] = true
        classes[`${this.prefix}-pane-moving`] = this.isMoving
        return classes
    }

    /** 展开收起触发器icon */
    get triggrrClass() {
        let classes = {}
        if (this.tlColsedMode === "left" && this.isOpen) {
            classes["icon-caret-left"] = true
        } else if (this.tlColsedMode === "left" && !this.isOpen) {
            classes["icon-caret-right"] = true
        } else if (this.tlColsedMode === "right" && this.isOpen) {
            classes["icon-caret-right"] = true
        } else if (this.tlColsedMode === "right" && !this.isOpen) {
            classes["icon-caret-left"] = true
        } else if (this.tlColsedMode === "top" && this.isOpen) {
            classes["icon-caret-left"] = true
        } else if (this.tlColsedMode === "top" && !this.isOpen) {
            classes["icon-caret-right"] = true
        } else if (this.tlColsedMode === "bottom" && this.isOpen) {
            classes["icon-caret-right"] = true
        } else if (this.tlColsedMode === "bottom" && !this.isOpen) {
            classes["icon-caret-left"] = true
        }
        return classes
    }

    get tooltipPosition() {
        let position = "right"
        if (this.tlColsedMode === "right" && !this.isOpen) {
            position = "left"
        }
        return position
    }

    get tooltipContent() {
        let tooltip = ""
        if (this.tlColsedMode === "left" && this.isOpen) {
            tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起左侧内容" : this.tlExpandTooltipContent
        } else if (this.tlColsedMode === "left" && !this.isOpen) {
            tooltip = isFalsy(this.tlContractTooltipContent) ? "展开左侧内容" : this.tlContractTooltipContent
        } else if (this.tlColsedMode === "right" && this.isOpen) {
            tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起右侧内容" : this.tlExpandTooltipContent
        } else if (this.tlColsedMode === "right" && !this.isOpen) {
            tooltip = isFalsy(this.tlContractTooltipContent) ? "展开右侧内容" : this.tlContractTooltipContent
        } else if (this.tlColsedMode === "top" && this.isOpen) {
            tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起顶部内容" : this.tlExpandTooltipContent
        } else if (this.tlColsedMode === "top" && !this.isOpen) {
            tooltip = isFalsy(this.tlContractTooltipContent) ? "展开顶部内容" : this.tlContractTooltipContent
        } else if (this.tlColsedMode === "bottom" && this.isOpen) {
            tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起底部内容" : this.tlExpandTooltipContent
        } else if (this.tlColsedMode === "bottom" && !this.isOpen) {
            tooltip = isFalsy(this.tlContractTooltipContent) ? "展开底部内容" : this.tlContractTooltipContent
        }
        return tooltip
    }

    px2percent(numerator: string | number, denominator: string | number) {
        return parseFloat(numerator + "") / parseFloat(denominator + "");
    }


    computeOffset() {
        this.offset = (this.valueIsPx ? this.px2percent(this.value as string, this.outerWrapper.nativeElement[this.offsetSize]) : this.value) as number * 10000 / 100
    }

    getComputedThresholdValue(type) {
        let size = this.outerWrapper.nativeElement[this.offsetSize];
        if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type];
        else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type];
    }
    getMin(value1, value2) {
        if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;
        else return Math.min(value1, value2);
    }
    getMax(value1, value2) {
        if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
        else return Math.max(value1, value2);
    }

    getAnotherOffset(value) {
        let res: string | number = 0;
        if (this.valueIsPx) res = `${this.outerWrapper.nativeElement[this.offsetSize] - parseFloat(value)}px`;
        else res = 1 - value;
        return res;
    }

    handleMove = (e) => {
        let pageOffset = this.isHorizontal ? e.pageX : e.pageY;
        let offset = pageOffset - this.initOffset;
        let outerWidth = this.outerWrapper.nativeElement[this.offsetSize];
        let value: string | number = ""
        if (this.valueIsPx) {
            value = `${parseFloat(this.oldOffset as string) + offset}px`
        } else {
            value = this.px2percent(outerWidth * (this.oldOffset as number) + offset, outerWidth)
        }
        let anotherValue = this.getAnotherOffset(value);
        if (parseFloat(value + "") <= parseFloat(this.computedMin + "")) value = this.getMax(value, this.computedMin);
        if (parseFloat(anotherValue + "") <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));
        e.atMin = this.value === this.computedMin;
        e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : (this.getAnotherOffset(this.value) as number).toFixed(5) === this.computedMax.toFixed(5);
        this.value = value
        this.onMoving.emit(e)
    }

    handleUp = (e) => {
        this.isMoving = false;
        off(document, 'mousemove', this.handleMove);
        off(document, 'mouseup', this.handleUp);
        this.onMoveEnd.emit()
    }

    onTriggerMouseDown(e) {
        this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
        this.oldOffset = this.value;
        this.isMoving = true;
        on(document, 'mousemove', this.handleMove);
        on(document, 'mouseup', this.handleUp);
        this.onMoveStart.emit()
    }

    constructor(private cdr: ChangeDetectorRef) { }

    ngOnInit(): void {
        console.log("ngOnInit");
    }

    ngAfterViewInit(): void {
        console.log("ngAfterViewInit");
        this.computeOffset()
    }

    ngAfterContentInit() {
        this.templates?.forEach((item) => {
            switch (item.getType()) {
                case 'left':
                    this.leftTemplate = item.template;
                    break;
                case 'right':
                    this.rightTemplate = item.template;
                    break;
                case 'top':
                    this.topTemplate = item.template;
                    break;
                case 'bottom':
                    this.bottomTemplate = item.template;
                    break;
                default:
                    this.leftTemplate = item.template;
                    break;
            }
        });

    }

    // 输入框数据变化时
    onChange: (value: any) => void = () => null;
    onTouched: () => void = () => null;

    writeValue(val: number | string): void {
        if (val !== this.value) {
            this.value = val
            this.computeOffset();
            this.cdr.markForCheck();
        }
    }
    // UI界面值发生更改,调用注册的回调函数
    registerOnChange(fn: any): void {
        this.onChange = fn;
    }

    // 在blur(等失效事件),调用注册的回调函数
    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }

    // 设置禁用状态
    setDisabledState?(isDisabled: boolean): void {

    }
}

TlTemplateDirective指令实现

import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
import { NzSafeAny } from "topdsm-lib/core/types";

@Directive({
  selector: '[tlTemplate]'
})
export class TlTemplateDirective {

  @Input('tlTemplate')
  name: string = "default"

  // @Input()
  // type: string = ""

  constructor(private viewContainer: ViewContainerRef, public template: TemplateRef<NzSafeAny>) {
    //this.template = templateRef;
  }
  ngOnInit(): void {
    this.viewContainer.createEmbeddedView(this.template)
  }
  getType() {
    return this.name;
  }
}

事件绑定、解绑

export const on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }
})();

export const off = (function() {
    if (document.removeEventListener) {
        return function(element, event, handler) {
            if (element && event) {
                element.removeEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event) {
                element.detachEvent('on' + event, handler);
            }
        };
    }
})();

component.html

<div [ngClass]="prefix + '-wrapper'" #outerWrapper>
    <div [ngClass]="prefix + '-horizontal'" *ngIf="isHorizontal; else verticalSlot">
        <div class="left-pane" [ngStyle]="{right: anotherOffset + '%'}" [ngClass]="paneClasses">
            <ng-container *ngTemplateOutlet="leftTemplate"></ng-container>
        </div>
        <div [ngClass]="prefix + '-trigger-con'" [ngStyle]="{left: offset + '%'}" (mousedown)="onTriggerMouseDown($event)">
            <div ngClass="tl-shrink-splitter-trigger tl-shrink-splitter-trigger-vertical" >
                <!-- <span class="tl-shrink-splitter-trigger-bar-con vertical" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" [tTooltip]="tooltipContent" [tooltipPosition]="tooltipPosition" *ngIf="tlShowExpandIcon"></span> -->
                <span class="tl-shrink-splitter-trigger-bar-con vertical" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" nz-tooltip [nzTooltipTitle]="tooltipContent" [nzTooltipPlacement]="tooltipPosition" *ngIf="tlShowExpandIcon"></span>
            </div>
        </div>
        <div class="right-pane" [ngStyle]="{left: offset + '%'}" [ngClass]="paneClasses">
            <ng-container *ngTemplateOutlet="rightTemplate"></ng-container>
        </div>
    </div>
    
    <ng-template #verticalSlot>
        <div [ngClass]="prefix + '-vertical'" >
            <div class="top-pane" [ngStyle]="{bottom: anotherOffset + '%'}" [ngClass]="paneClasses">
                <ng-container *ngTemplateOutlet="topTemplate"></ng-container>
            </div>
            <div [ngClass]="prefix + '-trigger-con'" [ngStyle]="{top: offset + '%'}" (mousedown)="onTriggerMouseDown($event)">
                <div ngClass="tl-shrink-splitter-trigger tl-shrink-splitter-trigger-horizontal" >
                    <!-- <span class="tl-shrink-splitter-trigger-bar-con horizontal" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" [tTooltip]="tooltipContent" [tooltipPosition]="tooltipPosition" *ngIf="tlShowExpandIcon"></span> -->
                    <span class="tl-shrink-splitter-trigger-bar-con horizontal" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" nz-tooltip [nzTooltipTitle]="tooltipContent" [nzTooltipPlacement]="tooltipPosition" *ngIf="tlShowExpandIcon"></span>
                </div>
            </div>
            <div class="bottom-pane" [ngStyle]="{top: offset + '%'}" [ngClass]="paneClasses">
                <ng-container *ngTemplateOutlet="bottomTemplate"></ng-container>
            </div>
        </div>
    </ng-template>
</div>

component.less

@split-prefix-cls: ~"tl-shrink-splitter";
@trigger-bar-background: rgba(23, 35, 61, 0.25);
@trigger-background: #f8f8f9;
@trigger-width: 8px;
@trigger-bar-width: 4px;
@trigger-bar-offset: (@trigger-width - @trigger-bar-width) / 2;
@trigger-bar-interval: 3px;
@trigger-bar-weight: 1px;
@trigger-bar-con-height: 20px;
.tl-shrink-splitter{
    position: relative;
    height: 100%;
    width: 100%;
}
.tl-shrink-splitter-wrapper{
    position: relative;
    height: 100%;
    width: 100%;
}

.@{split-prefix-cls}{
    background-color: #fff;
    border: 1px solid #dee2e6;
    &-pane{
        position: absolute;
        transition: all .3s ease-in;
        padding: 8px;

        &.tl-shrink-splitter-pane-moving{
            transition: none;
        }
        &.left-pane, &.right-pane {
            top: 0;
            bottom: 0;
        }
        &.left-pane {
            left: 0;
        }
        &.right-pane {
            right: 0;
            padding-left: 16px;
        }
        &.top-pane, &.bottom-pane {
            left: 0;
            right: 0;
        }
        &.top-pane {
            top: 0;
        }
        &.bottom-pane {
            bottom: 0;
            padding-top: 16px;
        }
        &-moving{
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
    }

    &-trigger{
        border: 1px solid #dcdee2;
        &-con {
            position: absolute;
            transform: translate(-50%, -50%);
            z-index: 10;
        }
        &-bar-con {
            position: absolute;
            overflow: hidden;
            &:hover{
                color: #000 !important;
            }
            &.vertical {
                top: 50%;
                left: -6px;
                width: 20px;
                height: @trigger-bar-con-height;
                background-color: #fff;
                border: 1px solid #ccc;
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                color: #b2b2b2;
                font-size: 14px;
                cursor: pointer;
            }
            &.horizontal {
                left: 50%;
                top: -4px;
                width: @trigger-bar-con-height;
                height: 20px;
                //transform: translate(-50%, 0);
                background-color: #fff;
                border: 1px solid #ccc;
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                color: #b2b2b2;
                font-size: 14px;
                cursor: pointer;
            }
        }
        &-vertical {
            width: @trigger-width;
            height: 100%;
            background: @trigger-background;
            border-top: none;
            border-bottom: none;
            cursor: col-resize;
            .@{split-prefix-cls}-trigger-bar {
                width: @trigger-bar-width;
                height: 1px;
                background: @trigger-bar-background;
                float: left;
                margin-top: @trigger-bar-interval;
            }
        }
        &-horizontal {
            height: @trigger-width;
            width: 100%;
            background: @trigger-background;
            border-left: none;
            border-right: none;
            cursor: row-resize;
            .@{split-prefix-cls}-trigger-bar {
                height: @trigger-bar-width;
                width: 1px;
                background: @trigger-bar-background;
                float: left;
                margin-right: @trigger-bar-interval;
            }
        }
    }

    &-horizontal {
        .@{split-prefix-cls}-trigger-con {
            top: 50%;
            height: 100%;
            width: 0;
        }
    }
    &-vertical {
        .@{split-prefix-cls}-trigger-con {
            left: 50%;
            height: 0;
            width: 100%;
        }
    }
}


.tl-shrink-splitter.contract{
    .tl-shrink-splitter-trigger-vertical{
        width: 0;
        padding-left: 0;
    }
    .tl-shrink-splitter-trigger-horizontal{
        height: 0;
        padding-top: 0;
    }
    .tl-shrink-splitter-trigger{
        border: 0;
    }

    &.contract-left{
        .tl-shrink-splitter-pane.left-pane{
            width: 0;
            padding: 0;
            overflow: hidden;
        }
        .right-pane{
            padding-left: 8px;
        }
    }

    .tl-shrink-splitter-trigger-bar-con{
        &.vertical{
            left: -6px;
        }
    }
    
    &.contract-right{
        .tl-shrink-splitter-trigger-bar-con{
            &.vertical{
                left: -16px;
            }
        }
    }

    &.contract-top{
        .tl-shrink-splitter-pane.top-pane{
            overflow: hidden;
            height: 0;
            padding: 0;
        }
        .bottom-pane{
            padding-top: 8px;
        }
        .tl-shrink-splitter-trigger-bar-con.horizontal{
            transform: rotate(90deg);
        }
    }

    &.contract-bottom{
        .tl-shrink-splitter-trigger-bar-con{
            &.horizontal{
                top: -16px;
            }
        }
        .tl-shrink-splitter-pane.bottom-pane{
            overflow: hidden;
            height: 0;
            padding: 0;
        }
        .top-pane{
            padding-top: 8px;
        }
        .tl-shrink-splitter-trigger-bar-con.horizontal{
            transform: rotate(90deg);
        }
    }
}
.tl-shrink-splitter.expand{
    .tl-shrink-splitter-trigger-bar-con{
        &.vertical{
            left: -8px;
        }
    }

    &.contract-top{
        .tl-shrink-splitter-trigger-bar-con.horizontal{
            transform: rotate(90deg);
        }
    }

    &.contract-bottom{
        .tl-shrink-splitter-trigger-bar-con.horizontal{
            transform: rotate(90deg);
        }
    }
}

页面效果

左右容器和上下容器
Angular组件(一) 分割面板ShrinkSplitter,angular.js,javascript,前端

demo

import { Component } from '@angular/core';

@Component({
  selector: 'tl-demo-shrink-splitter-basic',
  template: `
  <button tButton type="button" label="切换伸缩状态" class="ui-plusWidth ui-button-primary" style="margin-right: 8px" (click)="expandChange()"></button>
    <div class="split-box">
      <tl-shrink-splitter [(tlExpand)]="expand" [(ngModel)]="value" (onMoving)="triggerMoveHnadle($event)">
        <ng-template tlTemplate="left">
          <div>左侧区域自定义</div>
        </ng-template>
        <ng-template tlTemplate="right">
          <div>右侧区域自定义</div>
        </ng-template>
      </tl-shrink-splitter>  
    </div>
  `,
  styles: [
    `
      .split-box{
        height: 200px;
        display: flex;
        position: relative;
        overflow: hidden;
      }
      .split-right{
        margin-left: 10px;
        border: 1px solid #e3e3e3;
        flex:1;
      }
    `
  ]
})
export class TlDemoShrinkSplitterBasicComponent {

  expand = true

  value = 0.3

  expandChange(){
    this.expand = !this.expand
  }

  triggerMoveHnadle(e){
    console.log(e);
    console.log(this.value);  
  }
}

Angular组件(一) 分割面板ShrinkSplitter,angular.js,javascript,前端

Angular组件(一) 分割面板ShrinkSplitter,angular.js,javascript,前端文章来源地址https://www.toymoban.com/news/detail-821391.html

到了这里,关于Angular组件(一) 分割面板ShrinkSplitter的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 2.Angular组件概述

    组件是 Angular 应用的主要构造块。每个组件包括如下部分: 一个 HTML 模板,用于声明页面要渲染的内容 一个用于定义行为的 TypeScript 类 一个 CSS 选择器,用于定义组件在模板中的使用方式 要应用在模板上的 CSS 样式(可选) 创建组件 Angular CLI 是创建组件的最简单的途径,我

    2024年02月20日
    浏览(31)
  • Angular单元测试组件

    文章目录 前言 一、 单元测试是什么? 二、配置jasmine karma 三、技术点 1. 变更监测detectChanges 2. 模拟异步fakeAsync 3. Spy 四、单元测试基础结构 1. describe 2. beforeEachafterEach 3. it 4. expect 4.1 断言方法 5. configureTestingModule 6. compileComponents 7. createComponent 8. ComponentFixture 8.1 创建固件

    2024年02月13日
    浏览(32)
  • Angular中的组件

    组件简介 Angular中的组件,是一个使用 @component()装饰器 装饰的特殊类,同时在这个装饰器中指定 元数据 ,元数据包括 组件选择器 、 组件模板 、 组件样式 等。 组件是angular模块化的一个基本的组成元素。日常开发中,页面通常就是由一个或者多个组件堆叠而成。 组件的元

    2023年04月08日
    浏览(31)
  • Angular 独立组件入门

    如果你正在学习 Angular,那么你可能已经听说过独立组件(Component)。顾名思义,独立组件就是可以独立使用和管理的组件,它们能够被包含在其他组件中或被其他组件引用。 在本文中,我们将学习如何创建简单的独立组件以及如何在 Angular 应用程序中使用它们。 创建组件

    2024年02月13日
    浏览(31)
  • Angular独立组件简单体验

    Angular 14一项令人兴奋的特性就是Angular的独立组件终于来了。 在Angular 14中, 开发者可以尝试使用独立组件开发各种组件,但是值得注意的是Angular独立组件的API仍然没有稳定下,将来可能存在一些破坏性更新,所以不推荐在生产环境中使用。 对于已有的组件,我们可以在 @

    2024年01月20日
    浏览(36)
  • angular实现全局组件

    之前我们实现全局组件的第一种方式。我们是在定义了组件的时候通过在declares:[component],然后exports出该组件。最后在页面中每次导入该组件,而这次我们将采用另一种方式来实现 1 新建公用组件: 2 新建一个share.module.ts,在该module中引入我们所有的公共组件,本例中只有一个

    2024年02月12日
    浏览(42)
  • Angular系列教程之组件

    在Angular中,组件是构建Web应用程序的核心单元。它们允许我们将UI划分为独立且可重用的部分,并通过数据绑定和事件处理等机制来实现交互性。本文将介绍Angular组件的基本概念,并说明组件和指令的关系。 组件是一个由HTML模板、样式和逻辑代码组成的独立单元。它可以看

    2024年01月17日
    浏览(36)
  • Angular组件生命周期详解

    当 Angular 实例化组件类 并渲染组件视图及其子视图时,组件实例的生命周期就开始了。生命周期一直伴随着变更检测,Angular 会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就结束了。当 Ang

    2024年02月05日
    浏览(43)
  • angular——子组件如何接收父组件的动态传值

    开发过程中,父组件给子组件传值的情况很常见,今天我们就来聊聊父组件给子组件传值可能会发生哪些意外,什么情况下子组件无法接收到父组件最新的传值; 基本数据类型 :父组件给子组件传递 基本数据类型 ,子组件使用变量接收传值;当传值发生变化,子组件接收的

    2024年02月14日
    浏览(40)
  • Angular React Vue 比较 – 组件篇之内置组件

    本篇文章是组件篇的最后一篇,我们将探讨一下三大框架本身的内置组件。 在 Angular 中并没有内置组件,它只有一些内置指令。 虽然 Angular 官方把指令也称为一种特殊的组件,不过我们还是把指令在另外的篇章中讨论,就不在组件篇里讨论了。 React 提供了一些内置的组件,

    2024年03月16日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包