npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/pipes-call-function-in-template

v0.2.357-11

Published

> Pipe đa năng giúp gọi function xử lý logic ngay trong Angular template, hỗ trợ truyền tham số và trả về Observable (async).

Readme

@libs-ui/pipes-call-function-in-template

Pipe đa năng giúp gọi function xử lý logic ngay trong Angular template, hỗ trợ truyền tham số và trả về Observable (async).

Giới thiệu

LibsUiPipesCallFunctionInTemplatePipe cho phép gọi một arrow function tùy ý từ component ngay bên trong template HTML của Angular. Thay vì tạo nhiều Pipe riêng biệt cho từng nghiệp vụ, pipe này đóng vai trò là "bridge" truyền value, item, otherData vào function, nhận lại Observable<T> và kết hợp với async pipe để hiển thị kết quả.

Pipe tích hợp sẵn logic chuẩn hóa giá trị đầu ra: tự động xử lý null, undefined, chuỗi rỗng, số 0 và non-primitive (object, mảng) — không cần viết thêm guard trong function.

Tính năng

  • ✅ Gọi bất kỳ arrow function nào trực tiếp trong template mà không cần tạo Pipe riêng
  • ✅ Hỗ trợ truyền 2 tham số phụ: itemotherData
  • ✅ Trả về Observable<T> — kết hợp với async pipe hoặc @if (...; as val)
  • ✅ Tích hợp switchMap — tự hủy Observable cũ khi giá trị đầu vào thay đổi
  • ✅ Tự động chuẩn hóa giá trị rỗng: null / undefined / '' / NaN'__' (mặc định)
  • ✅ Tùy chỉnh giá trị mặc định cho 0 và empty qua tham số defaultValueEmpty
  • ✅ Non-primitive (object, mảng, Date) được giữ nguyên reference
  • ✅ Boolean được trả về nguyên dạng boolean, không bị stringify
  • ✅ Standalone pipe, Angular 16+

Khi nào sử dụng

  • Cần xử lý/biến đổi dữ liệu phức tạp để hiển thị nhưng không muốn tạo riêng một Pipe chỉ dùng 1 lần
  • Cần truyền thêm ngữ cảnh (item, otherData) vào hàm xử lý — ví dụ: tính tổng tiền từ giá × số lượng − giảm giá
  • Logic transform có thể tái sử dụng giữa nhiều hàng trong @for hoặc nhiều chỗ trong cùng component
  • Cần gọi một hàm async (mock API, lookup map) và hiển thị kết quả trong template

Cài đặt

npm install @libs-ui/pipes-call-function-in-template

Import

import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';

@Component({
  standalone: true,
  imports: [LibsUiPipesCallFunctionInTemplatePipe],
  // ...
})
export class MyComponent {}

Ví dụ sử dụng

1. Transform chuỗi đơn giản

import { Component } from '@angular/core';
import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';
import { Observable, of } from 'rxjs';
import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';

@Component({
  standalone: true,
  imports: [LibsUiPipesCallFunctionInTemplatePipe],
  template: `
    <span>{{ 'hello world' | LibsUiPipesCallFunctionInTemplatePipe : transformUppercase | async }}</span>
  `,
})
export class MyComponent {
  transformUppercase: TYPE_FUNCTION<string> = (data: { value: string }): Observable<string> => {
    return of((data.value || '').toUpperCase());
  };
}

Kết quả hiển thị: HELLO WORLD


2. Truyền tham số phụ itemotherData

import { Component } from '@angular/core';
import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';
import { Observable, of } from 'rxjs';
import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';

@Component({
  standalone: true,
  imports: [LibsUiPipesCallFunctionInTemplatePipe],
  template: `
    <!-- value=100, item=5 (số lượng), otherData=20 (giảm giá) -->
    <span>{{ 100 | LibsUiPipesCallFunctionInTemplatePipe : calculateTotal : 5 : 20 | async }}</span>
  `,
})
export class MyComponent {
  calculateTotal: TYPE_FUNCTION<string> = (data: {
    value: number;
    item?: number;
    otherData?: number;
  }): Observable<string> => {
    const qty = data.item || 0;
    const discount = data.otherData || 0;
    const total = data.value * qty - discount;
    return of(`${total.toFixed(0)} đ`);
  };
}

