All Projects → joanllenas → Ngx Remotedata

joanllenas / Ngx Remotedata

Licence: bsd-3-clause
💥 Slaying a UI Antipattern with Angular

Programming Languages

typescript
32286 projects
elm
856 projects

Projects that are alternatives of or similar to Ngx Remotedata

ngrx-signalr-core
A library to handle realtime SignalR (.NET Core) events using @angular, rxjs and the @ngrx library
Stars: ✭ 18 (-28%)
Mutual labels:  ngrx
Angular Ngrx Nx Realworld Example App
Exemplary real world application built with Angular 11, NgRx 10, nrwl/nx 11
Stars: ✭ 411 (+1544%)
Mutual labels:  ngrx
Redux Beacon
Analytics integration for Redux and ngrx/store
Stars: ✭ 645 (+2480%)
Mutual labels:  ngrx
ngrx-demo-apps
Demo to share module (ngrx state and reducers) between Ionic and Angular apps
Stars: ✭ 39 (+56%)
Mutual labels:  ngrx
Ngrx Actions
⚡️ Actions and Reducer Utilities for NGRX
Stars: ✭ 348 (+1292%)
Mutual labels:  ngrx
Soundcloud Ngrx
SoundCloud API client with Angular • RxJS • ngrx/store • ngrx/effects
Stars: ✭ 438 (+1652%)
Mutual labels:  ngrx
Keyist-Ecommerce
🔑 A simple ecommerce site powered with Spring Boot + Angular 10 + Ngrx + OAuth2
Stars: ✭ 220 (+780%)
Mutual labels:  ngrx
Angularfire
The official Angular library for Firebase.
Stars: ✭ 7,029 (+28016%)
Mutual labels:  ngrx
Yatrum
Yatrum - Share and Discover Travel Stories, Itineraries, Travel Guides built with ♥️ using Angular 4.0.0
Stars: ✭ 352 (+1308%)
Mutual labels:  ngrx
Angular Education
A list of helpful material to develop using Angular
Stars: ✭ 5,451 (+21704%)
Mutual labels:  ngrx
Store
🚀 NGXS - State Management for Angular
Stars: ✭ 3,191 (+12664%)
Mutual labels:  ngrx
Ngrx Forms
Enhance your forms in Angular applications with the power of ngrx
Stars: ✭ 319 (+1176%)
Mutual labels:  ngrx
Angular Contacts App Example
Full Stack Angular PWA example app with NgRx & NestJS
Stars: ✭ 570 (+2180%)
Mutual labels:  ngrx
ngx-redux-ui-management-recipes
Recipes for managing the UI layout of an application using Redux in Angular
Stars: ✭ 39 (+56%)
Mutual labels:  ngrx
Universal
Seed project for Angular Universal apps featuring Server-Side Rendering (SSR), Webpack, CLI scaffolding, dev/prod modes, AoT compilation, HMR, SCSS compilation, lazy loading, config, cache, i18n, SEO, and TSLint/codelyzer
Stars: ✭ 669 (+2576%)
Mutual labels:  ngrx
nativescript-task-app
An example task management NativeScript Angular app. Demonstrates ngrx effects, lazy-loading modules, offline storage and app branding.
Stars: ✭ 26 (+4%)
Mutual labels:  ngrx
Store
RxJS powered state management for Angular applications, inspired by Redux
Stars: ✭ 3,959 (+15736%)
Mutual labels:  ngrx
Angular Github Issues
This repository searches github for repositories, issues, and users. It is written in Angular 10 and is using ngrx/store for state.
Stars: ✭ 18 (-28%)
Mutual labels:  ngrx
Echoes Player
Echoes Player: the missing Media Player experience for Youtube - Built with Angular (9), ngrx (9), Angular CLI, Boostrap (SASS), Youtube api
Stars: ✭ 802 (+3108%)
Mutual labels:  ngrx
Angular Learning Resources
Curated chronological list of learning resources for Angular, from complete beginner to advanced level
Stars: ✭ 615 (+2360%)
Mutual labels:  ngrx

RemoteData

Slaying a UI Antipattern with Angular.

Build Status npm version

Library inspired by Kris Jenkins blog post about How Elm slays a UI antipattern, which mixes pretty well with another article written by Scott Hurff about what he calls the UI Stack.

Table Of Contents

What we are trying to solve

We are making an API request and want to display different things based on the request's status.

The traditional approach

export interface SunriseSunset {
  isInProgress: boolean;
  error: string;
  data: {
    sunrise: string;
    sunset: string;
  };
}

Let us see what each property means:

  • isInProgress: It is true while the data is being fetched.
  • error: It is either null (no errors) or any string (there are errors).
  • data: Either null (no data) or the result payload (there is data).

There are a few problems with this approach, the main one being that it is possible to create invalid states such as:

{
  "isInProgress": true,
  "error": "Fatal error",
  "data": {
    "sunrise": "I am good data.",
    "sunset": "I am good data too!"
  }
}

Our html template will have to use complex *ngIf statements to make sure we are displaying the correct information.

The RemoteData approach ™

Instead of using a complex data structures we use a single data type to express all possible request states:

type RemoteData<T, E> = NotAsked | InProgress<T> | Failure<E, T> | Success<T>;

This approach makes it impossible to create invalid states.

Installation

npm install --save ngx-remotedata

Basic Usage

// app.module.ts

import { RemoteDataModule } from 'ngx-remotedata';

@NgModule({
  imports: [
    // (...)
    RemoteDataModule
  ]
})
// app.component.ts

import {
  RemoteData,
  inProgress,
  notAsked,
  success,
  failure
} from 'ngx-remotedata';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  remoteData: RemoteData<string> = notAsked();

  setNotAsked() {
    this.remoteData = notAsked();
  }

  setInProgress() {
    this.remoteData = inProgress('In progress...');
  }

  setSuccess() {
    this.remoteData = success('Success!');
  }

  setFailure() {
    this.remoteData = failure('Wrong!');
  }
}
<!-- app.component.html -->

<ul>
  <li><button (click)="setNotAsked()">Not Asked</button></li>
  <li><button (click)="setInProgress()">InProgress</button></li>
  <li><button (click)="setSuccess()">Success</button></li>
  <li><button (click)="setFailure()">Failure</button></li>
</ul>

<hr />

<h4 *ngIf="remoteData | isNotAsked">Not Asked</h4>
<h4 *ngIf="remoteData | isInProgress">InProgress...</h4>
<h4 *ngIf="remoteData | isSuccess" style="color: green">
  {{ remoteData | successValue }}
</h4>
<h4 *ngIf="remoteData | isFailure" style="color: red">
  {{ remoteData | failureValue }}
</h4>

Examples

Demo

Source code

Api

RemoteData

RemoteData<T, E>

RemoteData is used to annotate your request variables. It wraps all possible request states into one single union type. Use the parameters to specify:

  • T: The success value type.

  • E: The error value type (string by default).

  • Type guard function: isRemoteData = <T, E>(value: unknown): value is RemoteData<T, E>.

NotAsked

  • Constructor function: notAsked<T, E>(): RemoteData<T, E>.
  • Type guard function: isNotAsked<T, E>(value: unknown): value is NotAsked.

When a RemoteData is a NotAsked instance, it means that the request hasn't been made yet.

type User = { email: string };
const myRemoteData: RemoteData<User> = notAsked();

// (...)

if (isNotAsked(myRemoteData)) {
  // Here myRemoteData is narrowed to NotAsked
}

InProgress

  • Constructor function: inProgress<T, E>(value?: T): RemoteData<T, E>.
  • Type guard function: isInProgress<T, E>(value: unknown): value is InProgress<T>.

When a RemoteData is an InProgress instance, it means that the request has been made, but it hasn't returned any data yet.

The InProgress instance can contain a value of the same T type as Success. Useful when you want to use the last Success value while the new data is being fetched.

type User = { email: string };
const myRemoteData: RemoteData<User> = inProgress({ email: '[email protected]' });

// (...)

if (isInProgress(myRemoteData)) {
  // Here myRemoteData is narrowed to InProgress
  console.log(`I have some data: ${myRemoteData.value().email}`);
}

Success

  • Constructor function: success<T, E>(value: T): RemoteData<T, E>.
  • Type guard function: isSuccess<T, E>(value: unknown): value is Success<T>.

When a RemoteData is a Success instance, it means that the request has completed successfully and the new data (of type T) is available.

type User = { email: string };
const myRemoteData: RemoteData<User> = success({ email: '[email protected]' });

// (...)

if (isSuccess(myRemoteData)) {
  // Here myRemoteData is narrowed to Success
  console.log(`I have some data: ${myRemoteData.value().email}`);
}

Failure

  • Constructor function: failure<T, E>(err: E, val?: T): RemoteData<T, E>.
  • Type guard function: isFailure<T, E>(value: unknown): value is Failure<E, T>.

When a RemoteData is a Failure instance, it means that the request has failed. You can get the error information (of type E) from the payload.

