10.07.2015 Views

belle-workbook

belle-workbook

belle-workbook

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Umbraco Belle WorkshopBuilding your firstproperty editor


OverviewThis guide explains how to setup a simple property editor, how to hook it into Umbraco’s datatypes, how to hook it into AngularJS modules and its injector, and finally how youcan test your property editor.These are all the steps we will go through:-• Setting up a plugin• Write some basic ‘hello world’ HTML & JS• Register the data type in Umbraco• Add external dependencies• Complete the MarkDown editorPrerequisitesThis guide is specifically focuses on how to use AngularJS with Umbraco. It does not coverAngularJS itself. There are a large number of resources freely available around the interwebthat cover AngularJS such as:-• egghead.io• angularjs.org/tutorial• tekpub.com/products/angularThe end resultBy the end of this guide, you will have a simple markdown editor running inside Umbracowhich is registered as a data type in the back office and assigned to a document type. Theeditor will also be able to create and modify data.


Step 1: Setting up a pluginThe first thing to do is create a new folder inside the ‘/App_Plugins’ folder. Let’s call it‘MarkDownEditor’Next create a simple manifest file to describe what this plugin does. This manifest will tellUmbraco about the new property editor and allows you to inject any needed files into theapplication, so create the file ‘/App_Plugins/MarkDownEditor/package.manifest’Inside this package manifest you need to add a bit of json to describe the property editor. Thecode snippet below contains inline comments describing the specific details:-{}/*you can define multiple editors*/propertyEditors: [{/*this must be a unique guid*/id: "7e062c13-7c41-4ad9-b389-41d88aeef87c",/*the name*/name: "Markdown Editor",/*the html file we will load for the editor*/editor: {view: "~/App_Plugins/MarkDownEditor/markdowneditor.html"}}],/*array of files we want to inject into the application on app_start*/javascript: ["~/App_Plugins/MarkDownEditor/markdowneditor.controller.js"]Writing some basic HTML & JSThe next step is to add 2 files to the /App_Plugins/MarkDownEditor/ folder:-markdowneditor.htmlmarkdowneditor.controller.jsThese will be the main files for the editor, with the .html file handling the view and the .js parthandling the functionality.In the .js file add a basic AngularJS controller declaration:-angular.module("umbraco").controller("My.MarkdownEditorController",function () {alert("The controller has landed");});


And in the .html file add:-Once you’ve complete the previous steps, you need to restart your application.CongratulationsYou’ve now created the basic parts for the editor:-• The package manifest, telling Umbraco what to load• The html view for the editor• The controller for wiring up the editor with AngularJS


Register the data type in UmbracoGo to the ‘Developer’ section in Umbraco and click the three dots next to the ‘Data Types’folder. Click ‘Create a new Data Type’ and call it ‘Markdown’. In the editor you can now selectyour property editor from the dropdownlist. It will be called ‘Markdown Editor’ as defined inthe earlier json code.Now save the data type and add it to a document type of your choice. If you now open anode that uses this document type in the Umbraco ‘Content’ section, you should be greetedwith an alert message that says “The controller has landed”.This means that all is well, and you can now edit and publish content. Hooray!Add external dependenciesNow, lets go a bit further and load in a markdown editor JavaScript library. For this examplelets use ‘PageDown’, but you can use whatever you wish.First of all you need to add some external files into the ‘/App_Plugins/MarkdownEditor/lib’folder. You can grab these files from the ‘PageDown’ editor project found here:-• https://github.com/samwillis/pagedown-bootstrap


