Skip to content

Notable Features

Kevin Glen Roy Greer edited this page Apr 28, 2015 · 2 revisions

FOAM possesses several features which are uncommon amongst Javascript MVC frameworks. These include:

  1. Reactive MVC: FOAM introduces a new variation of MVC, that we call Reactive-MVC, which is a hybrid of Functional Reactive Programming (FRP) and traditional MVC.  Every property of every modelled object in FOAM is a potential model.  If you access 'obj.field1' then you get the regular value of obj's field1 property.  But if you access obj$ you get a 'Value', which is essentially an MVC-Model with the following interface:

    interface Value {
    
    Object get();
    void set(Object o);
    addListener(Listener l);
    removeListener(Listener l);
 }
And if you do something like '`obj1.value$ = obj2.value$`' then that forms a two-way binding between those values.  There is also support for one-way binding and creating relationships between values (which you have one or two-way adapter functions).  This simplifies Views from having to build in support for typical MVC Models (ie. subscribing and unsubscribing from models, handling model changes, etc.) as every value has Model-like capabilities. 

Users can also do [spreadsheet-like reactive programming](http://foam-framework.github.io/foam/foam/demos/DemoCat.html?q=reacting).  The code from that example demonstrates this by having a set of objects react to movement of the mouse cursor:


>     // Make clock1 follow the mouse
Events.dynamic(function () { clock1.x = mouse.x; clock1.y = mouse.y; });

// Make clock2 orbit clock1 as a function of time.

Events.dynamic(function () {
   `clock2.x = clock1.x + 200*Math.cos(timer.time**Math.PI/1000);`
   `clock2.y = clock1.y + 200*Math.sin(timer.time**Math.PI/1000);`
});
Any function that is supplied to `Events.dynamic()` is automatically updated whenever any of the dependent values in the function update.  No need for listeners, events or batching logic.  Also note that in the second function above, we use '`timer.time`'.  Dynamic functions which react to time are essentially animations. This gives FOAM animation support as a natural consequence of its reactive programming support. See our [animation demo](http://foam-framework.github.io/foam/foam/demos/demoBlockDiagram.html) for an example.
  1. Standardized Data Access Objects (DAO's): FOAM uses a standard interface for accessing data, irregardless of the underlying storage mechanism.  With only a one-line change, the TodoMVC example can be switched from using local storage, to chrome storage, sync storage, IndexedDB, a REST server, or just simple arrays.  The DAO system also adds high-level features like: filtering, sorting, grouping, accumulators and joins.  Client-side caching, syncing, and offline support is also supported for network data sources.  A couple other features of note in this area include a superset of the query language seen in Google Search and GMail, and a client-side query-optimizer.  These two features ensure that you have rich performant search, even while offline. See our DAO demo for more details.

  2. Multi-Modal View Components: FOAM uses the same component model for DOM-based components as it uses for Canvas, SVG, WebGL, and non-visual components.  This means that you're free to mix-and-match the best underlying technologies for each component and then combine them transparently.

    For example, two FOAM calculator demos use the same code to instantiate and link two fundamentally different types of buttons. One uses simple DOM-based buttons, whereas the other uses graphical animated material design canvas buttons.  The code to instantiate and link the two fundamentally different types of buttons in the templates are identical.

    We’ve also created a number of demos and apps which support graphics. They all support the same Reactive-MVC data-binding and component model as the simplest DOM-based example.

  3. Physics Engine: FOAM has a simple physics engine which simplifies the creation of games and realistic-looking animations.

  4. Rich Meta-Data: FOAM objects maintain the meta-information at runtime, which allows for reusable meta-data driven components like DetailViews, TableViews, GridViews, etc. to be built.  This is what the "Active Modeller" in "Feature-Oriented Active Modeller" refers to.

    One interesting application of this is the FOAM API browser, which uses this meta-data to generate API docs at runtime without having to parse the source or use any kind of build process.

    FOAM refers to meta-data as Models, as in Model-Oriented Software Engineering or Model-Driven Architecture, which overloads the term with the term Model from MVC.  Which Model is being referred to can usually be inferred from the context.

  5. Reusable Controllers: Most MVC frameworks are really more like View-libraries, but FOAM provides a more complete MVC stack.  Just as DAOs and modelled objects provide a more robust Model layer than is typical, FOAM has support for high-level reusable controllers which provide standard support for CRUD++ UI's.  The ++ includes such things as: filtering, searching, sorting, canned queries, bookmarks, memento management, infinite scrolling, etc.

    For example, the FOAM GMail and Issue Tracker applications both use the same AppController component, despite having different Views, data models, and back-end DAO services.  Each application is under 1000 lines of code.

  6. Miscellaneous: Other noteworthy features of FOAM include:

    • Parse Combinator Library
    • Integrated Unit and Regression Testing
    • Run-Time Type-Checking
    • Dependency Management
  7. Cross-Language Cross-Tier Meta-Programming: FOAM is a generic meta-programming system and isn't restricted to just Javascript or to just Views.  In can be used just as well to generate server-side Java, C++ business objects, or database bindings.

    For example, if you open the console in the TodoMVC demo, and type 'Todo.javaSource();' you'll get generated java source for the Todo Model:

// Generated by FOAM, do not modify.
// Version 1

import foam.core.*;

public class Todo
  extends FObject
{
 
  public final static Property ID = new AbstractStringProperty() {
    public String getName() { return "id_"; }
    public String getLabel() { return "Id"; }
    public Object get(Object o) { return ((Todo) o).getId(); }
    public void set(Object o, Object v) { ((Todo) o).setId(toNative(v)); }
    public int compare(Object o1, Object o2) { return compareValues(((Todo)o1).id_, ((Todo)o2).id_); }
  };
 
  public final static Property COMPLETED = new AbstractBoolProperty() {
    public String getName() { return "completed_"; }
    public String getLabel() { return "Completed"; }
    public Object get(Object o) { return ((Todo) o).getCompleted(); }
    public void set(Object o, Object v) { ((Todo) o).setCompleted(toNative(v)); }
    public int compare(Object o1, Object o2) { return compareValues(((Todo)o1).completed_, ((Todo)o2).completed_); }
  };
 
  public final static Property TEXT = new AbstractStringProperty() {
    public String getName() { return "text_"; }
    public String getLabel() { return "Text"; }
    public Object get(Object o) { return ((Todo) o).getText(); }
    public void set(Object o, Object v) { ((Todo) o).setText(toNative(v)); }
    public int compare(Object o1, Object o2) { return compareValues(((Todo)o1).text_, ((Todo)o2).text_); }
  };
 

  final static Model model__ = new AbstractModel(new Property[] { ID, COMPLETED, TEXT, }) {
    public String getName() { return "Todo"; }
    public String getLabel() { return "Todo"; }
    public Property id() { return ID; }
  };

  public Model model() {
    return model__;
  }
  public static Model MODEL() {
    return model__;
  }

 
  private String id_;  
  private bool completed_;  
  private String text_;  

  public Todo()
  {
  }

  public Todo(String id, bool completed, String text)
  {  
     id_ = id;  
     completed_ = completed;  
     text_ = text;  
  }


 
  public String getId() {
      return id_;
  };
  public void setId(String id) {
      id_ = id;
  };
 
  public bool getCompleted() {
      return completed_;
  };
  public void setCompleted(bool completed) {
      completed_ = completed;
  };
 
  public String getText() {
      return text_;
  };
  public void setText(String text) {
      text_ = text;
  };
 

  public int hashCode() {
     int hash = 1;
 
     hash = hash * 31 + hash(id_);  
     hash = hash * 31 + hash(completed_);  
     hash = hash * 31 + hash(text_);  

     return hash;
  }

  public int compareTo(Object obj) {
     if ( obj == this ) return 0;
     if ( obj == null ) return 1;

     Todo other = (Todo) obj;

     int cmp;
 
     if ( ( cmp = compare(getId(), other.getId()) ) != 0 ) return cmp;  
     if ( ( cmp = compare(getCompleted(), other.getCompleted()) ) != 0 ) return cmp;  
     if ( ( cmp = compare(getText(), other.getText()) ) != 0 ) return cmp;  

     return 0;
  }

  public StringBuilder append(StringBuilder b) {
     return b
        .append("id=").append(getId()).append(", ")
        .append("completed=").append(getCompleted()).append(", ")
        .append("text=").append(getText())
        ;
  }

  public Object fclone() {
     Todo c = new Todo();
     c.setId(getId());
     c.setCompleted(getCompleted());
     c.setText(getText());
     return c;
  }

}

Clone this wiki locally