Single Page Application micro framework. Views, routes and controllers in 60 lines of code

Related tags

Routing spapp
Overview

SPApp

Single Page Application Micro Framework

Supports views, controllers and routing in 60 lines of code!

Introduction

If you heard anything about MVC in web applications, in particular words like "views", "controllers", "routing" and don't feel that you need more than jQuery in your project then this small library is for you.

Structure of your SPApp

Let's consider structure of simple "contact list" web application:

<script type="text/javascript" src="spapp.js"></script>
<body>

  <nav>
    <a href="#contact-list">Contacts</a>
    <a href="#about">About</a>
  </nav>
  
  <section id="contact-list" src="pages/contact-list.htm" default></section>
  <section id="contact-details" src="pages/contact-details.htm"></section>
  <section id="about" src="pages/about.htm" default></section>

</body>

We have <nav>igation section here and collection of <section>s - views that need to be presented when user clicks on one of hyperlinks in the nav bar (or anywhere else).

Routing

By defining <section id="some id"> element in your markup you actually define a route. When browser is navigated to {url of your app}#id-of-section the SPApp module catches this event and shows <section> with that ID.

No special mechanism for declaring routes is required - simply declare <section>s with proper IDs.

If you want to load some section by default, simply add the default attribute to it.

Views and Controllers

Notice that each <section> above has src={url} attribute declared. That URL points to partial HTML fragment that defines content of the view and its controller function.

Typical structure of a view looks like this:

  <h2>Contact Details</h2>

  <label>First name</label> <input name="firstName">
  <label>Last name</label> <input name="lastName">
  
  <button class="save">Save</button> 
  ... 

<script>
//|
//| view controller
//|
//| Note the name below shall match id of the section
//|
  window.app.page("contact-details", function() // registering the controller
  {
    // initialize view variables in localscope
    // this is "page ready" code - happens once per app life time.
    
    var $firstName = $(this).find('[name="firstName"]');
    var $lastName = $(this).find('[name="lastName"]');
    
    var contactId; // current id of presented contact 
    
    $(this).on("click","button.save", function(
      // save contact (contactId) using current $firstName $lastName values
    ));
    
    ...
    
    // presenter of the view - load data and show: 
    // this function is "page activated" code - it gets called each time the page gets presented 
    return function(params) {
      contactId = params; // setting current contactId 
      var contact = contacts[contactId];
      // show values 
      $firstName.val(contact.firstName);
      $lastName.val(contact.lastName);
      ...
      
    }
  }); 
</script>

As you see markup above contains two sections:

  1. html markup of the view per se and
  2. script block with app.page("{name}",scopeInitializerAndController ) function.

The scopeInitializerAndController function above serves two tasks:

  1. initilizes the view and its scope variables and
  2. returns function (a.k.a. "controller"). That function will be called each time the view needs to be presented/rendered.

Switching views and passing parameters

The application can be switched to show different views in one of these ways:

  1. By user navigating browser to {your app url}#name-of-the-view:optional-parameter;
  2. By user clicking on <a href="#name-of-the-view:optional-parameter"> hyperlinks on app page;
  3. By code, calling window.app("name-of-the-view", parameters) function.

In all cases the SPApp framework will call controller registered for the view passing given parameter to it.

View events

While switching the view the SPAapp will trigger two events:

  1. "page.hidden" with event.target set on <section> to be "closed" and
  2. "page.shown" with event.target set on <section> to be "shown".

So by subscribing on these events any code inside or outside controllers can receive notification about application state change.

Styling application and view states.

When the application navigates to particular view defined in <section id="view-a"> it is said that it is in "view-a" state. SPApp marks this state by assigning "view-a" class to the <body> element in your document. Having this you can define view visibility rules in your CSS:

/* all sections are off by default */
body > section { display:none; }
/* app state -> view visibility rules: */
body.view-a > section#view-a,
body.view-b > section#view-b,
body.view-c > section#view-c  { display:block; }

Note that switching the view into different states may require not only to hide/show some sections but also to style some other indicators like nav-bar items highlighting:

/* nav highlightion rules */
body.view-a > nav > a[href="#view-a"],
body.view-b > nav > a[href="#view-b"],
body.view-c > nav > a[href="#view-c"] { background:white; color:black; }

app.get - the page getter function

When SPAapp needs to download page's html it calls app.get(url, $page, pageName) function. Default implementation of the app.get is as follows:

app.get = function(src,$page,pageName) { 
  // src - string, url of the page to load
  // $page - jq wrapper, page element
  // pageName - string, name of the page to load
  return $.get(src, "html"); 
};

so it just delegates page downloading to jQuery and returns promise obtained from $.get().

In your application you can override that default loader to have something like this:

app.get = function(src,$page,pageName) { 
  var rq = $.get(src, "html"); 
      // generate load events 
      rq.done( function(html,textStatus,jqXHR) { $page.trigger("page.loadsuccess",jqXHR); } ) 
        .fail( function(jqXHR,textStatus,errorThrown) { $page.trigger("page.loadfailure",jqXHR,textStatus,errorThrown); } );
  // NOTE: your custom page downloader shall return jq promise
  return rq;
};

Wrapping up

It is not strictly required for controller's code to be placed inside the view markup, you can put them in separate script files and include as any other scripts:

<script type="text/javascript" src="controller-a.js"></script>
<script type="text/javascript" src="controller-b.js"></script>
...

The only thing that required is that scopeInitializerAndControoler function must be registered by calling

window.app.page("view-a", function() // registering the controller
  {
    ... initialization ...
    return function(params) {
      ... presentation ...
    }
  });

In the same way markup for views can be provided inline without use of external files:

  <section id="view-a" >
    ... markup of view-a ...
  </section>
  <section id="view-b" >
    ... markup of view-b ...
  </section>

Live Demo

Check this SPApp in action demo. I've created it in explicitly simple and minimalistic fashion in order to highlight just SPAapp. I have projects where it works in Twitter Bootstrap environment for example.

And check Team ToolBox application where SpApp is used.

Comments
  • Some changes for cleaning the sections after leaving a page.

    Some changes for cleaning the sections after leaving a page.

    Sry, the changes at the README.md are out of scope. But I missed it to change the readme and add the 'default' attribute to the sample code.

    Also i run in trouble by using spapp with many objects in the sections. After leaving a section some times the application slows down. I solved this by clearing the sections content after leaving. Sections without a 'src' attribute will be ignored.

    opened by ThatScalaGuy 3
  • Automatic css generation

    Automatic css generation

    I found this piece of css file difficult to manage:

    /* all sections are off by default */
    body > section { display:none; }
    /* app state -> view visibility rules: */
    body.view-a > section#view-a,
    body.view-b > section#view-b,
    body.view-c > section#view-c  { display:block; }
    

    I've written simple javascript to auto-create it.

    opened by lmarszal 1
  • New event handlers

    New event handlers

    Added 4 new event handlers so that applications can plug into the ajax functionality:

    • page.loadStarted triggered before the ajax call is made.
    • page.loadSucceeded triggered after the ajax call succeeds but before the new view is shown.
    • page.loadFailed triggered after the ajax call fails.
    • page.loadFinished always triggered after the ajax call is finished, regardless of whether it succeeded.

    Note: This PR removes the default error handling when an ajax call fails (alert("failed to get:" + src);).

    opened by todofixthis 1
  • page.shown isn't work

    page.shown isn't work

    if loaded page haven't record in pageHandlers array i.e the one haven't include app.page( id, func ) inside, than this code seems work wrong

    if($currentPage = $page)
          $currentPage.trigger("page.shown",currentPageName);
    

    Прошу простить за дурацкий английский, но на ком ж тренироваться )

    opened by otmjka 1
  • Fix broken headings in Markdown files

    Fix broken headings in Markdown files

    GitHub changed the way Markdown headings are parsed, so this change fixes it.

    See bryant1410/readmesfix for more information.

    Tackles bryant1410/readmesfix#1

    opened by bryant1410 0
  • Default view is not working

    Default view is not working

    I have downloaded the 1.3 release, default property does not work. It is not really an issue your master branch is working. I think you should update the release.

    Very good and simple project keep up the good work :+1:

    opened by okanbasoglu 2
Owner
Andrew
UI in all its incarnations. MS in Applied Physics and Mathematics. Diploma in Arts. Participated in HTML5 development at W3C as an invited expert.
Andrew
Micro client-side router inspired by the Express router

Tiny Express-inspired client-side router. page('/', index) page('/user/:user', show) page('/user/:user/edit', edit) page('/user/:user/album', album) p

Sloth 7.6k Dec 28, 2022
a tiny and isomorphic URL router for JavaScript

Synopsis Director is a router. Routing is the process of determining what code to run when a URL is requested. Motivation A routing library that works

a decoupled application framework 5.6k Dec 28, 2022
A chainable router designed for Next.js api. inspired and regex based from itty-router

Next.js Api Router A chainable router designed for Next.js api. inspired and regex based from itty-router Features Tiny (~8xx bytes compressed) with z

