frappe 0.4.0+6

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 32

Frappé

Build Status Coverage Status

A functional reactive programming library for Dart. Frappé extends the functionality of Dart's streams, and introduces new concepts like properties/signals.

Why FRP?

UI applications today are highly interactive and data driven. User input can trigger updates to the DOM, playing animations, invoking network requests, and modifying application state. Using the traditional form of event callbacks and modifying state variables can quickly become difficult to write and maintain.

Functional reactive programming (FRP) makes it clearer to define user and system events that cause state changes. For instance, it's easy to define "when a user performs A, do X and Y, then output Z."

When writing reactive code, you'll find yourself focusing more on the dependencies between events for business logic, and less time on their implementation details.

Example

Lets write an auto-complete movie widget with Frappé. The widget has an input field for the movie name, and a list element that displays movies that most closely match the user's input. A working version can be found here.

var searchInput = document.querySelector("#searchInput");
var suggestionsElement = document.querySelector("#suggestions");

var onInput = new EventStream(searchInput.onInput)
    .debounce(new Duration(milliseconds: 250)) // Limit the number of network requests
    .map((event) => event.target.value) // Get the text from the input field
    .distinct(); // Ignore duplicate events with the same text

// Make a network request to get the list of movie suggestions. Because requests
// are asynchronous, they can complete out of order. Use `flatMapLatest` to only
// respond to request for the last text change.
var suggestions = onInput.flatMapLatest((input) => querySuggestions(input));

