Globe Icon

The UI Blocks Methodology

The UI Blocks Methodology: Server-side components as an alternative to client-side Javascript frameworks

What is it?

UI Blocks is a component-based framework for building modular, nestable graphical user interfaces in ProcessWire using MVC principals. Similar to Vue or React components, but all markup is generated server-side. 

While our particular implementation is built on top of the ProcessWire CMS/CMF, which is in turn built on PHP, UI Blocks can also be thought of as a methodology or design pattern that could be implemented in PHP more generally, or in any other server side language. In fact, a version of the UI Blocks framework that doesn’t require ProcessWire as a dependency is on the roadmap.

Design Philosophy

UI Blocks is a variation on the MVC (Model-View-Controller) design pattern. MVC is a way of organizing your code into three separate categories: Models, Views, and Controllers. 

Models

Models are collections of code (usually classes) that are designed to ‘model’ the data in your application, which is often stored in a database. 

In an e-commerce application, for example, you would likely have a Product model, a Cart model, a User model, etc. Each of these models/classes would have properties/data (e.g. a Product would have a name, price, and SKU), as well as methods/functions they can perform (e.g. a Cart model would have a getTotal() method to total up the price of all of the products within it). 

The models are where the ‘heavy lifting’ or “business logic” happens in an application. They allow you to write and read from the database and to do whatever calculations are required on that data. 

Models are not concerned with whether the data is coming from or going to a GUI (graphical user interface/web page), a PDF report, or some kind of API like REST or GraphQL. The idea is simply to provide a good way to work with your application data that is independent of how it is collected from the user or shown to the user.

Models in ProcessWire

In ProcessWire, our models are represented by Pages (which are more like data records than they are strictly web “pages”—though they do often also represent a url-accessible web page). Pages function as an ORM (Object Relational Model) for the underlying data stored in the MySQL database, and ProcessWire’s selector engine makes querying the database for pages easy.

When a page is pulled from the database, a Page object is created to represent it in the application. Fields are mapped to properties of the object, and the methods of the Page class allow us to pull and modify the data. This is similar to ActiveRecord in Ruby on Rails or Eloquent in Laravel.

ProcessWire allows us to extend the Page class for a particular page template, allowing us to add our own models with their own methods that inherit all of the basic CRUD (Create Read Update Destroy) capabilities of pages.

Views

Views are the presentation layer. They define how your application data is shown to the user, though HTML markup. A static web page built with only HTML and CSS is basically just a view.

Only presentational code should be contained in the view file. This includes if/thens for deciding whether to show or hide a particular block of HTML based on the true/false state of some variable, and loops for displaying an array of data in a list or table.  

Often a separate templating language like Twig or Smarty is used inside of views to prevent the direct use of php and limit what types of logic can be used in the view. 

We do not use templating languages since PHP comes with its own template-friendly syntax (which we will demonstrate later). Templating languages also add another layer of processing overhead (and more things that can go wrong). They can also be limiting in those (hopefully rare) cases where you may need to bend the rules a bit and add a little more logic to your template.

Controllers

Controllers are what tie Models and Views together. They contain the code necessary to accept input from users (including the URL path, URL GET variables, and POST values from form inputs), to retrieve the appropriate data from the associated Model(s), to manipulate and pass that data to the appropriate View, and to return the View to the user.

Controllers should be “thin,” meaning they should not contain a lot of complex logic. As a good rule of thumb, if you discover some piece of code in one controller that you wish you could reuse in another, it probably didn’t belong in a controller to begin with and should be moved into a model instead.

UI Blocks vs MVC and HMVC

UI Blocks is an implementation of MVC. It might be more accurate to describe it as HMVC or Hierarchical-Model-View-Controller. This is the idea that instead of representing each page or screen by a single MVC triad, you could have each component within the page (as well as the page itself) represented by its own MVC triad.

You see, the trouble with traditional MVC is that websites and web applications are not fundamentally about pages, but about components (navigation blocks, shopping carts, product grids, post lists, contact forms, fields, etc.) that can appear and reappear on any number of pages. These component—or blocks—often appear nested inside of one another, as with a field inside of a form, or a shopping cart inside of a checkout. And the site’s layout (header, navigation, and footer) can itself be thought of as a block that encloses its child blocks.

In plain old MVC, this issue is typically addressed with the concept of “partials”, which are pieces of html code that can be included on more than one page. But without a good way to bundle the relevant controller logic (not to mention the required CSS and javascript) with that particular partial, developers end up with the same code duplicated in multiple controllers or having to create awkward workarounds and naming conventions.

UI Blocks follows this HMVC pattern, with the exception that each block doesn’t need to (and should not) have its own separate model. Keeping our models separate is allows us to maintain a separation of concerns between our front end and back end logic, making our back end code accessible by any UI Block and reducing code duplication. 

Typical UI Blocks Application Flow

UI Blocks vs Client-side Approaches

If you are familiar with the concept of components in Javascript frameworks like Vue.js or React, the UI Blocks methodology should sound familiar. 

In fact, although UI Blocks was first developed in 2015 (before I was aware of Vue.js), Vue.js is a near exact client-side implementation of the UI Blocks design pattern.

The fundamental difference between a client-side framework like Vue.js and UI Blocks is that all markup/html in UI blocks in generated server-side, while all markup in Vue.js is generated in the browser. 

The client-side approach

In Vue.js, all markup is generated and handled on the client (browser) side. In MVC terms, this means that the server is only responsible for the model and for providing an API to retrieve data from the model, while the client handles the controllers and views. 

The component controllers call the API using AJAX calls, and the API returns pure data in JSON format. The controller then massages the data as required and outputs it to the user using the component’s view (called a template in Vue.js lingo).

This client-side markup generation allows for an extremely dynamic and reactive experience for the user. If one piece of data in the system is changed, any part of the view that depends on that data can update immediately to represent the new state, and changes from the server can be pulled quietly in the background vs. the user having to wait for the entire page to refresh to display the new data.

Issues with the client-side approach

For a dynamic application with real-time updates and drag-and-drop interfaces (think games, graphic design apps, etc), the client-side approach is a must. However, there are some drawbacks to this approach:

  • It’s complicated to get setup properly, with a steep learning curve for developers who have to learn the Javascript NPM ecosystem, Webpack, etc. 
  • It requires building a separate API to access your data from the server. 
  • It offloads processing from the server to the browser, meaning users with slower computers or limited available memory may have a worse experience. 
  • Without extra effort, it will produce websites that are SEO-unfriendly, or difficult to bookmark and share.
  • It often requires multiple AJAX requests to build a single page, which are visible to the user as the page loading piecemeal. While subsequent requests are fast, initial load times can be longer.

The server-side approach

In a server-side approach, all markup/html is generated on the server and returned to the client (browser) in a single request/response cycle. The server is responsible for everything that goes into creating what the user sees. For MVC, this means that all three parts of the pattern take place on the server. The user enters a url or submits a form, the controller interprets it and gets the data from the model and inserts it into the view which is sent back to the user’s browser. The approach is simple to understand and requires relatively little to get started. 

Issues with the server-side approach

The cons to the server side approach are obvious. Specifically, it provides a less “reactive” user experience. Traditionally, you would have to wait for the entire page to reload before you could see changes to the state of your application, or receive feedback from a form submission. And while you could use javascript to enhance the reactivity of the page, organizing this code quickly became difficult. In order to update a section of the page, you often had to duplicate some logic from the server-side on the client side. “Spaghetti code” was the result.

The server-side approach with UI Blocks

UI Blocks aims to address some of the issues commonly associated with adding reactivity to server-side web applications, specifically:

  • Where do I put my AJAX code and how do I keep it organized? (AJAX “endpoints”)
  • How do I avoid duplicating my view/presentation logic from the server side in my javascript code?
  • How do I make a part of the page refresh, while still including it as part of the initial page load?

UI Blocks aims to do this without the extra complexity and overhead of moving to a fully client-side solution. Benefits include:

  • Simplicity. No pre-processing or compiling required. Just pure html, css, javascript, and php.
  • Low overhead
  • No build process necessary locally or on the server
  • SEO friendly (because markup is generated on the server)
  • No extra API layer required (Separation of concerns is achieved without separating the server from the markup)
  • Pages load together initially in a single http request, with the option of reloading individual page components/blocks separately as needed
  • Can be easily cached and output as a static site
  • Compatible with progressive enhancement, so that an application can be built to work even with javascript disabled.

Summary

The choice between client-side and and server-side is one of using the right tool for the right job. For highly dynamic applications, the benefits of a javascript framework like Vue.js will outweigh the extra complexity and overhead. But for a regular marketing website or simple application that is primarily informational, a server-side approach with UI Blocks may give you and your users the best experience.