Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A solution for two-binding between angular and a style sheet

I know this sounds silly but I'm writing a wysiwyg editor that allows Designers to create style guides. I've become very fascinated with 2 way binding in angular and was curious if it was possible to two-way bind between a css sheet and a ng-model input field. Currently, I'm making a dynamic style guide that allows a designer to colorpick a primary, secondary colors of a page. These color will change the entire theme of the site uniformly and it would be awesome to do this from the stylesheet itself.

HTML

<input type="text" ng-model="color.primary" />
<button class="btn primary-color" />

CSS

.primary-color {
  background: {{color.primary}};
}

js $scope.color {primary: '00f', secondary: '#e58'}

I know there are plenty of directives like ng-style and ng-class But I fear that every tag will have to be a directive because everything could have an ng-style/ng-class tag. Thus making my code not very dry and hard to maintain.

What if I wanted a dynamic style guide of css. A sheet that I could store as key value pairs of CSS into server like firebase, perhaps even 3way bind the changing of colors in real time? I'm pretty sure this cannot be accomplished using solely angular... would anyone have any ideas on pre compilers or hacks to accomplish this task so that it would result in one clean style guy?

like image 208
Armeen Harwood Avatar asked Mar 14 '14 21:03

Armeen Harwood


1 Answers

This was pretty fun to work on.

You have access to all of the styles on a page through document.styleSheets, so you just need scope the rules on a style. So lets say that I have this class:

.class {
    font-size: 20px;
    color: blue;
}

How jsfiddle implements sheets, this is the third style sheet added to the document, so I can just assign to the scope like this:

myApp.controller('TestController', ['$scope', function ($scope) {
    $scope.styles = document.styleSheets[3].rules;     
}]); 

This would let you do things like $scope.styles[0].style['color'] = 'red' to change the color of anything with class to red. Since it's the first thing in the style array.

But that's not cool enough, so we want to create a directive where we can change these from a ui. So we'd like to know all of the things that class controls, to create controls for them, So we can manipulate the css string to get all of those.

Next we have to create a temporary scoped object on the directive that starts off with all of the styles. The reason is that style sheets have checking, so as you type into an input if you do something like $scope.styles[0].style['color'] = 'g' and it was red, it will just reset to red.

So we create an input for each style type, with ng-model of our temp, then just listen for changes and attempt to assign to the style sheet.

I created a fiddle where I implement it, but the directive looks like this.

myApp.directive('styler', function() {
    return {
        scope: {
            styles: '='
        },
        restrict: 'EA',
        template: '<div ng-repeat="type in types">{{type}} <input ng-change="Change(type)" ng-model="temp_styles[type]"/></div>',
        link: function(scope, elt, attrs) {      
            // get the actual styles
            var types = scope.styles.cssText.replace(/ /g, '').split('{')[1].split('}')[0].split(';');
            scope.types = [];
            scope.temp_styles = {};
            // get rid of "" element at the end
            types.pop();
            for (var i in types) {
                var split = types[i].split(':');
                scope.types.push(split[0]);
                scope.temp_styles[split[0]] = split[1];
            }
            scope.Change = function(type) {
                scope.styles.style[type] = scope.temp_styles[type];
            };
        }
    };
});

Cool, dynamic two way binding of styles!

Hope this helped!

like image 113
hassassin Avatar answered Sep 22 '22 23:09

hassassin