Aris Riswanto 1 Jan 21, 2022
This package enables you to define your routes using the flat-routes convention.

Remix Flat Routes This package enables you to define your routes using the flat-routes convention. This is based on the gist by Ryan Florence ?? Insta

Kiliman 180 Jan 3, 2023
Micro.publish is an Obsidian plugin to publish notes directly to Micro.blog, written in TypeScript

Micro.publish Micro.publish is a community maintained plugin for Obsidian to publish notes to a Micro.blog blog. Installing This plugin will be availa

Otavio Cordeiro 14 Dec 9, 2022
A simple yet feature-rich counter that allows you to count the views of your Github README files and profile page

View Counter Purpose This is a simple yet feature-rich counter that allows you to count the views of your github README files and profile page. Featur

Toby Hagan 4 Nov 10, 2022
Lightweight analytics abstraction layer for tracking page views, custom events, & identifying visitors

A lightweight analytics abstraction library for tracking page views, custom events, & identify visitors. Designed to work with any third-party analyti

David Wells 1.9k Dec 31, 2022
This restaurant project is a SPA (single-page application) website. The user can navigate between the home, menu and contact page. I used the MealDB API to display some menu items.

Fresh Cuisine This restaurant project is from the Odin Project and it is a SPA (single-page application) website. The user can navigate between the ho

Virag Kormoczy 7 Nov 2, 2022
This plugin can be embedded in PHP application to give the web application specific routes/href

Routes Plugin PHP This plugin can be embedded in PHP application to give the web application specific routes/href location and for entering specific/l

Ifechukwudeni Oweh 7 Jul 17, 2022
GameLand is an online gaming web application that allows users to view different kind of games available and share their views on each game.

GameLand is an online gaming web application that allows users to view different kind of games available and share their views on each game.Users can like and make reservations to play online. Built with HTML/CSS , JAVASCRIPT,API.

tarike bouari 6 Sep 9, 2022
Create flexible REST endpoints and controllers from Sequelize models in your Express app

Finale Create flexible REST endpoints and controllers from Sequelize models in your Express or Restify app. This project aims to be a Sequelize 4.x an

Tom Juszczyk 181 Oct 18, 2022
Node js package makes creating node jd dependincies files like Controllers,Entities and Repositories easier by executing a few instructions

Nodejs Studio Node js package makes creating node js project dependincies files like Controllers,Entities and Repositories easier by executing a few i

Syrian Open Source 9 Oct 12, 2022
This project is for backend developers with node js. It create the "Mongoose models, routers and controllers" with cli command line.

Node.js Boilerplate This project is for backend developers with node js. It create the "Mongoose models, routers and controllers" with cli command lin

Bilal Yaver 3 Sep 19, 2022
A Node.js express middleware that implements API versioning for route controllers

express-version-route This npm package provides an ExpressJS middleware to load route controllers based on api versions. Implementing API Versioning i

Liran Tal 87 Nov 15, 2022
A simple tool to help you connect your favorite controllers / Arduino to various train simulator games on Windows using memory hacks.

A simple tool to help you connect your favorite controllers (e.g. Densha de Go! series) / Arduino to various train simulator games on Windows using memory hacks.

Tongze Wang 2 Feb 7, 2022
Perfect SvelteKit dark mode in 2 lines of code. Support System preference and any other theme with no flashing

This library is a port of next-theme for SvelteKit. All credit goes to pacocoursey and all next-themes contributors While usable, this library is stil

null 42 Sep 30, 2022
A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs

A fast and optimized middleware server with an absurdly small amount of code (300 lines) built on top of Deno's native HTTP APIs with no dependencies. It also has a collection of useful middlewares: log file, serve static, CORS, session, rate limit, token, body parsers, redirect, proxy and handle upload. In "README" there are examples of all the resources. Faster's ideology is: all you need is an optimized middleware manager, all other functionality is middleware.

Henrique Emanoel Viana 19 Dec 28, 2022
Get the latest Flashbots blocks and Flashbots transactions using TypeScript in two lines of code !

mev-blocks-js This package can let you query the Flashbots blocks API easily from any JavaScript or TypeScript project. You can access the Flashbots b

Luca G.F. 6 May 14, 2022
Framework agnostic CLI tool for routes parsing and generation of a type-safe helper for safe route usage. 🗺️ Remix driver included. 🤟

About routes-gen is a framework agnostic CLI tool for routes parsing and generation of a type-safe helper for safe route usage. Think of it as Prisma,

Stratulat Alexandru 192 Jan 2, 2023