The Failure instance can contain a value of the same T type as Success. Useful when you want to use the last Success value while displaying the failure message.

type User = { email: string };
const myRemoteData: RemoteData<User> = failure('Something went wrong.', {
  email: '[email protected]'
});

// (...)

if (isFailure(myRemoteData)) {
  // Here myRemoteData is narrowed to Failure
  console.log(`This is the failure: ${myRemoteData.error()}`);
  console.log(`I have some data: ${myRemoteData.value().email}`);
}

The default type for errors is string, but you can also provide other types like Error:

type User = { email: string };
const myRemoteData: RemoteData<User, Error> = failure(
  new Error('Something went wrong.')
);

Unwrapping RemoteData values

getOrElse

getOrElse<T, E>(rd: RemoteData<T, E>, defaultValue: T): T;

getOrElse unwraps and returns the value of Success instances or the defaultValue when it's any other RemoteData variant.

let myRemoteData = success('ok!');
console.log(getOrElse(myRemoteData, 'The default value')); // ok!

myRemoteData = failure('There has been an error');
console.log(getOrElse(myRemoteData, 'The default value')); // The default value

fold

fold<T, E>(
  onNotAsked: () => T,
  onInProgress: (value: T | undefined) => T,
  onFailure: (error: E, value: T | undefined) => T,
  onSuccess: (value: T) => T,
  rd: RemoteData<T, E>
): T;

With fold you unwrap the RemoteData value by providing a function for each of the type variants.

Transforming RemoteData values

map

map<A, B, E>(
  fn: (a: A) => B,
  rd: RemoteData<A, E>
): RemoteData<B, E>;

With map you provide a transformation function that is applied to a RemoteData only when it's a Success instance.

const scream = (s: string) => s.toUpperCase();
const hello = success('hello!');
const helloScreaming = map(scream, hello);
console.log(helloScreaming); // success('HELLO!')

mapFailure

mapFailure<A, E, F>(
  fn: (e: E) => F,
  rd: RemoteData<A, E>
): RemoteData<A, F>;

With mapFailure you provide a transformation function that is applied to a RemoteData only when it's a Failure instance.

const scream = (s: string) => s.toUpperCase();
const error = failure('wrong!');
const wrongScreaming = mapFailure(scream, error);
console.log(wrongScreaming); // failure('WRONG!')

Pipes

isNotAsked

transform<T, E>(rd: RemoteData<T, E>): boolean;

Returns true when RemoteData is a NotAsked instance.

anyIsNotAsked

transform<T, E>(
  rds$: Observable<RemoteData<T, E>>[]
): boolean;

Returns true when any RemoteData<T, E>[] items is a NotAsked instance.

isInProgress

transform<T, E>(rd: RemoteData<T, E>): boolean;

Returns true when RemoteData is an InProgress instance.

anyIsInProgress

transform<T, E>(
  rds$: Observable<RemoteData<T, E>>[]
): boolean;

Returns true when any RemoteData<T, E>[] item is an InProgress instance.

isFailure

transform<T, E>(rd: RemoteData<T, E>): boolean;

Returns true when RemoteData is a Failure instance.

isSuccess

transform<T, E>(rd: RemoteData<T, E>): boolean;

Returns true when RemoteData is a Success instance.

hasValue

transform<T, E>(rd: RemoteData<T, E>): boolean;

Returns true when RemoteData is a Success instance or is an InProgress or Failure instance with a value that is not null or undefined.

successValue

transform<T, E>(
  rd: RemoteData<T, E>,
  defaultValue?: T
): T | undefined;

Returns the Success payload (of type T) when the RemoteData is a Success instance, otherwise it returns the defaultValue when provided or undefined when not.

inProgressValue

transform<T, E>(
  rd: RemoteData<T, E>,
  defaultValue?: T | undefined
): T | undefined;

Returns the InProgress payload (of type T) when RemoteData is an InProgress instance, otherwise it returns the provided defaultValue or undefined when not.

remoteDataValue

transform<T, E>(rd: RemoteData<T, E>): T | E | undefined;

Returns the InProgress, Failure or Success payload (of type T) when RemoteData is an InProgress, Failure or Success instance. Returns undefined otherwise.

failureError

transform<T, E>(rd: RemoteData<T, E>): E | undefined

Returns the Failure error payload (of type E) when RemoteData is a Failure instance or undefined otherwise.

failureValue

transform<T, E>(rd: RemoteData<T, E>): T | undefined

Returns the Failure payload (of type T) when RemoteData is a Failure instance or undefined otherwise.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].