I'm developing a simple HTML editor. The main idea is to have "default theme for a layout" and components can override theme's values. To manage theme's and component's styles I've created simple isolated directive which accept object and index. The main problem is, when the component do not has a style, the input tag is empty and it's logically. Is there a way to do differently?
For example, If the component do not has a style then inside directive it would display theme's value, but on change it would modify component's style, not theme's.
If this architecture is not an angular way, then could you advice other approach?
Thank you.
Plunker
Here is code snippet:
<!DOCTYPE html>
<html>
<head>
<title>Theme</title>
</head>
<body>
<div data-ng-app="myApp" data-ng-controller="defaultCtrl" style="margin: 100px 100px">
<ol>
<li data-ng-repeat="component in document" data-ng-style="m(theme, component.style)" data-ng-controller="componentController" data-ng-init="init(component)">
{{ theme }}
{{ component }}
{{ component.content }}
<button data-ng-click="editComponent()">Edit component</button>
</li>
</ol>
<button data-ng-click="editTheme()">Edit theme</button>
<div id="editor"></div>
<script type="text/ng-template" id="component.html">
<p>Component</p>
<border-editor ref-obj="component.style" ref-index="border"></border-editor>
</script>
<script type="text/ng-template" id="theme.html">
<p>Theme</p>
<border-editor ref-obj="theme" ref-index="border"></border-editor>
</script>
</div>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("myApp", []);
app.controller("defaultCtrl", ["$scope", "$templateCache", "$compile", function($scope, $templateCache, $compile) {
$scope.theme = {
border: "1px solid red"
}
$scope.document = [
{
content: "First block",
style: {
border: "1px solid green"
}
},
{
content: "Second Block",
style: {}
},
{
content: "Third block",
style: {}
}
];
$scope.m = function() {
var merged = {},
args = arguments.length;
for (var i = 0; i < args; i++) {
for (var k in arguments[i]) {
if (arguments[i].hasOwnProperty(k) && arguments[i][k] !== undefined) {
merged[k] = arguments[i][k]
}
}
}
return merged;
}
$scope.editTheme = function() {
$scope.createEditor($scope, 'theme.html');
}
var editorScope;
function createEditor(scope, tpl) {
if(editorScope) {
editorScope.$destroy();
}
var tpl = $templateCache.get(tpl);
editorScope = scope.$new();
var content = $compile(tpl)(editorScope);
$("#editor").html(content);
}
$scope.createEditor = createEditor;
}]);
app.controller("componentController", ["$scope", function($scope) {
$scope.init = function(component) { $scope.component = component; }
$scope.editComponent = function() {
$scope.createEditor($scope, 'component.html');
}
}]);
app.directive("borderEditor", function() {
return {
restrict: "E",
scope: {
refObj: "=",
refIndex: "@"
},
template: '<input data-ng-model="refObj[refIndex]" />',
link: function(scope, el, a) {}
}
})
</script>
</body>
</html>
OK. I found a solution. I used ng-model in conjunction with $parsers and $formatters properties.
I advise everyone who do not understand how the ng-model works - read this article.
Here is updated plunker.
app.directive("borderEditor", function() {
return {
restrict: "E",
scope: {
ngModel: "=",
alternate: "="
},
require: "^ngModel",
template: '{{ngModel || "undefined" }} <input data-ng-model="border"/>',
link: function(scope, el, a, ngModel) {
ngModel.$formatters.push(function(modelValue) {
return !modelValue ? scope.alternate : modelValue;
});
ngModel.$parsers.push(function(viewValue) {
return viewValue;
});
ngModel.$render = function() {
scope.border = ngModel.$viewValue;
}
scope.$watch("border", function(newValue, oldValue) {
if(newValue != oldValue) {
ngModel.$setViewValue(newValue);
}
});
}
}
})