Skip to content

defaultIfEmpty - Valor por Defecto si el Flujo está Vacío

El operador defaultIfEmpty es un operador que emite un valor por defecto especificado si Observable se completa sin emitir ningún valor. Se usa para lidiar con arrays vacíos o resultados de API vacíos.

🔰 Sintaxis Básica y Operación

ts
import { from } from 'rxjs';
import { defaultIfEmpty } from 'rxjs';

from([]).pipe(
  defaultIfEmpty('Sin valor')
).subscribe(console.log);

// Salida:
// Sin valor

En este ejemplo, defaultIfEmpty emitirá 'Sin valor' para un array vacío convertido en Observable con from.

🌐 RxJS Official Documentation - defaultIfEmpty

💡 Ejemplos de Uso Típicos

  • Si el usuario no ingresó ninguna información
  • Cuando la API devuelve un resultado vacío
  • Si ninguno de los valores satisface las condiciones

Esto se usa para completar la situación "no se devolvió nada" en casos como estos.

ts
import { of, EMPTY } from 'rxjs';
import { defaultIfEmpty, delay } from 'rxjs';

function mockApiCall(hasData: boolean) {
  return hasData
    ? of(['A', 'B', 'C']).pipe(delay(500))
    : EMPTY.pipe(delay(500));
}

mockApiCall(false)
  .pipe(defaultIfEmpty('Sin datos'))
  .subscribe(console.log);

// Salida:
// Sin datos

🧪 Ejemplos de Código Prácticos (con UI)

✅ 1. Usado para Determinar si un Array está Vacío

ts
import { from } from 'rxjs';
import { defaultIfEmpty } from 'rxjs';

// Construir UI
const container = document.createElement('div');
container.innerHTML = '<h3>Ejemplo del operador defaultIfEmpty:</h3>';
document.body.appendChild(container);

const emptyBtn = document.createElement('button');
emptyBtn.textContent = 'Procesar array vacío';
container.appendChild(emptyBtn);

const nonEmptyBtn = document.createElement('button');
nonEmptyBtn.textContent = 'Procesar array no vacío';
container.appendChild(nonEmptyBtn);

const result = document.createElement('div');
result.style.marginTop = '10px';
result.style.padding = '10px';
result.style.border = '1px solid #ccc';
container.appendChild(result);

emptyBtn.addEventListener('click', () => {
  result.textContent = 'Procesando...';
  from([]).pipe(
    defaultIfEmpty('Sin datos')
  ).subscribe(value => {
    result.textContent = `Resultado: ${value}`;
  });
});

nonEmptyBtn.addEventListener('click', () => {
  result.textContent = 'Procesando...';
  from([1, 2, 3]).pipe(
    defaultIfEmpty('Sin datos')
  ).subscribe(value => {
    result.textContent = `Resultado: ${value}`;
  });
});

✅ 2. Completar Valor por Defecto para Resultado Vacío en API

ts
import { of, EMPTY } from 'rxjs';
import { defaultIfEmpty, delay } from 'rxjs';

function mockApiCall(hasData: boolean) {
  return hasData
    ? of([
        { id: 1, name: 'Artículo 1' },
        { id: 2, name: 'Artículo 2' },
      ]).pipe(delay(1000))
    : EMPTY.pipe(delay(1000));
}

const apiContainer = document.createElement('div');
apiContainer.innerHTML = '<h3>Procesamiento de resultado de API con defaultIfEmpty:</h3>';
document.body.appendChild(apiContainer);

const dataBtn = document.createElement('button');
dataBtn.textContent = 'Con datos';
dataBtn.style.marginRight = '10px';
apiContainer.appendChild(dataBtn);

const emptyBtn2 = document.createElement('button');
emptyBtn2.textContent = 'Sin datos';
apiContainer.appendChild(emptyBtn2);

const output = document.createElement('div');
output.style.marginTop = '10px';
output.style.padding = '10px';
output.style.border = '1px solid #ccc';
apiContainer.appendChild(output);

dataBtn.addEventListener('click', () => {
  output.textContent = 'Recuperando...';
  mockApiCall(true)
    .pipe(defaultIfEmpty('No se encontraron datos'))
    .subscribe({
      next: (val) => {
        if (Array.isArray(val)) {
          const ul = document.createElement('ul');
          val.forEach((item) => {
            const li = document.createElement('li');
            li.textContent = `${item.id}: ${item.name}`;
            ul.appendChild(li);
          });
          output.innerHTML = '<h4>Resultado:</h4>';
          output.appendChild(ul);
        } else {
          output.textContent = val;
        }
      },
    });
});

emptyBtn2.addEventListener('click', () => {
  output.textContent = 'Recuperando...';
  mockApiCall(false)
    .pipe(defaultIfEmpty('No se encontraron datos'))
    .subscribe({
      next: (val) => {
        output.textContent = val.toString();
      },
    });
});

Publicado bajo licencia CC-BY-4.0.