Skip to content

Instantly share code, notes, and snippets.

@consoledotblog
Last active August 29, 2015 13:57
Show Gist options
  • Save consoledotblog/9486040 to your computer and use it in GitHub Desktop.
Save consoledotblog/9486040 to your computer and use it in GitHub Desktop.
AngularJS - Responsive Transclusion
<!-- Simple directive template -->
<!-- This will always be hidden -->
<div class="sampleDirectiveTemplate" ng-if="false">
<!-- ng-src will never be processed and the image won't download -->
<img ng-src="pic.png" />
</div>
<!-- Directive template that assumes a shouldShow property is defined on the model -->
<!-- This will always be hidden if our directive code tells it to be -->
<div class="sampleDirectiveTemplate" ng-if="responsiveDirective.shouldShow">
<!-- ng-src will not download unless shouldShow equates to false -->
<img ng-src="pic.png" />
<!-- ng-transclude and any content meant to be transcluded will
not be processed if shouldShow equates to false -->
<div ng-transclude></div>
</div>
/* Mappings to the twitter bootstrap 3 breakpoints */
var breakpoints = [
{name: "xs", lowPixel: 0, highPixel: 767},
{name: "sm", lowPixel: 768, highPixel: 991},
{name: "md", lowPixel: 992, highPixel: 1199},
{name: "lg", lowPixel: 1200, highPixel: Infinity}
];
/* Reading attributes from a directive */
function controller($scope, $element, $attrs) {
...
var includedatArray = $attrs.includedat ? JSON.parse($attrs.includedat) : undefined;
var selectedBreakpoints = breakpoints.filter(function(breakpoint) {
return _.contains(includedatArray, breakpoint.name);
});
...
}
/* Reading css classes from a directive */
function controller($scope, $element, $attrs) {
...
var classArray = $attrs.class.split(" ");
var selectedBreakpointsByClass = _.clone(breakpoints),
breakpointNamesToShow = _.chain(classArray)
.intersection(bootstrapVisibleClasses)
.map(function(visibleClass) {
return visibleClass.split("-")[1];
})
.value(),
breakpointNamesToHide = _.chain(classArray)
.intersection(bootstrapHiddenClasses)
.map(function(hiddenClass) {
return hiddenClass.split("-")[1];
})
.value();
//hidden-xx > precendence than visible-xx in twbs.
//If there is a hidden class present, visible classes don't matter.
if(breakpointNamesToHide.length > 0) {
selectedBreakpointsByClass = _.filter(selectedBreakpointsByClass, function(bp) {
if(_.contains(breakpointNamesToHide, bp.name)) {
return false; //want to hide this one
}
return true;
});
}
else if(breakpointNamesToShow.length > 0) {
selectedBreakpointsByClass = _.filter(selectedBreakpointsByClass, function(bp) {
if(_.contains(breakpointNamesToShow, bp.name)) {
return true; //want to show this one
}
return false;
});
}
...
}
/* Call a method 'handleResize' whenever the viewport size changes */
angular.element(window).on('resize', handleResize);
/* Responsive directive */
angular.module('myApp.responsiveDirective', [])
.directive('responsive', function($timeout) {
/**
* These match up with the twitter bootstrap 3 breakpoints
* @type {Array}
*/
var breakpoints = [
{name: "xs", lowPixel: 0, highPixel: 767},
{name: "sm", lowPixel: 768, highPixel: 991},
{name: "md", lowPixel: 992, highPixel: 1199},
{name: "lg", lowPixel: 1200, highPixel: Infinity}
];
var bootstrapVisibleClasses = ["visible-xs", "visible-sm", "visible-md", "visible-lg"],
bootstrapHiddenClasses = ["hidden-xs", "hidden-sm", "hidden-md", "hidden-lg"];
function getSelectedBreakpointsByTWBSClasses(classArray) {
var selectedBreakpointsByClass = _.clone(breakpoints),
breakpointNamesToShow = _.chain(classArray)
.intersection(bootstrapVisibleClasses)
.map(function(visibleClass) {
return visibleClass.split("-")[1];
})
.value(),
breakpointNamesToHide = _.chain(classArray)
.intersection(bootstrapHiddenClasses)
.map(function(hiddenClass) {
return hiddenClass.split("-")[1];
})
.value();
//hidden-xx > precendence than visible-xx in twbs. If there is a hidden class present, visible classes don't matter.
if(breakpointNamesToHide.length > 0) {
selectedBreakpointsByClass = _.filter(selectedBreakpointsByClass, function(bp) {
if(_.contains(breakpointNamesToHide, bp.name)) {
return false; //want to hide this one
}
return true;
});
}
else if(breakpointNamesToShow.length > 0) {
selectedBreakpointsByClass = _.filter(selectedBreakpointsByClass, function(bp) {
if(_.contains(breakpointNamesToShow, bp.name)) {
return true; //want to show this one
}
return false;
});
}
return selectedBreakpointsByClass;
}
function getSelectedBreakpointsByAttribute(includedatArray) {
var selectedBreakpoints = breakpoints.filter(function(breakpoint) {
return _.contains(includedatArray, breakpoint.name);
});
return selectedBreakpoints;
}
/**
*
* If there is an includedat attribute, the directive will use that to determine where this directive's
* content appears at.
* If there is no attribute, the directive will look for twbs hidden/visible classes and use those
* to determine the inclusion of content.
* @param $scope
* @param $element
* @param $attrs
* @param $attrs.includedat {Array} Array of strings matching the twitter bootstrap breakpoint keys
*/
function controller($scope, $element, $attrs) {
$scope.responsiveDirectiveModel = {
shouldShow: false
};
var selectedBreakpoints,
includedatArray = $attrs.includedat ? JSON.parse($attrs.includedat) : undefined;
if(includedatArray) {
selectedBreakpoints = getSelectedBreakpointsByAttribute(includedatArray);
} else {
selectedBreakpoints = getSelectedBreakpointsByTWBSClasses($attrs.class.split(" "));
}
/**
* Called everytime the window is resized. The current window width is checked against the breakpoints
* specified via the 'includedat' attribute on the directive.
*/
function handleResize() {
console.log('shouldshow: ' + $scope.responsiveDirectiveModel.shouldShow);
var originalShouldShow = $scope.responsiveDirectiveModel.shouldShow,
newShouldShow;
//Check to see if any of the selected breakpoints contain the current width
var currentWidthMatchesASelectedBreakpoint = _.some(selectedBreakpoints, function(breakpoint) {
if((window.innerWidth >= breakpoint.lowPixel) &&
(window.innerWidth <= breakpoint.highPixel)) {
return true;
}
});
//Match occurs, so load up the templateid passed in through an attribute
if(currentWidthMatchesASelectedBreakpoint) {
newShouldShow = true;
} else {
newShouldShow = false;
}
if(originalShouldShow !== newShouldShow) {
$timeout(function() {
$scope.responsiveDirectiveModel.shouldShow = newShouldShow;
});
}
}
angular.element(window).on('resize', handleResize);
//kick one check off right away
handleResize();
}
return {
restrict: 'A',
templateUrl: 'directives/responsive/responsive.tpl.html',
replace: true,
transclude: true,
scope: true,
controller: controller
};
});
<!-- Using the responsive directive with attributes -->
<div responsive includedat="['xs', 'lg']">
<!-- This will only be shown and processed in extra small and large viewports -->
<img ng-src="pic.png" />
</div>
<!-- using the responsive directive with twos classes -->
<div responsive class="visible-xs visible-lg">
<!-- This will only be shown and processed in extra small and large viewports -->
<img ng-src="pic.png" />
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment