Building fully dynamic web apps from independent components

#1

Hello everyone,

I’ve had some success with turning my browser into a desktop window (i.e. the same you would get if you used Qt, Swing or .NET’s UI controls. In a nutshell, I publish a web page which contains an empty body element plus a header which opens a WebSocket connection to the server plus a short piece of JavaScript which accepts commands from the server. Example commands are: loadScript(url) to load arbitrary JavaScript into the page and setHtml(id, html) to modify the page just like jQuery and $(’#id’).html(html); would do.

That works pretty well. It allows me to quickly prototype my app in Python. When I see a performance issue, I can create some JavaScript and push that to the client to optimize round trip times and responsiveness.

I’ve been using Flask for this. Migrating my code to Autobahn was easy enough. But as the application grows, I see unwanted dependencies which creep into the code and the design. What I want is to tell the framework “put a text editor into the UI”. The framework should then find a text editor component, deploy it (HTML, JavaScript, CSS), allow me to configure it and put it into the DOM.

I’d like to use Web Components for this. Polymer looked like a good starting point. But I got stuck early with two problems:

  1. The Polymer code expects me to deploy dozens of static files on the HTTP server. That feels like a bad solution. I can wire my components using RPC and PubSub but when it comes to deliver CSS, I have to become root, copy files around, … I need a solution where an application component can talk to the static HTTP server and register additional resources.

So I need a HTTP server which I can configure via RPC to publish additional resources.

  1. When the Polymer component is initialized, it is wired with its template. That allows data binding between JavaScript code and HTML element attributes and content. It looks nice. But I need a way to publish these events via RPC or PubSub. The code itself isn’t an issue but since I load those components dynamically, I need a way to inject the current Crossbar connection or session into them while or after they have been created. Note: At this time, I know that such a session must exist since an application component (currently connected to the router) called some method (via RPC) to add the Polymer component to the UI.

The only solution which I currently see is a global JavaScript variable - which I don’t like for the obvious reasons. Creating a second session seems too expensive, especially when the UI is made from dozens of components. Maybe a component could ask for a “shared” connection from AutobahnJS. If there is no connection yet, a new one is created. If there is one, then the connection is shared.

To sum it up, I want to create a plugin system for web applications where each plugin does a specific job without imposing itself on any other plugin. An application would discover the existing plugins from a plugin manager (via RPC). One such plugin could be a chat service. This plugin would offer connectors for an authentication plugin. A “chat UI” plugin would connect to the chat service to discover users, channels and subscribe to topics. It would use the authentication plugin to login a user.

A log service could collect log messages from any number of plugins in a central NoSQL database. A text indexing plugin could make this data searchable. A log UI would use both to show log data in real time or to analyze historical events. A monitor UI would use certain indicators to discover the system’s health from the log service’s data.

The goal is that each such plugin would only contain the minimum part of the UI. The log UI doesn’t know about menus. It can just display the log data in a table. The app would load the plugin, create a menu bar and attach menu items which would do RPC calls to the log UI.

As a first step, it won’t be possible to style all the plugins consistently. This is more a proof-of-concept right now. But I think with a dynamic HTTP server, it might be possible to chain CSS files. The plugin brings one, then the app can append it’s special rules and a theme plugin can modify that further. LESS might help, too, by defining variables and hooks for themes to quickly create a common look and feel.

Comments?

0 Likes

#2

Hi,

thanks for your detailed description of your actual goals and context. From the GH issue I didn’t get it. E.g. why on earth would I want sth that generates the HTML, CSS for a Polymer component dynamically. Ok. Your are approaching new land with the stuff you describe;)

Anyway … In this response I only touch one piece: a Webserver for static resources where the resources can be setup dynamically via RPC. This is useful in itself, and could be done quite easily.

Here is a first idea for API of such thing:

webserver.create_resource(size) : resource_id

webserver.append_resource(resource_id, bytes): remaining

webserver.publish_resource(resource_id, url, mime_type): ok

The webserver is still a static one, but its resources can be created dynamically.

Is that what you had in mind?

···

Sent from Mobile (Google Nexus 5)

Am 01.02.2015 14:50 schrieb “Aaron Digulla” adig...@gmail.com:

Hello everyone,

I’ve had some success with turning my browser into a desktop window (i.e. the same you would get if you used Qt, Swing or .NET’s UI controls. In a nutshell, I publish a web page which contains an empty body element plus a header which opens a WebSocket connection to the server plus a short piece of JavaScript which accepts commands from the server. Example commands are: loadScript(url) to load arbitrary JavaScript into the page and setHtml(id, html) to modify the page just like jQuery and $(’#id’).html(html); would do.

That works pretty well. It allows me to quickly prototype my app in Python. When I see a performance issue, I can create some JavaScript and push that to the client to optimize round trip times and responsiveness.

I’ve been using Flask for this. Migrating my code to Autobahn was easy enough. But as the application grows, I see unwanted dependencies which creep into the code and the design. What I want is to tell the framework “put a text editor into the UI”. The framework should then find a text editor component, deploy it (HTML, JavaScript, CSS), allow me to configure it and put it into the DOM.

I’d like to use Web Components for this. Polymer looked like a good starting point. But I got stuck early with two problems:

  1. The Polymer code expects me to deploy dozens of static files on the HTTP server. That feels like a bad solution. I can wire my components using RPC and PubSub but when it comes to deliver CSS, I have to become root, copy files around, … I need a solution where an application component can talk to the static HTTP server and register additional resources.

So I need a HTTP server which I can configure via RPC to publish additional resources.

  1. When the Polymer component is initialized, it is wired with its template. That allows data binding between JavaScript code and HTML element attributes and content. It looks nice. But I need a way to publish these events via RPC or PubSub. The code itself isn’t an issue but since I load those components dynamically, I need a way to inject the current Crossbar connection or session into them while or after they have been created. Note: At this time, I know that such a session must exist since an application component (currently connected to the router) called some method (via RPC) to add the Polymer component to the UI.

The only solution which I currently see is a global JavaScript variable - which I don’t like for the obvious reasons. Creating a second session seems too expensive, especially when the UI is made from dozens of components. Maybe a component could ask for a “shared” connection from AutobahnJS. If there is no connection yet, a new one is created. If there is one, then the connection is shared.

To sum it up, I want to create a plugin system for web applications where each plugin does a specific job without imposing itself on any other plugin. An application would discover the existing plugins from a plugin manager (via RPC). One such plugin could be a chat service. This plugin would offer connectors for an authentication plugin. A “chat UI” plugin would connect to the chat service to discover users, channels and subscribe to topics. It would use the authentication plugin to login a user.

A log service could collect log messages from any number of plugins in a central NoSQL database. A text indexing plugin could make this data searchable. A log UI would use both to show log data in real time or to analyze historical events. A monitor UI would use certain indicators to discover the system’s health from the log service’s data.

The goal is that each such plugin would only contain the minimum part of the UI. The log UI doesn’t know about menus. It can just display the log data in a table. The app would load the plugin, create a menu bar and attach menu items which would do RPC calls to the log UI.

As a first step, it won’t be possible to style all the plugins consistently. This is more a proof-of-concept right now. But I think with a dynamic HTTP server, it might be possible to chain CSS files. The plugin brings one, then the app can append it’s special rules and a theme plugin can modify that further. LESS might help, too, by defining variables and hooks for themes to quickly create a common look and feel.

Comments?

You received this message because you are subscribed to the Google Groups “Autobahn” group.

To unsubscribe from this group and stop receiving emails from it, send an email to autobahnws+...@googlegroups.com.

To post to this group, send email to autob...@googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/autobahnws/a58002e1-31de-496d-aaa7-a9a3077532c8%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

0 Likes

#3

I’ve been thinking along the same lines. Actually, I want a web server where components can register themselves. So the server would publish a simple API via Crossbar and app components could say “if someone asks for this URL, then I’d like to contribute”.

The alternative would be that each app component brings it’s own web server and I build my app from URLs from different servers. But creating a secure web server is hard work.

So the first step would be a simple API to register RPC methods which should be called when the server receives a specific URL plus an API where I can attach static resources (i.e. a method where I pass a URL and a predefined response to avoid the RPC overhead).

That would allow many cool things:

  • Component Y could extend component X by asking for it’s resources, patching them and registering them under new names. If an application uses component X, they wouldn’t notice that Y even exists. And Y could be built on top of X without needing to have all files in a single project.

  • Components can be refreshed while the application is still running - the web server can send events when resources change and the app can listen to them to refresh components that have changed. Save CSS and see the new styles immediately.

  • I’ve been developing a non-save application: There is no save button. The internal data model is persisted as changes are being made. Everything can stop and restart at any point in time. My app could refresh it’s components without the user even noticing for small bug fixes or ask for bigger changes.

And this idea means that I’ve been looking in the wrong place: I don’t want to change Crossbar.io, I need a stand alone web server which uses Crossbar to allow components to attach via RPC and which publishes events for interested parties.

That could be applications wanting to refresh components or themes, it could be monitoring tools that try to detect attacks, it could be cluster monitoring which needs to decide whether to start new nodes as the load ramps up.

As for the API, I was thinking along these lines:

webserver.static_resource(path, bytes, content_type=None, cacheable=False, last_modified=None) : resource_id

Register a static resource that should be returned when a browser asks for path.

If the content_type is None, the type is determine from the name. *.html -> text/html, etc.

If last_modified is None, then the current time is used. This allows to tell clients whether a resource is unchanged after a component has restarted.

webserver.delete_esource(resource_id)

webserver.dynamic_resource(path, rpc_callback, is_prefix=False, omit_headers=False) : resource_id

Register a dynamic resource

rpc_callback is the name of a RPC method to be called with the arguments (path, parameters, request_headers)

path is the path part of the URL

parameters is the decoded query part of the URL (?name=value)

if is_prefix is true, then the rpc_callback will be called for any resource URL which starts with path

if omit_headers is true, then request_headers will be an empty dict (optimization)

The callback must return a JSON object with these properties:

content - string

response_headers - dict

If response_headers is empty, some useful defaults will be calculates based on the extension of the URL (mime type), no-cache, …

If the responses become huge, the JSON object could contain other flags which say “partial content”, causing the dynamic web server to loop.

···

On Monday, February 2, 2015 at 8:53:06 PM UTC+1, Tobias Oberstein wrote:

Hi,

thanks for your detailed description of your actual goals and context. From the GH issue I didn’t get it. E.g. why on earth would I want sth that generates the HTML, CSS for a Polymer component dynamically. Ok. Your are approaching new land with the stuff you describe;)

Anyway … In this response I only touch one piece: a Webserver for static resources where the resources can be setup dynamically via RPC. This is useful in itself, and could be done quite easily.

0 Likes