Kết quả hiển thị: 480 đ (100 × 5 − 20)


3. Dùng trong @for để render từng hàng

import { Component, signal } from '@angular/core';
import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';
import { Observable, of } from 'rxjs';
import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';

type T_product = { id: string; name: string; status: string };

@Component({
  standalone: true,
  imports: [LibsUiPipesCallFunctionInTemplatePipe],
  template: `
    @for (product of products(); track product.id) {
      <div>
        {{ product.name }}
        — {{ product.status | LibsUiPipesCallFunctionInTemplatePipe : formatStatus | async }}
      </div>
    }
  `,
})
export class MyComponent {
  protected products = signal<T_product[]>([
    { id: '1', name: 'Sản phẩm A', status: 'active' },
    { id: '2', name: 'Sản phẩm B', status: 'inactive' },
  ]);

  formatStatus: TYPE_FUNCTION<string> = (data: { value: string }): Observable<string> => {
    const map: Record<string, string> = {
      active: 'Đang kinh doanh',
      inactive: 'Ngừng kinh doanh',
    };
    return of(map[data.value] ?? data.value);
  };
}

4. Tùy chỉnh giá trị mặc định cho null0

<!-- null → 'Chưa có dữ liệu' thay vì '__' mặc định -->
{{ null | LibsUiPipesCallFunctionInTemplatePipe : undefined : undefined : undefined : { valueIsEmpty: 'Chưa có dữ liệu' } | async }}

<!-- 0 → 'Miễn phí' thay vì '0' mặc định -->
{{ 0 | LibsUiPipesCallFunctionInTemplatePipe : undefined : undefined : undefined : { valueIs0: 'Miễn phí' } | async }}

<!-- Không cần function, chỉ cần normalize giá trị hiển thị -->
{{ price() | LibsUiPipesCallFunctionInTemplatePipe : undefined : undefined : undefined : { valueIs0: 'Miễn phí', valueIsEmpty: 'Chưa cập nhật' } | async }}

5. Gọi hàm async (mock API, lookup bất đồng bộ)

import { Component } from '@angular/core';
import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';
import { Observable, of, delay } from 'rxjs';
import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';

@Component({
  standalone: true,
  imports: [LibsUiPipesCallFunctionInTemplatePipe],
  template: `
    @if ('active' | LibsUiPipesCallFunctionInTemplatePipe : fetchStatusLabel | async; as label) {
      <span class="status-badge">{{ label }}</span>
    } @else {
      <span>Đang tải...</span>
    }
  `,
})
export class MyComponent {
  fetchStatusLabel: TYPE_FUNCTION<string> = (data: { value: string }): Observable<string> => {
    const statusMap: Record<string, string> = {
      active: 'Đang hoạt động',
      inactive: 'Ngừng hoạt động',
      pending: 'Đang chờ duyệt',
    };
    return of(statusMap[data.value] || 'Không xác định').pipe(delay(500));
  };
}

6. Sử dụng standalone (gọi transform trực tiếp trong TypeScript)

import { TestBed } from '@angular/core/testing';
import { firstValueFrom, of } from 'rxjs';
import { LibsUiPipesCallFunctionInTemplatePipe } from '@libs-ui/pipes-call-function-in-template';

const pipe = TestBed.runInInjectionContext(
  () => new LibsUiPipesCallFunctionInTemplatePipe()
);

// Không truyền function — chỉ normalize giá trị
const result = await firstValueFrom(pipe.transform('hello'));
// result === 'hello'

const empty = await firstValueFrom(pipe.transform(null));
// empty === '__' (CHARACTER_DATA_EMPTY)

// Truyền function transform
const upper = await firstValueFrom(
  pipe.transform('world', (data) => of(data.value.toUpperCase()))
);
// upper === 'WORLD'

