Constraint based layout paradigm for AngularJS web applications inspired by Apple's Auto Layout for iOS and OS X.
HTML and CSS have been designed to present a page style layout like one that you might find on a newspaper. However, nowadays those technologies are also used to for layout of applications that should resemble native ones.
Many features that are needed to properly layout an application are missing from CSS. For example, there is no way to specify that two elements on a page should have the same height!
With angular-autolayout, you can use the same layout technology that Apple gives to native iOS and OS X developers for your HTML5 app.
Install by cloning the repository or via Bower:
bower install angular-autolayout
Add angular-autolayout to your imported scripts:
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-autolayout/dit/angular-autolayout.min.js"></script>
Require autolayout in your AngularJS app module:
angular.module("myApp", ["autolayout"]);
You can now use the al-constraint directive to add layout constraints with both Appple's Visual Format Language or by specifying parameters:
<div id="myContainer">
<div id="myLeftBox">Hello</div>
<div id="myRightBox">Layout!</div>
<al-constraint align="top">|-[myLeftBox(==myRightBox)]-[myRightBox]-|</al-constraint>
<al-constraint>V:|-[myLeftBox]-|</al-constraint>
<al-constraint
element="myLeftBox"
attribute="height"
relation="equal"
to-element="myRightBox"
to-attribute="height"></al-constraint>
</div>
Or programmatically in your controller:
angular.module("myApp").controller("myController", function(autolayout) {
autolayout(document.getElementById("myContainer"))
.addConstraint({
element: document.getElementById("myLeftBox"),
attribute: "right",
toElement: document.getElementById("myRightBox"),
toAttribute: "left",
relation: "equal",
constant: -10
});
});
Angular-Autolayout is made to replicate Apple's Auto Layout for the web. The Auto Layout Guide provides some useful documentation that is relevant for this project. Refer especially to the Visual Format Languagee documentation as it has been closely adopted.
The preferred way of usage of angular-autolayout is via the provided directives:
al-constraint
The al-constraint is the core directive that will add layout constraints to its sibling elements relative to its parent element.
It can be used with both visual language format constraints:
<al-constraint visual-format="V:|-[myDiv]-|"></al-constraint>
or equivalent:
<al-constraint>V:|-[myDiv]-|</al-constraint>
Or it can be used in a form that will map to the programmatic API:
<al-constraint
element="myLeftBox"
attribute="height"
relation="equal"
to-element="myRightBox"
to-attribute="height"
multiplier="2"
constant="10"
priority="100"></al-constraint>
See examples for practical usages of this and other directives.
al-update-on
This is an attribute directive to be used on an autolayout container element. It expect a value that identify the name of an event upon which the autolayout should update an re-materialize. For example, in your view:
<div id="containerDiv" al-update-on="myEvent">
<div id="myBox">Content</div>
<al-constraint>|[myBox]|</al-constraint>
</div>
In you controller:
angular.element("#containerDiv").css("width", "50%");
scope.$broadcast("myEvent"); // This will trigger an update on the containerDiv's autolayout
al-autolayout-on-resize
The al-autolayout-on-resize attribute directive will update the autolayout of the container element it's defined on upon window resize. Because the update of an autolayout affects all the child autolayouts, this directive needs to be used only on the topmost autolayout in a hierarchy. For example:
<div id="containerDiv" style="width:100%" al-update-on-resize>
<div id="myBox">Content</div>
<al-constraint>|[myBox]|</al-constraint>
</div>
The only injectable service exposed is called autolayout. With it you can access to all the functionalities provided by the al-constraint directive via code.
API
-
autolayout(containerElement)(constructor)Creates a new autolayout object and attach it to the given DOM element. It accepts both
angular.elements or plain DOM elements such as those retrieved bydocument.getElementById. An instance created with this constructor will be referenced with<autolayout>in the rest of this document. -
<autolayout>.addConstraint(constraint, options)This is the main method of the library. It adds a constraint to the current layout and immediately applies it. It returns an
arrayof constraints objects that can be used to remove specific constraints withremoveConstraint. A single constraint object will resolve in the constraint expression:element.attribute <relation> toElement.toAttribute * multiplier + constant
The parameters are:
constraint
- A
stringin Visual Format Language that will result in the generation of one or more constraints; - An
objectwith the following keys:elementis the first DOM element affected by the constraint. It can benullto refer to the container element used in the constructor. This element should either be the container element or a direct child of it;attributecould be a string referring to an attribute converter like "left". It can also be a function, in which case it will be treated as acreateconverter's function receiving element, containerElement and solver. If a function is provided, it should implement the caching behavior ofprovider.expressionForElementAttributeby itself;toElementis the second DOM element affected by the constraint. It can also benullbut only ifelementis specified;toAttribute, likeattributeis the attribute converter for thetoElement;multiplierdefaults to 1.0 and it's applied to the generated constraint expression as shown above;constantdefaults to 0 and it's applied to the generated constraint expression as shown above;priority, if specified, makes the constraint not required and will respect other constraints priorities in order to resolve conflicting layout requests.
options
Options are currently considered only if the
constraintparameter is a visual format language string. The available options in this case is:alignshould be an attribute converter to be used in equation constraints between child elements found in a visual format language string. This will provide a convenient way to add such common constraints. See the examples for a more obvious explanation.
- A
-
<autolayout>.removeConstriant(constraint)Removes a constraint object or all the constraints in an array.
-
<autolayout>.materialize()Materialize all the constrained variables values into CSS layout attributes. This method is automatically called when adding a new constraint.
-
<autolayout>.update()Some container element's attributes may be updated with external values as they are never materialized by an autolayout having that element as it's container. When that happen, this method will update those values using converters'
prepareUpdateandupdatefunctions and re-materialize the updated constraints values. This operation will be recursively applied to child autolayouts instances. -
<autolayout>.destroy()Destroy the autolayout instance and removes all the added data from the DOM elements.
The autolayout service has an autolayoutProvider that can be injected in a module's config phase:
angular.module("myModule", ["autolayout"]).config(function(autolayoutProvider) {
// Configure autolayoutProvider here
});
The autolayoutProvider allows your to configure many part of how the autolayout service behaves. The accessible configuration properties are:
standardSpace: default to 8, it is the number of pixels for the standard spacing used by visual language specified constraints such as|-[ElemId]-|. The spaces added by|-and-|depends by this parameter.relations: is a map of relation names to functions used to resolve therelationparameter of a constraint. By default, equal, lessOrEqual and greaterOrEqual relations are defined. A relation is defined as afunction (leftExp, rightExp, priority)that returns a new Cassowary.js constraint such asc.Equationorc.Inequality.attributeConverters: a map of attribute names to objects to resolveattributeandtoAttributeparameters of a constraint. By default, the top, left, bottom, right, height, width, centerX and centerY converters are defined. A converter object should define the following keys:create: afunction (element, parentElement, solver)that should return a Cassowary.js variable or expression;materialize: afunction(element, expression)that receives the expression generated bycreateand should apply the proper value to the provided element's layout style;update(optional): afunction (containerElement, expression, solver)called by the serviceupdatemethod that should callsolver.suggestValueto update fix constraint variables;prepareUpdate(optional): afunction(expression, solver)called to prepare the solver to edit the given expression. The default implementation callssolver.addEditVaron the expression.
autolayoutInstanceDataKey: default to "$autolayout" is the key used to store the autolayout instance in a container element's data;autolayoutContainerElementExpressionsDataKey: default to "$autolayoutContainerExpressions" is the key used to store created autolayout expressions in a container element's data;autolayoutChildElementExpressionsDataKey: default to "$autolayoutExpressions" is the key used to store created autolayout expressions in a child element's data.
The provider also exposes methods that are used to interact with attributeConverters:
expressionForElementAttribute: is afunction(propertyName, element, containerElement, solver)that can be used to execute thecreatefunction of a converter with the given property name, returning a Cassowary.js expression or variable. This method cashes requested expressions in the element's data usingautolayoutChildElementExpressionsDataKeyorautolayoutContainerElementExpressionsDataKey; this provides the desired property of always referring to the same Cassowary.js expression for a given element's attribute. To see how this method can be used in a custom attribute converter, take a look at the providedwidthconverter in the sources;materializeExpressionValue: is afunction(propertyName, element, expression)that has no expected return value and can be used to execute thematerializefunction of a converter.
Cassowary.js
Cassowary.js is included and provided as a constant via the injectable cassowary. Being it a constant, it can also be injected in the config phase to be used within autolayoutProvider's configuration parameters.
To setup the project, you'll need a working node environment with NPM, Bower and Grunt. When that is set up execute the following commands in a shell within the project directory:
npm install
bower install
There are a number of Karma based unit tests using Mocha, Chai and Sinon. To run them use:
grunt test
Building the project will generate a packaged angular-autolayout.js and it's minified version in the dist directory that are already provided for convenience. Build with:
grunt build
This project uses Cassowary.js and PEG, any contribution to those projects will help this project too.
The first thing you could do to partecipate to angular-autolayout is to use it in your project. By doing that you'll be more likely to find fixes or improvements that may be needed.
Some tasks that can be foreseen are:
- There is a workaround (that should be handled more nicely) to fix an exception thrown by Cassowary.js when the
endEditmethod of the solver is called rapidly by theal-update-on-resizedirective; - Fix bugs that arise with usage;
- Better error reporting;
- Support for responsive layouts?;
- Extend the functionalities provided by
angular-autolayoutto fit more specific needs; - Use the (not very documented) power of Cassowary.js to allow more freedom in constraints creation;
- Performance profiling and fine tuning;
- A twin module that provides an Interface Builder like experience in creating constraints.
The MIT License (MIT)
Copyright (c) 2014 Nicola Peduzzi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.