• UI组件
  • 前端

【免费下载链接】ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)

项目地址: https://gitcode.com/gh_mirrors/ng/ngx-bootstrap
点击查看 免费下载

导读

在 ngx-bootstrap 中,progressbar 组件允许通过属性绑定逐条设置 max、animate、striped 与 type,但当项目需要统一风格的进度条时,逐条配置既繁琐又容易遗漏。本文围绕官方文档 progressbar.examples.config-default.use-case.md 描述的 "Configuring defaults" 示例,讲解如何通过依赖注入覆盖 ProgressbarConfig,实现全局默认值配置,并深入源码剖析其工作原理、适用场景与注意事项,帮助你在项目中一次性定制所有进度条的默认外观与行为。

一、场景概述:Configuring defaults 示例要解决什么问题

1.1 用例文档描述的目标

官方 spec 文档 progressbar.examples.config-default.use-case.md 定义了这个演示用例(适用于 BS version 3&4 的 DEMO 页面):

  • Primary Actor(主要参与者):User
  • Scope(作用范围):Ngx-bootstrap DEMO,Bootstrap 版本 3 与 4
  • Goal(目标):向用户展示如何配置 progressbar 的默认值

其主成功场景(Main success scenario)为:

  1. 用户打开 Progressbar 演示页面;
  2. 用户点击 Configuring defaults 子菜单(变体场景:用户滚动到该子菜单);
  3. 用户看到 1 个具有特定 value 和 type 的进度条(具体样式见 Template 源码);
  4. 组件源码应通过 ProgressbarConfig 编写。

也就是说,这个示例的核心教学点不是某个进度条的属性绑定,而是 "如何通过 ProgressbarConfig 服务为整个 progressbar 组件提供一套全局默认配置"。

1.2 与其它 progressbar 示例的定位差异

在文档目录 docs/spec/progressbar 下,与该用例并列的还有 static、dynamic、stacked 三个用例:

  • Static:静态绑定 value、type 等属性;
  • Dynamic:通过组件逻辑动态改变 value 与 type;
  • Stacked:使用 BarValue[] 数组实现多段堆叠进度条;
  • Configuring defaults:不逐条传属性,而是通过注入覆盖全局默认配置。

四者互补:前三者演示"单次使用"层面的配置,而本用例演示"全局约定"层面的配置,适用于项目中需要统一进度条风格的场景。

二、核心配置类:ProgressbarConfig 源码解析

配置默认值的落点是一个可注入的服务类 ProgressbarConfig,源码位于 src/progressbar/progressbar.config.ts:

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

@Injectable({
  providedIn: 'root'
})
export class ProgressbarConfig {
  /** if `true` changing value of progress bar will be animated */
  animate = false;
  /** If `true`, striped classes are applied */
  striped = false;
  /** maximum total value of progress element */
  max = 100;
}

该类由三个可配置属性组成,含义与默认值如下:

属性类型默认值说明
animatebooleanfalse为 true 时进度条值变化带动画(对应 .progress-bar-animated)
stripedbooleanfalse为 true 时应用条纹样式类(对应 .progress-bar-striped)
maxnumber100进度条的总量上限,用于计算百分比宽度

它被声明为 providedIn: 'root' 的 root 级服务,因此默认情况下在整个应用共享同一个实例;在组件/模块层面通过 providers 覆盖时,则会生成局部实例(详见下文第三节)。注意:type(success/info/warning/danger)不在全局默认配置范围内,它属于每个进度条实例的语义属性,需按组件单独指定或交给 BarComponent 的内置默认值 'info'(见 src/progressbar/bar.component.ts)。

三、全局默认值如何生效:依赖注入链路剖析

3.1 配置值进入组件的入口

ProgressbarConfig 的默认值通过组件输入属性的初始值进入组件。查看 src/progressbar/progressbar.component.ts:

export class ProgressbarComponent {
  /** maximum total value of progress element */
  max = input<number>(this._config.max);

  /** if `true` changing value of progress bar will be animated */
  animate = input<boolean>(this._config.animate);

  /** If `true`, striped classes are applied */
  striped = input<boolean>(this._config.striped);

  /** provide one of the four supported contextual classes: `success`, `info`, `warning`, `danger` */
  type = input<ProgressbarType | undefined>();

  /** current value of progress bar. Could be a number or array of objects
   * like {"value":15,"type":"info","label":"15 %"}
   */
  value = input<number | BarValue[]>(0);

  isStacked = computed(() => Array.isArray(this.value()));
  ...
  constructor(private _config: ProgressbarConfig) {
  }
}

关键点在于:

  • max、animate、striped 三个输入使用 this._config.xxx 作为默认值(Angular 17+ 的 input() 信号式 API);
  • 组件构造时注入 ProgressbarConfig(constructor(private _config: ProgressbarConfig));
  • 因此,只要在合适的注入器层级覆盖了 ProgressbarConfig,未显式绑定的 max/animate/striped 就会自动取到覆盖后的值;
  • 而显式绑定(如 [max]="200")的优先级始终高于全局默认值,属于"默认值兜底、显式绑定优先"的机制。

3.2 演示组件中的覆盖方式

官方 DEMO 的组件实现位于 libs/doc-pages/progressbar/src/lib/demos/config/config.ts:

import { Component } from '@angular/core';
import { ProgressbarConfig } from 'ngx-bootstrap/progressbar';

// such override allows to keep some initial values

export function getProgressbarConfig(): ProgressbarConfig {
  return Object.assign(new ProgressbarConfig(), { animate: true, striped: true,  max: 150 });
}

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: 'demo-progressbar-config',
  templateUrl: './config.html',
  providers: [{ provide: ProgressbarConfig, useFactory: getProgressbarConfig }],
  standalone: false
})
export class DemoProgressbarConfigComponent {}

对应的模板 libs/doc-pages/progressbar/src/lib/demos/config/config.html:

<div class="mb-2">
  <progressbar class="progress-striped active"  [value]="136" type="danger"><i>136 / 150</i></progressbar>
</div>

这份代码体现了三层含义:

  1. 保留初始值的覆盖模式:工厂函数 getProgressbarConfig() 先 new ProgressbarConfig()(得到默认值 animate: false, striped: false, max: 100),再用 Object.assign 覆盖为 { animate: true, striped: true, max: 150 }。注释 "such override allows to keep some initial values" 明确说明:这种写法能保留类中的初始值,避免逐项手写全部字段——即便将来 ProgressbarConfig 新增字段,默认值也不会丢失。
  2. 局部注入覆盖:providers: [{ provide: ProgressbarConfig, useFactory: getProgressbarConfig }] 在组件注入器层级提供了新的 ProgressbarConfig 实例。根据 Angular 依赖注入的层级解析规则,该组件树内的 progressbar 会优先解析到这个局部实例,从而不影响应用其它区域的进度条。
  3. 模板只传个别属性:模板中仅显式绑定了 [value]="136" 和 type="danger",未绑定 max/animate/striped——它们全部取自覆盖后的全局默认值(max=150、动画、条纹),最终渲染出"136 / 150"、danger 红色、带动画和条纹的进度条,与 spec 文档"看到 1 个具有特定 value 和 type 的进度条"的验收条件一致。

3.3 从 spec 到实现的对应关系

对照用例文档与 DEMO 源码,可得到如下映射:

spec 文档中的步骤对应实现
用户点击 Configuring defaults 子菜单在 libs/doc-pages/progressbar/src/lib/progressbar-section.list.ts 中注册为 title: 'Configuring defaults', anchor: 'bs3-config' 的 demo 条目
组件源码通过 ProgressbarConfig 编写config.ts 中的 providers 与 useFactory
展示 1 个特定 value 和 type 的进度条config.html 中 [value]="136"、type="danger" 及文字标签 <i>136 / 150</i>

四、渲染与计算原理:默认值如何变成可见样式

4.1 外层 progressbar 到内层 bar 的传递

progressbar 组件本身不直接渲染视觉元素,它通过模板把配置转发给内部的 bar 子组件。见 src/progressbar/progressbar.component.html:

@if (!isStacked()) {
  <bar [type]="type()" [value]="_value()" [max]="max()" [animate]="animate()" [striped]="striped()">
    <ng-content></ng-content>
  </bar>
} @else {
  @for (item of _values(); track item) {
    <bar
    [type]="item.type" [value]="item.value" [max]="item.max || max()" [animate]="animate()" [striped]="striped()">{{ item.label }}</bar>
  }
}

单值模式下,max()、animate()、striped() 直接来自组件输入(其默认值即 ProgressbarConfig);value 为数组时进入堆叠模式(Stacked),每个 bar 还可通过 item.max 覆盖最大上限。

4.2 bar 的百分比计算与类型样式

真正负责"算宽度、加样式"的是 BarComponent,见 src/progressbar/bar.component.ts:

export class BarComponent {
  /** maximum total value of progress element */
  max = input<number>(100);

  /** current value of progress bar */
  value = input<number | undefined>(0);

  /** if `true` changing value of progress bar will be animated */
  animate = input<boolean | undefined>(false);

  /** If `true`, striped classes are applied */
  striped = input<boolean | undefined>(false);

  /** provide one of the four supported contextual classes: `success`, `info`, `warning`, `danger` */
  type = input<ProgressbarType | undefined>('info');

  percent = 100;
  ...
  constructor(
    private el: ElementRef,
    private renderer: Renderer2
  ) {
    // Watch for value and max changes to update percent
    effect(() => {
      const currentValue = this.value() ?? 0;
      const currentMax = this.max() || 100;
      this.percent = 100 * (Number(currentValue) / Number(currentMax));
    });

    // Watch for type changes to update classes
    effect(() => {
      const currentType = this.type();
      this.applyTypeClasses(currentType);
    });
  }
}

核心逻辑:

  • 百分比公式:percent = 100 * value / max。以示例 value=136, max=150 计算,约为 90.67%,进度条宽度按此百分比填充;
  • 宿主绑定:'[style.width.%]': 'percent' 将百分比映射为宽度,'[class.progress-bar-animated]': 'animate()'、'[class.progress-bar-striped]': 'striped()' 控制动画与条纹类(见 bar.component.ts);
  • 类型样式:applyTypeClasses() 依据 ProgressbarType(定义于 src/progressbar/progressbar-type.interface.ts)动态增删 progress-bar-{type} 与 bg-{type} 类,切换类型时会先移除旧类型类再添加新类型类。

4.3 测试对默认值行为的验证

仓库单元测试 src/progressbar/testing/progressbar.component.spec.ts 印证了上述行为:

  • 默认值测试:<progressbar></progressbar> 渲染后,外层元素包含 progress 类且 max 属性为 '100'(对应 ProgressbarConfig.max = 100);
  • 百分比测试:[max]="100" [value]="60" 时 bar 的 style.width 为 '60%';
  • 动态切换测试:将 max 从 100 改为 200、value 从 75 改为 100 后,宽度从 '75%' 变为 '50%',类型类从 progress-bar-warning 变为 progress-bar-danger;
  • 类型绑定测试:切换 type 时 progress-bar-striped 等其它类名不会被覆盖,且 bg-{type} 类同步增删。

这些用例说明:无论默认值来自何处(ProgressbarConfig 或属性绑定),最终都收敛到同一套"百分比计算 + 类型类管理"逻辑,行为一致、可预测。

五、实战:在真实项目中配置 progressbar 全局默认值

5.1 安装与引入

通过 Angular CLI 示意图安装并引入 progressbar 模块(官方 DEMO 的安装提示见 progressbar-section.list.ts 中的 importInfo 字段):

ng add ngx-bootstrap --component progressbar

或在代码中手动引入。组件已支持 standalone,见 src/progressbar/progressbar.component.ts;也提供模块方式聚合导出,见 src/progressbar/progressbar.module.ts:

// 模块方式
import { NgModule } from '@angular/core';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';

@NgModule({
  imports: [ProgressbarModule],
})
export class AppModule {}

5.2 为整个应用定制默认值

如果想全局生效(所有进度条默认 max=150、带动画、带条纹),在根模块或根组件的 providers 中覆盖:

import { ProgressbarConfig } from 'ngx-bootstrap/progressbar';

export function getProgressbarConfig(): ProgressbarConfig {
  return Object.assign(new ProgressbarConfig(), {
    animate: true,
    striped: true,
    max: 150
  });
}

// 在 AppModule 的 providers 中:
providers: [
  { provide: ProgressbarConfig, useFactory: getProgressbarConfig }
]

5.3 为局部模块/组件定制默认值

若只需某个功能模块内的进度条统一风格,把同样的 provider 放进该模块或组件的 providers 即可;Angular 注入器按层级就近解析,外层应用配置不受影响。这也是官方 DEMO 组件 config.ts 的做法。

5.4 使用建议与优先级规则

  • 优先级:显式属性绑定 > 局部 ProgressbarConfig > 全局 ProgressbarConfig > 类内建默认值。即"默认值兜底、显式绑定优先";
  • 保留初始值:推荐 Object.assign(new ProgressbarConfig(), {...}) 的覆盖写法,而不是直接手写一个对象字面量——后者会丢失 ProgressbarConfig 未来的新增字段,导致隐性行为差异;
  • type 不参与全局默认:type 属于实例语义,需要时在模板上绑定(如 type="danger"),或依赖 BarComponent 的默认值 'info';
  • 值超过 max 的表现:示例中 value=136 < max=150,处于正常范围;若 value 超过 max,按百分比公式计算出的宽度将超过 100%,渲染效果取决于外层容器,因此合理设置 max 是全局配置的关键。

六、小结

"Configuring defaults" 示例展示了 ngx-bootstrap progressbar 一种优雅的全局配置机制:通过覆盖可注入的 ProgressbarConfig(animate、striped、max 三个默认值),配合 Angular 依赖注入的层级解析,即可在不改动任何模板绑定的前提下统一项目进度条风格;useFactory + Object.assign 的写法既保留了初始值,又避免逐项手写。从源码链路看,配置值经 ProgressbarComponent 的输入默认值进入组件,转发给 BarComponent 完成百分比计算与样式类管理,整个过程有 单元测试 覆盖,行为可预期、可验证。想要继续深入,可对比阅读同一目录下的 static 示例、dynamic 示例 与 stacked 示例,以完整掌握 progressbar 的三种配置维度。

  • UI组件
  • 前端

【免费下载链接】ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)

项目地址: https://gitcode.com/gh_mirrors/ng/ngx-bootstrap
点击查看 免费下载
Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