suggestions.listen((movies) =>
    suggestionsElement.children
        ..clear()
        ..addAll(movies.map((movie) => new LIElement()..text = movie));

// Show "Searching ..." feedback while the request is pending
var isPending = searchInput.onInput.isWaitingOn(suggestions);
isPending.where((value) => value).listen((_) {
  suggestionsElement.children
      ..clear()
      ..add(new DivElement()..text = "Searching ...");
});

Future<List<String>> querySuggestions(String input) {
  // Query some API that returns suggestions for 'input'
}

API

You can explore the full API here.

Reactable

The Reactable class extends from Stream and is inherited by EventStream and Property. Because these classes extend from Dart's Stream, you can pass them directly to other APIs that expect a Stream.

EventStream

An EventStream represents a series of discrete events. They're like a Stream in Dart, but extends its functionality with the methods found on Reactable.

Event streams can be created from a property via Property.asEventStream(), or through one of its constructor methods. If an event stream is created from a property, its first event will be the property's current value.

An EventStream will inherit the behavior of the stream from which it originated. So if an event stream was created from a broadcast stream, it can support multiple subscriptions. Likewise, if an event stream was created from a single-subscription stream, only one subscription can be added to it. Take a look at the article on single-subscription streams vs broadcast streams to learn more about their different behaviors.

Property

A Property represents a value that changes over time. They're similar to event streams, but they remember their current value. Whenever a subscription is added to a property, it will receive the property's current value as its first event.

Properties can be created through one of its constructors, or from an event stream via EventStream.asProperty(). Depending on how the property was created, it may or may not have a starting value. Separate methods are available for creating properties with an initial value, i.e. Property.fromStreamWithInitialValue() and EventStream.asPropertyWithInitialValue(). Properties can support having a null initial value, and is partly the motivation for having separate construction methods.

Internally, properties are implemented as broadcast streams and can receive multiple subscriptions.

If you were to model text input using properties and streams, the individual key strokes would be events, and the resulting text is a property.

Learning More

Definitely take a look at the API documentation, and play around with some of the examples. It's also worth checking out BaconJS and RxJS. They're both mature FRP libraries, and offer some great resourses on the subject.

Running Tests

Tests are run through the Grinder build task. This will run the Dart Analyzer, linter and unit tests.

  • Install grinder: pub global activate grinder
  • Run grinder: grind build

Features and bugs

Please file feature requests and bugs at the issue tracker.

Contributing

Take a look here on ways to contribute to Frappé.

Changelog

Note: Patch versions that only include documentation changes are omitted.

0.4.0+5 (08/06/2015)

  • Add better type annotations to suppress warnings in DDC. [#47]

0.4.0+3 (03/09/2015)

  • Fix an issue where the stream returned by Property.asEventStream() would still behave like a property [#38]

0.4.0 (03/02/2015)

  • Fix an issue where EventStreams wouldn't be the same type of stream as its source [#17]
  • Transformation methods on Property or EventStream now return the same type of Reactable
  • Add Reactable.concat()
  • Add Reactable.concatAll()
  • Add Reactable.doAction()
  • Add Reactable.mergeAll()
  • Add Reactable.sampleOn()
  • Add Reactable.sampleEachPeriod()
  • Add Reactable.selectFirst()
  • Add Reactable.startWith()
  • Add Reactable.startWithValues()
  • Add Reactable.zip()
  • Add EventStream.empty() constructor
  • Add EventStream.fromValue() constructor
  • Add EventStream.periodic() constructor
  • Bug fixes in Reactable.isWaitingOn()
  • Deprecate Reactable.asStream(), it's now Reactable.asEventStream()
  • Deprecate Property operator overrides, equals(), >, >=, <, <=, +, -, *, /
  • Property.and() and Property.or() can now accept any stream
  • Property.not() has been moved to Reactable
  • Remove type declerations for Reactable.scan()

0.3.2+1 (01/10/2015)

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  frappe: "^0.4.0+6"

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:frappe/frappe.dart';
        
Version Uploaded Documentation Archive
0.4.0+6 Aug 6, 2015 Go to the documentation of frappe 0.4.0+6 Download frappe 0.4.0+6 archive
0.4.0+5 Aug 6, 2015 Go to the documentation of frappe 0.4.0+5 Download frappe 0.4.0+5 archive
0.4.0+4 Mar 10, 2015 Go to the documentation of frappe 0.4.0+4 Download frappe 0.4.0+4 archive
0.4.0+3 Mar 10, 2015 Go to the documentation of frappe 0.4.0+3 Download frappe 0.4.0+3 archive
0.4.0+2 Mar 3, 2015 Go to the documentation of frappe 0.4.0+2 Download frappe 0.4.0+2 archive
0.4.0+1 Mar 3, 2015 Go to the documentation of frappe 0.4.0+1 Download frappe 0.4.0+1 archive
0.4.0 Mar 3, 2015 Go to the documentation of frappe 0.4.0 Download frappe 0.4.0 archive
0.3.2 Jan 7, 2015 Go to the documentation of frappe 0.3.2 Download frappe 0.3.2 archive
0.3.1 Dec 21, 2014 Go to the documentation of frappe 0.3.1 Download frappe 0.3.1 archive
0.3.0+4 Dec 11, 2014 Go to the documentation of frappe 0.3.0+4 Download frappe 0.3.0+4 archive

All 19 versions...

Analysis

This feature is new.
We welcome feedback.

We analyzed this package, and provided a score, details, and suggestions below.

  • tool failures on Dec 6, 2017
  • Dart: 2.0.0-dev.8.0
  • pana: 0.7.3+1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
64
Health:
Code health derived from static analysis. [more]
0
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
0
Overall score:
Weighted score of the above. [more]
32

Platforms

Detected platforms:

Error(s) prevent platform classification.

Suggestions

  • Fix lib/src/property.dart.

    Strong-mode analysis of lib/src/property.dart failed with the following error:

    line: 82 col: 70
    The operands of the '&&' operator must be assignable to 'bool'.

  • Fix lib/src/reactable.dart.

    Strong-mode analysis of lib/src/reactable.dart failed with the following error:

    line: 40 col: 3
    Invalid override. The type of 'Reactable.asyncExpand' ('((T) → Stream<dynamic>) → Reactable<dynamic>') isn't a subtype of 'Stream<T>.asyncExpand' ('<E>((T) → Stream<E>) → Stream<E>').

Dependencies

Package Constraint Resolved Available
Direct dependencies
stream_transformers ^0.3.0 0.3.0+3
Dev dependencies
browser ^0.10.0
grinder >=0.7.0 <0.7.1
guinness ^0.1.6