Transform

| Tham số | Type | Bắt buộc | Default | Mô tả | |---|---|---|---|---| | value | any | ✅ | — | Giá trị đầu vào cần xử lý hoặc hiển thị | | functionCall | TYPE_FUNCTION | ❌ | undefined | Arrow function nhận { value, item, otherData } và trả về Observable<T>. Nếu không truyền, pipe chỉ chuẩn hóa value | | item | any | ❌ | undefined | Tham số phụ thứ nhất — ví dụ: số lượng, row index, config object | | otherData | any | ❌ | undefined | Tham số phụ thứ hai — ví dụ: giảm giá, currency, extra config | | defaultValueEmpty | { valueIs0?: any; valueIsEmpty?: any } | ❌ | undefined | Ghi đè giá trị mặc định khi output là 0 hoặc falsy/null/undefined |

Cú pháp trong template:

{{ value | LibsUiPipesCallFunctionInTemplatePipe : functionCall : item : otherData : defaultValueEmpty | async }}

Kết quả trả về — quy tắc chuẩn hóa:

| Giá trị output | Kết quả | |---|---| | null / undefined / '' / NaN | defaultValueEmpty.valueIsEmpty nếu có, hoặc '__' | | 0 (số nguyên, float) | defaultValueEmpty.valueIs0 nếu có, hoặc '0' | | Số khác 0 | Chuỗi stringify: '42', '-1', '3.14' | | boolean | Giữ nguyên true / false (không stringify) | | Non-primitive (object, mảng, Date, function) | Giữ nguyên reference |

Types & Interfaces

import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';
/**
 * Type cho function xử lý logic trong pipe.
 * Nhận object chứa value, item, otherData — trả về Observable<T>.
 */
export type TYPE_FUNCTION<T = any> = (
  data: { value: any; item?: any; otherData?: any }
) => Observable<T>;

Ví dụ khai báo có type cụ thể:

import { Observable, of } from 'rxjs';
import { TYPE_FUNCTION } from '@libs-ui/interfaces-types';

// Typed rõ ràng — T = string
formatCurrency: TYPE_FUNCTION<string> = (data: {
  value: number;
  item?: string; // currency code
}): Observable<string> => {
  const currency = data.item ?? 'VND';
  return of(`${data.value.toLocaleString()} ${currency}`);
};

Lưu ý quan trọng

⚠️ Bắt buộc dùng async pipe: Pipe này luôn trả về Observable. Bắt buộc kết hợp với | async trong template hoặc dùng @if (... | async; as val) để unwrap giá trị. Thiếu async sẽ hiển thị object Observable thay vì giá trị thực.

⚠️ Function phải là arrow function (class property): functionCall phải là class property kiểu arrow function, không được là method thông thường. Method thông thường sẽ mất this context khi được truyền qua pipe.

// ✅ ĐÚNG — class property arrow function
formatLabel: TYPE_FUNCTION<string> = (data) => of(this.translate(data.value));

// ❌ SAI — method thông thường, mất `this`
formatLabel(data: { value: string }): Observable<string> {
  return of(this.translate(data.value)); // this là undefined khi pipe gọi
}

⚠️ SwitchMap hủy Observable cũ: Pipe dùng switchMap nội bộ. Khi value thay đổi, Observable từ lần gọi trước bị hủy ngay lập tức. Đây là hành vi đúng cho hầu hết use case, nhưng cần chú ý nếu function trả về Observable thực hiện side-effect.

⚠️ bigint 0n không kích hoạt valueIs0: defaultValueEmpty.valueIs0 chỉ áp dụng cho value === 0 (số JS tiêu chuẩn). 0n (BigInt) được xử lý như falsy primitive và rơi vào nhánh valueIsEmpty.

⚠️ Symbol gây TypeError: Truyền Symbol vào pipe (dù là value hay kết quả trả về từ function) sẽ ném TypeError khi pipe cố stringify. Không dùng Symbol với pipe này.