Now open the ‘markdowneditor.controller.js’ file and edit it as follows:-angular.module("umbraco").controller("My.MarkdownEditorController",//inject Umbraco’s assetsServicefunction ($scope,assetsService) {//tell the assetsService to load the markdown.editor libs from//the markdown editor plugin folderassetsService.load(["/App_Plugins/MarkDownEditor/lib/markdown.converter.js","/App_Plugins/MarkDownEditor/lib/markdown.sanitizer.js","/App_Plugins/MarkDownEditor/lib/markdown.editor.js"]).then(function () {//this function will execute when all dependencies haveloadedalert("editor dependencies loaded");});//load the separate css for the editor to avoid it blocking our jsloadingassetsService.loadCss("/App_Plugins/MarkDownEditor/lib/markdown.css");});This now loads in our external dependencies, but only when they are needed by the editor.Now let’s replace the ‘alert()’ in the code above that can instantiate the PageDown editor:-var converter2 = new Markdown.Converter();var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias);editor2.run();Now add the ID to the text area in the HTML. For more information on the HTML structure,see the PageDown demo here:-• https://github.com/samwillis/pagedown-bootstrap/blob/master/demo/browser/demo.htmlyour content


Now, clear the cache, reload the document and you should see the PageDown editor runningNow when you save or publish a page the value of the editor is automatically synced to thecurrent content object and sent to the server. This happens through the power of AngularJSand the ‘ng-model’ attribute.


Step 2:Adding some configurationoptions to our property editorOverviewThis section progresses the work completed on the markdown editor you created in Step 1.The main aim is to demonstrate how to can add configuration options to the editor.ConfigurationAn important part of building a good property editor, is to build something relatively flexiblethat can be reused for different things. For example, the standard rich text editor in Umbracoallows you to choose the buttons and stylesheets you wish to use for each instance of theeditor making it specific to that DocType.Similarly, a property editor can be used as many times as required with various configurationoptions.Package.manifestTo add configuration options to our markdown editor, open the ‘package.manifest’ file.Just below the editor definition, paste in the following code:-preValueEditor: {fields: [{label: "Preview",description: "Display a live preview",key: "preview",view: "boolean"},{label: "Default value",description: "If value is blank, the editor will show this",key: "defaultValue"view: "textarea"}]}Remember - separate the editor element and prevalue editor definition with a comma, or youwill get a json error.


So what did you just add?You just added a prevalue editor, with a fields collection. This collection contains informationabout the controls we will render on the DataType configuration for this particular editor.The first option above gets the label ‘Preview’ and uses the view ‘boolean’. This will allow youto turn preview on/off via a simple checkbox.The name ‘boolean’ derives from the Umbraco convention where all preview editors arestored in ‘/Umbraco/views/prevalueeditors/’ and matched via .htmlThe second option does the same as the first option, however instead of a Boolean, aTextArea is used to allow the user to add a default value for the editor.Now save the manifest, restart the app pool and have a look at the markdown datatype inUmbraco. You should see that you have the two configuration options.Using the configurationThe next step is to gain access to our new configuration options. To do this, open the‘markdowneditor.controller.js’ file.First you need to add the ‘Default Value’ functionality. So basically when the $scope.model.value is empty or undefined, the default value is used. To do this, add the following to thebeginning of the controller:-if($scope.model.value === null || $scope.model.value === ""){$scope.model.value = $scope.model.config.defaultValue;}Do you see what’s new?The ‘$scope.model.config’ object is new. The other thing you will notice is that because ofour configuration settings we now have access to ‘$scope.model.config.defaultValue’. Thiscontains the configuration value for that key.It really is that easy to setup and use the configuration values from code.However, you can also use these values without any javascript, so open the ‘markdowneditor.html’ file instead.You can also use the configuration directly in your HTML as shown below. You can use it totoggle the preview , using the ‘ng-show’ attribute:-


Step 3: Integrating Services witha Property EditorOverviewIn this part you will integrate one of the built-in Umbraco services. For this sample you willuse the dialog service to hook into the media picker and return some image data to themarkdown editor.Injecting the serviceFirst you need to get access to the service. This is done in the construction of the controllerwhere it is added as a parameter as follows:-angular.module("Umbraco").controller("My.MarkdownEditorController",//inject Umbracos assetsServce and dialog servicefunction ($scope,assetsService, dialogService) { ... }This works in exactly the same way as with the ‘assetsService’ you added in Step One.Hooking into PageDownThe PageDown editor we are using has an event model, so you can easily hook into anyevents that are triggered by the media picker by adding a hook after the editor has started:-//Start the editorvar converter2 = new Markdown.Converter();var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias);editor2.run();//subscribe to the image dialog clickseditor2.hooks.set(“insertImageDialog”, function (callback) {//here we can intercept our own dialog handlingreturn true; // tell the editor that we’ll take care of gettingthe image url});});Notice the callback. This is used to return whatever data you need to to the editor.Now you have access to the editor events, you can trigger a media picker dialog by using thedialogService.


You can inject whatever html you want with this service, but it also has a number ofshorthand options for things like a media picker:-//the callback is called when the use selects imagesdialogService.mediaPicker({callback: function(data){//data.selection contains an array of images$(data.selection).each(function(i, item){//try using $log.log(item) to see what this data contains});}});Getting to the image dataBecause of Umbraco’s generic nature, you don’t always know where your image is, as amedia object’s data is basically an array of properties, so how do you pick the right one? - youcannot always be sure the property is called ‘UmbracoFile’ for instance.For cases like this, a helper service is available - ‘imageHelper’. This utility has some usefulmethods for accessing images embedded in property data, as well as any associatedthumbnails.Just remember to inject this ‘imageHelper’ in the controller constructor as well(in the same place as dialogService and assetsService).You can now get the image value from the selected media item, and return it through thecallback:-var imagePropVal = imageHelper.getImagePropertyValue({imageModel: item, scope: $scope });callback(imagePropVal);Now when you run the markdown editor and click the image button, we are presented with anative Umbraco dialog, listing the standard media archive.Clicking an image and choosing ‘Select’ returns the image to the editor which then renders itas:-![Koala picture][1][1]: /media/1005/Koala.jpgThe above is valid markdown code, representing the image, and if preview is turned on, youwill see the image below the editor.


Step 4: Adding Serverside Data to aProperty EditorOverviewIn this tutorial we will add a serverside API controller, which will query a custom table in theumbraco database, and then return the data to a simple angular controller + view.The end result will be a person-list, populated from a custom table, when clicked it will storethe ID of the selected person.Setup the DatabaseFirst thing we need is some data, below is a simple SQL Script for create a people table withsome random data in. You could also use http://generatedata.com for larger amounts of data:CREATE TABLE people (id INTEGER NOT NULL IDENTITY(1, 1),name VARCHAR(255) NULL,town VARCHAR(255) NULL,country VARCHAR(100) NULL,PRIMARY KEY (id));GOINSERT INTO people(name,town,country) VALUES(‘Myles A. Pearson’,’Tailles’,’UnitedKingdom’);INSERT INTO people(name,town,country) VALUES(‘Cora Y. Kelly’,’Froidchapelle’,’Latvia’);INSERT INTO people(name,town,country) VALUES(‘Brooke Baxter’,’Mogi dasCruzes’,’Grenada’);INSERT INTO people(name,town,country) VALUES(‘Illiana T. Strong’,’Bevel’,’Bhutan’);INSERT INTO people(name,town,country) VALUES(‘Kaye Frederick’,’Rothesay’,’Turkmenistan’);INSERT INTO people(name,town,country) VALUES(‘Erasmus Camacho’,’Sint-Pieters-Kapelle’,’SaintVincent and The Grenadines’);INSERT INTO people(name,town,country) VALUES(‘Aimee Sampson’,’Hawera’,’Antiguaand Barbuda’);


Setup APIController RoutesNext we need to defined a ApiController to expose a server side route which our applicationwill use to fetch the data.For this, we will create a file at: /app_code/PersonApiController.cs It must be in app_codesince we want our app to compile it on start, alternatively, you can just add it to a normal .netproject and compile into a dll as normal.In the PersonApiController.cs file, add:using System;using System.Collections.Generic;using System.Linq;using System.Web;using Umbraco.Web.WebApi;using Umbraco.Web.Editors;using Umbraco.Core.Persistence;namespace My.Controllers{[Umbraco.Web.Mvc.PluginController("My")]public class PersonApiController : UmbracoAuthorizedJsonController{//we will add a method here later}}This is a very basic Api controller which inherits from UmbracoAuthorizedJsonControllerthis specific class will only return json data, and only to requests which are authorized toaccess the backoffice.


Setup the GetAll() MethodNow that we have a controller, we need to create a method, which can return a collection ofpeople, which our editor will use.So first of all, we add a Person class to the My.Controllers namespacepublic class Person{public int Id { get; set; }public string Name { get; set; }public string Town { get; set; }public string Country { get; set; }}We will use this class to map our table data to an c# class, which we can return as json later.Now we need the GetAll() method which returns a collection of people, insert this inside thePersonApiController class:public IEnumerable GetAll(){}Inside the GetAll() method, we now write a bit of code, that connects to the database, createsa query and returns the data, mapped to the Person class above://get the databasevar db = UmbracoContext.Application.DatabaseContext.Database;//build a query to select everything the people tablevar query = new Sql().Select("*").From("people");//fetch data from DB with the query and map to Person objectreturn db.Fetch(query);We are now done with the server side of things, with the file saved in app_code you can nowopen the Url: /umbraco/My/PersonApi/GetAllThis will return our json code.


Create a Person ResourceNow that we have the serverside in place, and a Url to call, we will setup a service to retrieveour data. As an Umbraco specific convention, we call these services a *resource, so we alwayshave an indication what services fetch data from the DB.Create a new file as person.resource.js and add://adds the resource to umbraco.resources module:angular.module(‘umbraco.resources’).factory(‘personResource’,function($q, $http) {//the factory object returnedreturn {//this cals the API Controller we setup earliergetAll: function () {return $http.get("My/PersonApi/GetAll");}};});This uses the standard angular factory pattern, so we can now inject this into any of ourcontrollers under the name personResource.The getAll method just returns a $http.get call, which handles calling the url, and will returnthe data when its ready.


Create the View and ControllerWe will now finally setup a new view and controller, which follows previous tutorials, so haverefer to those for more details:The view:{{person.Name}}The controller:angular.module("umbraco").controller("My.PersonPickerController",function($scope, personResource){personResource.getAll().then(function(response){$scope.people = response.data;});});The flowSo with all these bits in place, all you need to do is register the property editor in a package.manifest - have a look at the first tutorial in this series. You will need to tell the package to loadboth your personpicker.controller.js and the person.resource.js file on app start.With this, the entire flow is:1. The view renders a list of people with a controller2. The controller asks the personResource for data3. The personResource returns a promise and asks the /my/PersonAPI APIController4. The APIController queries the database, which returns the data as stronglytyped Person objects5. The APIController returns those Person objects as json to the resource6. The resource resolve the promise7. The controller populates the viewEasy huh? - honestly though, there is a good amount of things to keep track of, but eachcomponent is tiny and flexible.


Wrap upThe important part of the above is the way you create an APIController call the database foryour own data, and finally expose the data to angular as a service using $http.For simplicity, you could also have skipped the service part, and just called $http directly inyour controller, but by having your data in a service, it becomes a reusable resource for yourentire application.Congratulations you have now completed this workshop and are now a fully qualifiedUmbraco Belle developer (ish)!!You have:-• Created a plugin• Defined a property editor• Injected your own JavaScript libraries and some 3rd party ones• Made the editor configurable• Connected the editor with native dialogs and services• Looked at koala pictures• Worked with the APIController to work with custom json dataCopyrightThe contents of this book were originally authored by Per Ploug and it’s content and allcopyright belongs to the Umbraco HQ.The original copy of this tutorial can be found online within the Umbraco Belledocumentation:-http://umbraco.github.io/Belle/#/tutorials/CreatingAPropertyEditor

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!