angular.module("app", []).controller('app', function ($log, $scope) {
$scope.layout = 'vertical';
$scope.toggle = function () {
if ($scope.layout === 'vertical') {
$scope.layout = 'horizontal';
} else {
$scope.layout = 'vertical';
}
};
}).directive("layout", function ($log, $parse) {
var link = function (scope, concreteElement, concreteAttributes, transcludeFN) {
var direction = $parse(concreteAttributes.layout);
var width = function (e) {
return Math.floor(e.outerWidth(true)) || 0;
};
var height = function (e) {
return Math.floor(e.outerHeight(true)) || 0;
};
var wrapper = concreteElement.find(".wrapper");
var nw = null;
var ctr = null;
var se = null;
var verticalOrientation = true;
var doLayout = function () {
nw.css("left", "0px").css("top", "0px").css("bottom", "").css("right", "");
se.css("bottom", "0px").css("right", "0px").css("left", "").css("top", "");
ctr.css("left", "").css("right", "").css("bottom", "").css("top", "");
verticalOrientation = direction(scope) === 'vertical';
if (verticalOrientation) {
nw.css("right", "0px");
ctr.css("left", "0px").css("right", "0px");
se.css("left", "0px");
ctr.css("top", height(nw) + "px");
ctr.css("bottom", height(se) + "px");
} else {
nw.css("bottom", "0px");
ctr.css("top", "0px").css("bottom", "0px");
se.css("top", "0px");
ctr.css("left", width(nw) + "px");
ctr.css("right", width(se) + "px");
}
};
// transclude the contents of the element and associate the parent scope with the
// transcluded elements
// the clone parameter is actually the set of nodes directly below the node with the directive, and
// each of the nodes has been compiled and linked already.
// in thise case, we're going to use the scope's parent scope to for the linking
transcludeFN(scope.$parent, function (clone) {
nw = $(clone.filter(".position-north, .position-west").get(0));
ctr = $(clone.filter(".position-center").get(0));
se = $(clone.filter(".position-south, .position-east").get(0));
nw.css("position", "absolute");
ctr.css("position", "absolute");
se.css("position", "absolute");
// choose the right set of nodes from all those that have been cloned, compiled, and linked
// we need to append to the concreteElement, because it might be different from the templateElement
// when inside a ng-repeat (lots of examples get this wrong)
wrapper.append(nw);
wrapper.append(ctr);
wrapper.append(se);
});
scope.$watch(function () {
return width(nw);
}, doLayout);
scope.$watch(function () {
return height(nw);
}, doLayout);
scope.$watch(function () {
return width(se);
}, doLayout);
scope.$watch(function () {
return height(se);
}, doLayout);
scope.$watch(function () {
return width(wrapper);
}, doLayout);
scope.$watch(function () {
return height(wrapper);
}, doLayout);
scope.$watch(direction,
doLayout);
};
var compile = function (templateElement, templateAttributes, transcludeFN) {
// here, you can modify the templateElement and templateAttributes;
// the compile function returns the linking function; we're going to "transclude"
// in the linking function and not in the compile function
return function (scope, concreteElement, concreteAttributes) {
link(scope, concreteElement, concreteAttributes, transcludeFN);
};
};
return {
// it's probably good practice to ALWAYS introduce a scope when transcluding and using scope.$parent
// when applying the transclusion function
scope: true,
restrict: 'A',
compile: compile,
transclude: true,
// use replace to swap out the node that has this directive with the node generated by the template;
// this isn't really necessary
template: "<div class='wrapper' style='width: 100%; height: 100%; position: relative'></div"
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>AngularJS Template</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.4/angular.js"></script>
<script src="app.js"></script>
</head>
<body><div ng-controller='app' style='height: 100%' ng-click='toggle()'>
<!-- for testing, i've added a repeater, because if you're using the wrong element for linking it'll become apparent -->
<div layout="layout">
<!-- south and east are the same component -->
<div class='position-south'><span ng-show='layout==="vertical"'>South</span><span ng-hide='layout==="vertical"'>East</span>
</div>
<!-- north and west are the same component -->
<div class='position-north'><span ng-show='layout==="vertical"'>North</span><span ng-hide='layout==="vertical"'>West</span>
</div>
<div class='position-center'>Center</div>
</div>
</div></body>
</html>
html, body, [layout] {
height: 100%;
width: 100%;
}
.wrapper {
background-color: red;
}
.position-north, .position-west {
background-color : blue;
}
.position-south, .position-east {
background-color: green;
}
.position-center {
background-color: yellow;
}