This project demonstrates usage of AngularJS to build simple portal system with floating panels. Drag'n'drop is supported thanks to usage of JQuery UI. To support proper binding between JQuery UI Sortable interface and AngularJS models I created custom widget which looks at DOM changes done by JQuery and map them properly back to AngularJS. Also after any change in the model all the panels are re-created.

To make everything clean and easy to maintain I decided to use JSON for the models. Except having benefit of being widely known and easy it also allow us to use great AngularJS service component called $resource. Using $resource service we can load any model directly from the server and save it back with one line of code.

Bootstrap, from Twitter were used to create documentation/demo page. Possibly soon - when I'll improve the widget - it will be used also for panels styling.


Move panels around and try to edit JSON model to check if changes are properly propagated in both directions.

To understand how this component works you need to know AngularJS, at least a little. In case of any doubts please look at AngularJS documentation. The structure of the code is typical to every AngularJS application. First of all, we need controller which will also contain our JSON model:

var PortalController = function($resource) {
  var scope = this;

  scope.portalModel = $resource('app/models/:model', {model:'default.json'}).get();

  scope.$watch('portalModel', function() { 
    scope.portalModel.$save();
  });
};

PortalController.$inject = ['$resource'];

As you can see it's very easy to create model with full RESTful backend. For more extensive documentation on how to use $resource service look at $resource documentation. If you need to take any additional actions connected with model change just put them inside the watcher. Please keep in mind that you are not forced to use any RESTful backend even if it's so easy. You can for example use plain javascript object as a model and skip any saving:

var PortalController = function() {
  this.portalModel = {
    name: "Some name", 
    layout: [
      [
        { header: "Header 1", content: "Content 1" },
        { header: "Header 2", content: "Content 2" }
      ], 
      [
        { header: "Header 3", content: "Content 3" },
        { header: "Header 4", content: "Content 4" }
      ]
    ]
  }
};

Ok, we have a proper controller, now it's time for our portal widget itself:

<div ng:app ng:controller="PortalController">
  <div ui:panels model="portalModel">
</div>

The only interesting thing which you can see there is custom tag ui:panels which is a marker for our portal widget. model property is set to the name of our model (which should be in JSON format).

The last thing which we need to do is to create JSON file with model:

{
  "name": "Name of your portal model",
  "layout" :
  [
    [
      { 
        "header": "Panel 1", 
        "content": "This is first panel, displayed in top left corner." 
      },
      {  
        "header": "Panel 2", 
        "content": "This is second panel, displayed in bottom left corner." 
      }
    ], [
      { 
        "header": "Panel 1", 
        "content": "This is first panel, displayed in top right corner." 
      },
      {  
        "header": "Panel 2", 
        "content": "This is second panel, displayed in bottom right corner." 
      }
    ]
  ]
}

As you can see structure looks like that:

layout: [
    column [
        panel { header, content },
        panel { header, content },
        panel { header, content }
    ],
    column [
        panel { header, content },
        panel { header, content },
        panel { header, content }
    ]
    column [
        panel { header, content },
        panel { header, content },
        panel { header, content }
    ]
]

Both header and content are strings and except plain text can also contain HTML tags. HTML will be rendered properly inside the panel. Right now there is no limit for columns or panels number.

# Description
1 Improve css styles for panels
2 Add option to specify url as panel content (should be loaded dynamically)
3 Add options to minimize, close and pin the panel
4 Clean up the project structure to clearly separate presentation layer from library itself
If you have any questions, suggestions, or need to contact me for and other reason just hit me on Twitter: