From 0da60087aeb31483abaa2828708ae05edc1edba6 Mon Sep 17 00:00:00 2001 From: Per Ploug Krogslund Date: Mon, 9 Sep 2013 14:37:42 +0200 Subject: [PATCH 1/3] Adds touch and swiping support to navigation and all modals --- .../src/common/services/navigation.service.js | 10 ++- src/Umbraco.Web.UI.Client/src/less/hacks.less | 2 + .../src/views/common/main.controller.js | 6 +- src/Umbraco.Web.UI.Client/test/config/unit.js | 66 ------------------- 4 files changed, 13 insertions(+), 71 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/test/config/unit.js diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index bd941745dc..5a53334cbb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -66,6 +66,7 @@ angular.module('umbraco.services') var service = { active: false, mode: "default", + touchDevice: false, ui: ui, /** @@ -142,12 +143,15 @@ angular.module('umbraco.services') * Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again */ leaveTree: function () { - service.active = false; + if(!service.touchDevice){ + service.active = false; + $timeout(function(){ if(!service.active){ service.hideTree(); } }, 300); + } }, /** @@ -379,7 +383,7 @@ angular.module('umbraco.services') * * @description * hides the search pane - */ + */ hideSearch: function () { setMode("default-hidesearch"); }, @@ -392,7 +396,7 @@ angular.module('umbraco.services') * hides any open navigation panes and resets the tree, actions and the currently selected node */ hideNavigation: function () { - this.ui.currentSection = ""; + this.ui.currentSection = ""; this.ui.actions = []; this.ui.currentNode = undefined; setMode("default"); diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index 6665b4c750..1e214dc5d4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -3,6 +3,8 @@ /* CONTAINS ALL HACKS AND OTHER QUICK-FIXES THAT WE MUST HANDLE BEFORE A RELEASE */ +/*wft ms?*/ +*{ -ms-touch-action: none;} .umbracoDialog{ width: auto !Important; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js index d63b2f15fd..ec43d49ebe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js @@ -10,9 +10,11 @@ */ function MainController($scope, $routeParams, $rootScope, $timeout, $http, $log, notificationsService, userService, navigationService, legacyJsLoader) { - //detect if the current device is touch-enabled - $scope.touchDevice = (true === ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch)); + //detect if the current device is touch-enabled + $scope.touchDevice = ("ontouchstart" in window || window.touch || window.navigator.msMaxTouchPoints===5 || window.DocumentTouch && document instanceof DocumentTouch); + navigationService.touchDevice = $scope.touchDevice; + //the null is important because we do an explicit bool check on this in the view //the avatar is by default the umbraco logo $scope.authenticated = null; diff --git a/src/Umbraco.Web.UI.Client/test/config/unit.js b/src/Umbraco.Web.UI.Client/test/config/unit.js deleted file mode 100644 index e6ad709dc3..0000000000 --- a/src/Umbraco.Web.UI.Client/test/config/unit.js +++ /dev/null @@ -1,66 +0,0 @@ -// base path, that will be used to resolve files and exclude -basePath = '../..'; - -// list of files / patterns to load in the browser -files = [ - JASMINE, - JASMINE_ADAPTER, - - 'lib/jquery/jquery-2.0.3.min.js', - 'lib/angular/angular.min.js', - 'lib/angular/angular-cookies.min.js', - 'test/lib/angular/angular-mocks.js', - 'lib/umbraco/Extensions.js', - 'src/app_dev.js', - 'src/common/directives/*.js', - 'src/common/filters/*.js', - 'src/common/services/*.js', - 'src/common/security/*.js', - 'src/common/mocks/**/*.js', - 'src/views/**/*.controller.js', - 'test/unit/**/*.spec.js' -]; - -plugins = [ - 'karma-jasmine', - 'karma-chrome-launcher', - 'karma-phantomjs-launcher' - ]; - -// use dots reporter, as travis terminal does not support escaping sequences -// possible values: 'dots' || 'progress' -reporters = 'progress'; - -// these are default values, just to show available options - -// web server port -port = 8089; - -// cli runner port -runnerPort = 9109; - -// enable / disable colors in the output (reporters and logs) -colors = true; - -// level of logging -// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG -logLevel = LOG_INFO; - -// enable / disable watching file and executing tests whenever any file changes -autoWatch = false; - -// polling interval in ms (ignored on OS that support inotify) -autoWatchInterval = 0; - -// Start these browsers, currently available: -// - Chrome -// - ChromeCanary -// - Firefox -// - Opera -// - Safari -// - PhantomJS -browsers = ['PhantomJS']; - -// Continuous Integration mode -// if true, it capture browsers, run tests and exit -singleRun = true; From d2e68cca54249cc5370e7c94bfb541fe00d9c6b1 Mon Sep 17 00:00:00 2001 From: Per Ploug Krogslund Date: Mon, 9 Sep 2013 14:37:49 +0200 Subject: [PATCH 2/3] Docs update --- .../src/tutorials/Add-ServerSide-Data.ngdoc | 157 ++++++++++++++++++ ...grating-Services-With-PropertyEditor.ngdoc | 2 +- 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc new file mode 100644 index 0000000000..335cb96ff6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Add-ServerSide-Data.ngdoc @@ -0,0 +1,157 @@ +@ngdoc overview +@name Adding serverside data to a property editor +@description + +##Overview +In this tutorial we will add a serverside API controller, which will query a custom table in the umbraco 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 store the ID of the selected person. + +##Setup the database +First thing we need is some data, below is a simple SQL Script for create a `people` table with some 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) + ); + GO + + INSERT INTO people(name,town,country) VALUES('Myles A. Pearson','Tailles','United Kingdom'); + INSERT INTO people(name,town,country) VALUES('Cora Y. Kelly','Froidchapelle','Latvia'); + INSERT INTO people(name,town,country) VALUES('Brooke Baxter','Mogi das Cruzes','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','Saint Vincent and The Grenadines'); + INSERT INTO people(name,town,country) VALUES('Aimee Sampson','Hawera','Antigua and Barbuda'); + + +##Setup ApiController routes +Next we need to defined a `ApiController` to expose a server side route which our application will use to fetch the data. + +For this, we will create a file at: `/app_code/PersonApiController.cs` It must be in app_code since we want our app to compile it on start, alternatively, you can just add it to a normal .net project 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 `UmbracoAuthorizedJsonController` this specific class will only return json data, and only to requests which are authorized to access the backoffice + +##Setup the GetAll() method +Now that we have a controller, we need to create a method, which can return a collection of people, which our editor will use. + +So first of all, we add a `Person` class to the `My.Controllers` namespace: + + public 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 the PersonApiController class: + + public IEnumerable GetAll() + { + + } + +Inside the GetAll() method, we now write a bit of code, that connects to the database, creates a query and returns the data, mapped to the `Person` class above: + + //get the database + var db = UmbracoContext.Application.DatabaseContext.Database; + //build a query to select everything the people table + var query = new Sql().Select("*").From("people"); + //fetch data from DB with the query and map to Person object + return db.Fetch(query); + +We are now done with the server side of things, with the file saved in app_code you can now open the Url: /umbraco/My/PersonApi/GetAll + +This will return our json code. + +##Create a Person Resource +Now that we have the serverside in place, and a Url to call, we will setup a service to retrieve our data. As an Umbraco specific convention, we call these services a *resource, so we always have 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 returned + return { + //this cals the Api Controller we setup earlier + getAll: function () { + return $http.get("My/PersonApi/GetAll"); + } + }; + } + ); + +This uses the standard angular factory pattern, so we can now inject this into any of our controllers under the name `personResource`. + +the getAll method just returns a $http.get call, which handles calling the url, and will return the data when its ready. + +##Create the view and controller +We will now finally setup a new view and controller, which follows previous tutorials, so have refer to those for more details: + +####the view: + +
+ +
+ +####The controller: + + angular.module("umbraco") + .controller("My.PersonPickerController", function($scope, personResource){ + personResource.getAll().then(function(response){ + $scope.people = response.data; + }); + }); + +##The flow +So 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 load both 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 controller +2. the controller asks the personResource for data +3. the personResource returns a promise and asks the /my/PersonAPI api controller +4. The apicontroller queries the database, which returns the data as strongly typed Person objects +5. the api controller returns those `Person` objects as json to the resource +6. the resource resolve the promise +7. the controller populates the view + +Easy huh? - honestly tho, there is a good amount of things to keep track of, but each component is tiny and flexible. + +##Wrap-up +The important part of the above is the way you create an `ApiController` call the database for your 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 in your controller, but by having your data in a service, it becomes a reusable resource for your entire application. + + diff --git a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Intergrating-Services-With-PropertyEditor.ngdoc b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Intergrating-Services-With-PropertyEditor.ngdoc index 93998a7ced..2c4cae08fd 100644 --- a/src/Umbraco.Web.UI.Client/docs/src/tutorials/Intergrating-Services-With-PropertyEditor.ngdoc +++ b/src/Umbraco.Web.UI.Client/docs/src/tutorials/Intergrating-Services-With-PropertyEditor.ngdoc @@ -51,7 +51,7 @@ For cases like this, a helper service is available: `imageHelper`. This utillity So we get the image page from the selected media item, and return it through the callback: - var imagePropVal = imageHelper.getImagePropertyVaue({ imageModel: item, scope: $scope }); + var imagePropVal = imageHelper.getImagePropertyValue({ imageModel: item, scope: $scope }); callback(imagePropVal); Now when we run the markdown editor and click the image button, we are presented with a native umbraco dialog, listing the standard media archive. From e69f0ae6a32739b99cca875d7bf659c2bd9e7e7a Mon Sep 17 00:00:00 2001 From: Per Ploug Krogslund Date: Mon, 9 Sep 2013 14:38:02 +0200 Subject: [PATCH 3/3] karma conf update --- src/Umbraco.Web.UI.Client/test/config/karma.conf.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 91db5e6fed..e853b31a44 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -23,7 +23,7 @@ module.exports = function(karma) { 'lib/underscore/underscore.js', - 'lib/umbraco/Extensions.js', + 'lib/umbraco/Extensions.js', 'lib/yepnope/yepnope.min.js', 'test/config/app.unit.js', @@ -38,7 +38,7 @@ module.exports = function(karma) { 'src/views/**/*.controller.js', 'test/unit/**/*.spec.js', - {pattern: 'lib/umbraco/*.js', watched: true, served: true} + {pattern: 'lib/umbraco/NamespaceManager.js', watched: true, served: true} ], // list of files to exclude