If you haven't already seen Sir Trevor JS, then you must take a look now! It allows the user to insert blocks of different types, with a really nice interface.
There are many benefits to this approach in a responsive site, as the content types are separated using json and can be outputted in rows. I have used Sir Trevor JS along with Wagtail Streamfield, which does a similar thing.
But what if we want the same experience on an Angular project? well unfortunately the only version available is a wrapper around the main version, without proper Angular integration. I decided to write my own which could support the integration better. Here is how I did it.
First off we need to include angular.js in the page and add the ng-app to the html tag. Now we can create a controller for the editor with some default data:
Now we have some data we need assign the controller and add the loop which will output the blocks in the html page:
Note that we also included a directive attribute called 'block'. This is going to be a dynamic directive which changes the block type based on the data. Lets add the directive and a function to the controller to handle it:
In this updateType function we are finding the template based on the block type. But we are missing templates for the block types. Lets add those to the html:
Now the blocks are showing, we need to add the functionality to the controls to move blocks around, add and remove blocks. add these functions to your controller:
You can view the working version here:
http://kmturley.github.io/angular-sir-trevor/There are many benefits to this approach in a responsive site, as the content types are separated using json and can be outputted in rows. I have used Sir Trevor JS along with Wagtail Streamfield, which does a similar thing.
But what if we want the same experience on an Angular project? well unfortunately the only version available is a wrapper around the main version, without proper Angular integration. I decided to write my own which could support the integration better. Here is how I did it.
First off we need to include angular.js in the page and add the ng-app to the html tag. Now we can create a controller for the editor with some default data:
angular.module('app', [])
.controller('editor', ['$scope', '$compile', '$templateCache', function ($scope, $compile, $templateCache) {
'use strict';
$scope.blocks = [
{ type: 'text', content: '<h1>Angular Sir Trevor</h1><p>AngularJS block-based editor inspired by Sir Trevor JS</p>' },
{ type: 'image', content: 'http://massimages.mobi/wp-content/uploads/forest-wallpaper-widescreen-hd-photograph-6.jpg' }
];
}])
Now we have some data we need assign the controller and add the loop which will output the blocks in the html page:
<div class="editor" ng-controller="editor">
<div block class="block {{ block.type }}" ng-repeat="block in blocks" ng-class="{ 'editing': block.edit }" ng-click="block.edit=true"></div>
</div>
Note that we also included a directive attribute called 'block'. This is going to be a dynamic directive which changes the block type based on the data. Lets add the directive and a function to the controller to handle it:
// directive
.directive('block', ['$window', '$compile', '$templateCache', function ($window, $compile, $templateCache) {
'use strict';
return {
restrict: 'A',
link: function (scope, element, attr, ctrl) {
scope.updateType(element, scope);
}
};
}])
// add to the editor controller
$scope.updateType = function (element, scope) {
if (scope.block.type === 'text') {
element.removeAttr('tabindex', '0');
element.html($templateCache.get(scope.block.type));
} else {
element.attr('tabindex', '0');
element.html($templateCache.get(scope.block.type) + $templateCache.get('overlay'));
}
$compile(element.contents())(scope);
};
In this updateType function we are finding the template based on the block type. But we are missing templates for the block types. Lets add those to the html:
<script type="text/ng-template" id="overlay">
<div class="overlay">
<div class="controls">
<button ng-click="moveUp(blocks, block)">▲</button>
<button ng-click="remove(blocks, block)">X</button>
<button ng-click="moveDown(blocks, block)">▼</button>
</div>
<p>UPLOAD FILE</p>
<div class="area"><input upload type="file" /></div>
<p>OR MODIFY EXISTING URL</p>
<input paste type="text" ng-model="block.content" />
</div>
</script>
<script type="text/ng-template" id="text">
<div text contenteditable="true">{{ block.content }}</div>
<div class="controls">
<button ng-click="moveUp(blocks, block)">▲</button>
<button ng-click="remove(blocks, block)">X</button>
<button ng-click="moveDown(blocks, block)">▼</button>
</div>
</script>
<script type="text/ng-template" id="image">
<img image ng-src="{{ block.content }}" alt="" />
</script>
<script type="text/ng-template" id="video">
<div class="wide"><iframe video content="{{ block.content }}" frameborder="0" allowfullscreen></iframe></div>
</script>
Now the blocks are showing, we need to add the functionality to the controls to move blocks around, add and remove blocks. add these functions to your controller:
$scope.add = function (array, element) {
array.push(element);
};
$scope.remove = function (array, element) {
var index = array.indexOf(element);
if (index === -1) {
return false;
}
array.splice(index, 1);
};
$scope.moveUp = function (array, element) {
var index = array.indexOf(element);
if (index === -1) {
return false;
}
if (array[index - 1]) {
array.splice(index - 1, 2, array[index], array[index - 1]);
} else {
return 0;
}
};
$scope.moveDown = function (array, element) {
var index = array.indexOf(element);
if (index === -1) {
return false;
}
if (array[index + 1]) {
array.splice(index, 2, array[index + 1], array[index]);
} else {
return 0;
}
};
You can view the working version here:
And get the source code at:
https://github.com/kmturley/angular-sir-trevor

No comments:
Post a Comment