├── .bowerrc ├── .gitignore ├── README.md ├── angularTemplate.qext ├── bower.json ├── css ├── index.css └── index.less ├── debug.log ├── favicon.ico ├── img ├── bg-gray.png ├── logo---powered-by-Qlik.png └── placeholder.png ├── index.html ├── js ├── controllers │ └── home.js ├── directives │ ├── dropDown.js │ ├── exportToCsv.js │ ├── getObject.js │ ├── googleAnnotationChart.js │ └── visualization.js ├── lib │ ├── app.js │ ├── ga.js │ └── main.js └── services │ ├── api.js │ ├── app.js │ └── utilities.js ├── preview.png ├── thumb.png ├── views └── home.html └── wbfolder.wbl /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "js/vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | bower_components 6 | files 7 | js/vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Template for Qlik Sense Capabilities API 2 | A simple template to create a website with Angular js. It utilizes the Capabilities API for seamless interaction and multipage mashups 3 | 4 | ## Installation 5 | - Place project under your extensions folder C:\Users\\Documents\Qlik\Sense\Extensions 6 | - From the command line run `bower install` to get all the libraries. 7 | - Access it from [http://localhost:4848/extensions/angularTemplate/index.html](http://localhost:4848/extensions/angularTemplate/index.html) 8 | 9 | ## Usage 10 | 11 | ##### Changing app and server 12 | - js/lib/app.js, change the configuration to much your server host url and the app id. Qlik Sense server is running on secure protocol so make sure to change the port to 443 13 | 14 | ##### Adding pages 15 | 1. In js/controllers, Copy, paste and rename your new controller 16 | 2. In views, copy, paste and rename your new view for your controller 17 | 3. Load your new pages and define the url routes in js/lib/main.js, 18 | * L12, add the controller `'controller.yourPage': scriptsUrl + 'js/controllers/yourPage',` 19 | * L46, add the url routes 20 | ``` .when('/d3', { 21 | templateUrl: scriptsUrl+"views/yourPage.html", 22 | controller: 'controller.yourPage' 23 | ``` } ) 24 | * Finally, load them by adding the controller in L58 `'controller.controller.yourPage'` 25 | * You can access your new page by going to http://localhost:4848/extensions/angularTemplate/index.html#/yourPage 26 | 27 | ##### Adding Qlik Sense Objects 28 | - Add in your html the code ``. 29 | * qvid: the object id as found at the dev-hub `http://localhost:4848/dev-hub/single-configurator` 30 | * height: the object desired height 31 | * interaction: false if you want to disable interactions in your object otherwise just omit, defaults to true 32 | 33 | ##### Adding Drop Down filter 34 | - Add in your html the code `` 35 | * data-dimension: is the dimension to populate the drop down list from 36 | * data-title: is for the text that will be displayed in the button 37 | * data-id: a unique id/name that the app will use to change the contents like text, colors etc 38 | * data-showselected="true" : if you want the button text to change to the seleted text 39 | 40 | 41 | ## Tutorials 42 | 43 | ##### Tutorial on how to use it with 'Helpdesk Management.qvf': 44 | - Creating a website with Angular and the Capabilities API 45 | 46 | ##### Article on Memory Management and error handling: 47 | - Angularjs and Capabilities API - Memory management and "qv-collapsed-listbox-delegated-open" error 48 | 49 | ##### Capability APIs 2.2 and Angularjs, disabled selections fix 50 | - Capability APIs 2.2 and Angularjs, disabled selections fix 51 | 52 | ##### Added export to csv directive as workaround for the bug found in 2.1 53 | - Capabilities API error on exporting data 54 | 55 | ##### D3 support for custom Charts 56 | - Angularjs, Capabilities API and D3 57 | 58 | ##### getObject using a directive 59 | - Angularjs, Capabilites API - getObject the Angular way using directive 60 | 61 | ##### Creating Drop Down Menus 62 | - Angularjs & Capabilities API - DropDown Directive 63 | 64 | 65 | ##### Examples for these tutorials are found in the playground 66 | - http://webapps.qlik.com/yianni/playground/index.html 67 | - https://github.com/yianni-ververis/capabilities-api-angular-template-playground 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /angularTemplate.qext: -------------------------------------------------------------------------------- 1 | { 2 | "type": "mashup", 3 | "name": "Angular Template", 4 | "allowPosition": true 5 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friluftsframjandet", 3 | "version": "0.1", 4 | "main": "index.html", 5 | "ignore": [ 6 | "**/.*", 7 | "libs" 8 | ], 9 | "dependencies": { 10 | "domReady": "2.0.1", 11 | "bootstrap": "3.3.6", 12 | "angular-bootstrap": "0.12.1", 13 | "d3": "3.5.9" 14 | } 15 | } -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | overflow-y: auto; 4 | touch-action: auto; 5 | font-family: 'Source Sans Pro', sans-serif; 6 | font-style: normal; 7 | font-weight: 400; 8 | -webkit-font-smoothing: antialiased; 9 | } 10 | h1 { 11 | font-family: Georgia, serif; 12 | font-weight: bold; 13 | font-size: 28px; 14 | margin: 0 !important; 15 | padding: 0 !important; 16 | } 17 | h2, 18 | h3, 19 | h4, 20 | h5 { 21 | font-family: 'Source Sans Pro', sans-serif; 22 | color: #666666; 23 | font-weight: bold; 24 | display: inline-block; 25 | margin: 0 !important; 26 | padding: 0 !important; 27 | } 28 | h2 { 29 | font-size: 18px; 30 | } 31 | h3 { 32 | font-size: 15px; 33 | } 34 | a { 35 | cursor: pointer; 36 | } 37 | div.qvobject, 38 | div.qvplaceholder { 39 | padding: 0 0 0 0; 40 | height: 400px; 41 | } 42 | div.qvobject.one { 43 | height: 100px; 44 | } 45 | div.qvobject.fifty { 46 | height: 50px; 47 | } 48 | .noMarginPadding { 49 | margin: 0 !important; 50 | padding: 0 !important; 51 | } 52 | .noMarginPaddingTopBottom { 53 | margin-top: 0 !important; 54 | margin-bottom: 0 !important; 55 | padding-top: 0 !important; 56 | padding-bottom: 0 !important; 57 | } 58 | /************* 59 | * HEADER 60 | **************/ 61 | /************* 62 | * QLIK 63 | **************/ 64 | .qlik, 65 | .qlik a, 66 | .qlik a:hover { 67 | color: #65a604 !important; 68 | } 69 | /************* 70 | * BOOTSTRAP 71 | **************/ 72 | .container { 73 | max-width: 950px; 74 | width: 100%; 75 | } 76 | .container .row { 77 | padding-bottom: 20px; 78 | } 79 | .container .row .btn { 80 | font-weight: bold; 81 | border-radius: 0; 82 | } 83 | .container .row .btn.btn-success { 84 | background-color: #77b62a; 85 | border-color: #77b62a; 86 | } 87 | .container .row .btn.btn-danger { 88 | background-color: #da0063; 89 | border-color: #da0063; 90 | } 91 | .container .row .btn.btn-danger:hover, 92 | .container .row .btn.btn-success:hover { 93 | color: #77b62a; 94 | background-color: white; 95 | border-color: lightgray; 96 | } 97 | .container .row .dropdown-menu { 98 | /*.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus*/ 99 | } 100 | .container .row .dropdown-menu .active, 101 | .container .row .dropdown-menu .active a, 102 | .container .row .dropdown-menu .active a:hover, 103 | .container .row .dropdown-menu .active a:focus, 104 | .container .row .dropdown-menu li:hover, 105 | .container .row .dropdown-menu li a:hover { 106 | color: white; 107 | background-color: #77b62a; 108 | } 109 | .container .row .scrollable-menu { 110 | height: auto; 111 | overflow-x: hidden; 112 | z-index: 9999999; 113 | max-height: 200px; 114 | } 115 | /************* 116 | * ANGULAR 117 | **************/ 118 | [ng\:cloak], 119 | [ng-cloak], 120 | .ng-cloak { 121 | display: none !important; 122 | } 123 | -------------------------------------------------------------------------------- /css/index.less: -------------------------------------------------------------------------------- 1 | 2 | @color-blue: #407ec9; 3 | @color-blue-dark: #123b71; 4 | @color-red: #da0063; 5 | @color-green: #77b62a; 6 | 7 | @font-sans: 'Source Sans Pro', sans-serif; 8 | @font-serif: Georgia,serif; 9 | 10 | html, 11 | body { 12 | overflow-y: auto; 13 | touch-action: auto; 14 | font-family: @font-sans; 15 | font-style: normal; 16 | font-weight: 400; 17 | -webkit-font-smoothing: antialiased; 18 | } 19 | h1 { 20 | font-family: @font-serif; 21 | font-weight: bold; 22 | font-size: 28px; 23 | margin: 0 !important; 24 | padding: 0 !important; 25 | } 26 | h2, h3, h4, h5 { 27 | font-family: @font-sans; 28 | color: #666666; 29 | font-weight: bold; 30 | display: inline-block; 31 | margin: 0 !important; 32 | padding: 0 !important; 33 | } 34 | h2 { 35 | font-size: 18px; 36 | } 37 | h3 { 38 | font-size: 15px; 39 | } 40 | a { 41 | cursor: pointer; 42 | } 43 | div.qvobject, div.qvplaceholder { 44 | padding: 0 0 0 0; 45 | height: 400px; 46 | } 47 | div.qvobject.one { 48 | height: 100px; 49 | } 50 | div.qvobject.fifty { 51 | height: 50px; 52 | } 53 | .noMarginPadding { 54 | margin: 0 !important; 55 | padding: 0 !important; 56 | } 57 | .noMarginPaddingTopBottom { 58 | margin-top: 0 !important; 59 | margin-bottom: 0 !important; 60 | padding-top: 0 !important; 61 | padding-bottom: 0 !important; 62 | } 63 | 64 | /************* 65 | * HEADER 66 | **************/ 67 | 68 | /************* 69 | * QLIK 70 | **************/ 71 | .qlik, 72 | .qlik a, 73 | .qlik a:hover { 74 | color: #65a604 !important; 75 | } 76 | /************* 77 | * BOOTSTRAP 78 | **************/ 79 | .container { 80 | max-width: 950px; 81 | width: 100%; 82 | .row { 83 | padding-bottom: 20px; 84 | .btn { 85 | font-weight: bold; 86 | border-radius: 0; 87 | // text-transform: uppercase; 88 | &.btn-success { 89 | background-color: @color-green; 90 | border-color: @color-green; 91 | } 92 | &.btn-danger { 93 | background-color: @color-red; 94 | border-color: @color-red; 95 | } 96 | &.btn-danger:hover, 97 | &.btn-success:hover { 98 | color: @color-green; 99 | background-color: white; 100 | border-color: lightgray; 101 | } 102 | } 103 | .dropdown-menu { 104 | .active, 105 | .active a, 106 | .active a:hover, 107 | .active a:focus, 108 | li:hover, 109 | li a:hover { 110 | color: white; 111 | background-color: @color-green; 112 | } 113 | /*.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus*/ 114 | } 115 | .scrollable-menu { 116 | height: auto; 117 | overflow-x: hidden; 118 | z-index: 9999999; 119 | max-height: 200px; 120 | } 121 | } 122 | } 123 | 124 | 125 | /************* 126 | * ANGULAR 127 | **************/ 128 | [ng\:cloak], [ng-cloak], .ng-cloak { 129 | display: none !important; 130 | } -------------------------------------------------------------------------------- /debug.log: -------------------------------------------------------------------------------- 1 | [0109/135414:ERROR:tcp_listen_socket.cc(76)] Could not bind socket to 127.0.0.1:6004 2 | [0109/135414:ERROR:node_debugger.cc(86)] Cannot start debugger server 3 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/favicon.ico -------------------------------------------------------------------------------- /img/bg-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/img/bg-gray.png -------------------------------------------------------------------------------- /img/logo---powered-by-Qlik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/img/logo---powered-by-Qlik.png -------------------------------------------------------------------------------- /img/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/img/placeholder.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | myApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 | 37 | 38 |
39 | 40 | -------------------------------------------------------------------------------- /js/controllers/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name friluftsframjandetApp.controller:controller.dashboard 6 | * @author yianni.ververis@qlik.com 7 | * @description 8 | * # controller.dashboard 9 | * Controller of the myApp 10 | */ 11 | app.obj.angularApp 12 | .controller('controller.home', function ($scope, $rootScope, $location, $injector, api, utility) { 13 | var me = {}; 14 | 15 | me.init = function () { 16 | me.measures = [ 17 | ["Count( {$} Distinct %CaseId )", false] 18 | ]; 19 | $scope.kapi = []; 20 | me.objects = ['ycppXj']; 21 | } 22 | 23 | me.boot = function () { 24 | me.init(); 25 | 26 | me.events(); 27 | 28 | me.createKpis(); 29 | // me.getObjects(); 30 | 31 | // For debugging selections uncommment the line below 32 | app.obj.app.getObject('CurrentSelections', 'CurrentSelections'); 33 | utility.log('Page loaded: ', $scope.page); 34 | }; 35 | 36 | me.events = function () { 37 | // me.getObjects = function () { 38 | // api.destroyObjects().then(function(){ 39 | // api.getObjects(me.objects); 40 | // }) 41 | // } 42 | me.createKpis = function() { 43 | angular.forEach(me.measures, function(value, key) { 44 | api.getHyperCube([], [value[0]], function(data){ 45 | $scope.kapi[key] = (value[1])?utility.string2thousands(data[0][0].qText):data[0][0].qText; 46 | }); 47 | }); 48 | } 49 | $rootScope.clearAll = function () { 50 | app.obj.app.clearAll(); 51 | } 52 | $rootScope.goTo = function(page) { 53 | api.destroyObjects().then(function(){ 54 | $location.url('/' + page); 55 | }); 56 | } 57 | } 58 | 59 | me.boot(); 60 | }); 61 | -------------------------------------------------------------------------------- /js/directives/dropDown.js: -------------------------------------------------------------------------------- 1 | ';use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name myApp.directive: dropDown 6 | * @description 7 | * # dropDown 8 | * 9 | */ 10 | app.obj.angularApp 11 | .directive('dropDown', function($parse, $sce, $compile, $timeout, api) { 12 | var me = { 13 | def: { 14 | restrict: 'AE', 15 | transclude: true 16 | } 17 | }; 18 | 19 | me.boot = function() { 20 | // Get all the attributes 21 | me.def.scope = { 22 | dimension: '=', 23 | name: '=', 24 | id: '=', 25 | title: '=', 26 | width: '=', 27 | fontSize: '@', 28 | }; 29 | 30 | me.def.link = function(scope, element, attrs) { 31 | scope.items = {}; 32 | scope.currentItem = { 33 | qText: scope.title 34 | }; 35 | scope.$watch('dimension', function(newValue, oldValue) { 36 | scope.fontSize = (scope.fontSize) ? scope.fontSize : '14'; 37 | me.def.cssTemplate(scope); 38 | api.getHyperCubeQ([newValue], []).then(function(data) { 39 | scope.items = data; 40 | }) 41 | }); 42 | scope.dropDownChangeTitle = function (obj) { 43 | app.obj.app.field(scope.dimension).select([obj.qElemNumber], false, false) 44 | scope.title = obj.qText; 45 | scope.currentItem = obj; 46 | } 47 | }; 48 | 49 | me.def.template = '\n\ 50 |
\n\ 51 | \n\ 54 | \n\ 57 |
'; 58 | 59 | 60 | me.def.cssTemplate = function (obj) { 61 | var css = '\n\ 62 | \n\ 70 | '; 71 | angular.element('head').append(css); 72 | } 73 | 74 | return me.def; 75 | } 76 | 77 | return me.boot(); 78 | }); -------------------------------------------------------------------------------- /js/directives/exportToCsv.js: -------------------------------------------------------------------------------- 1 | ';use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name friluftsframjandetApp.controller: exportToCsv 6 | * @description 7 | * exports a hyperqube to csv 8 | * Directive of the friluftsframjandetApp 9 | */ 10 | app.obj.angularApp 11 | .directive('exportToCsv', function($parse, $sce, $compile, api) { 12 | var me = { 13 | def: { 14 | restrict: 'A' 15 | } 16 | }; 17 | 18 | me.boot = function () { 19 | me.def.scope = { 20 | title: '=title', 21 | headers: '=headers', 22 | data: '=data' 23 | }; 24 | 25 | me.def.link = function (scope, element, attrs) { 26 | var el = element[0]; 27 | element.bind('click', function(e){ 28 | api.getHyperCubeQ(scope.data[0], scope.data[1]).then(function(data){ 29 | var csvString = ''; 30 | if (scope.title.length) { 31 | csvString = scope.title + "\n"; 32 | } 33 | if (scope.headers.length) { 34 | for(var i=0; i', { 45 | style:'display:none', 46 | href:'data:application/octet-stream;base64,' + btoa(csvString), 47 | download: scope.title + '.csv' 48 | }).appendTo('body') 49 | a[0].click() 50 | a.remove(); 51 | }); 52 | }); 53 | 54 | }; 55 | 56 | return me.def; 57 | }; 58 | 59 | return me.boot(); 60 | }); -------------------------------------------------------------------------------- /js/directives/getObject.js: -------------------------------------------------------------------------------- 1 | ';use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name myApp.directive: getObject 6 | * @description 7 | * # getObject 8 | * Controller of the myApp 9 | */ 10 | app.obj.angularApp 11 | .directive('getObject', function($parse, $sce, $compile, $timeout) { 12 | var me = { 13 | def: { 14 | restrict: 'AE', 15 | replace: true, 16 | terminal: true 17 | } 18 | }; 19 | 20 | me.boot = function () { 21 | me.def.scope = { 22 | qvid: '=', 23 | id: '=', 24 | height: '=', 25 | interaction: '=', 26 | }; 27 | 28 | me.def.link = function (scope, element, attrs) { 29 | scope.$watch('qvid',function(newValue,oldValue) { 30 | var noInteraction = (_.isUndefined(scope.interaction) || scope.interaction) ? false : true; 31 | if (element[0].innerHTML.length==0) { 32 | var html = '
'; 33 | element.html(html); 34 | $timeout(function(){ 35 | app.obj.app.getObject(scope.id, newValue, {noInteraction: noInteraction}).then(function(model){ 36 | app.obj.getObjectModel.push(model); 37 | }); 38 | }, 500); 39 | } else { 40 | $( "#" + scope.id ).animate({ 41 | opacity: 0, 42 | }, 400, function() { 43 | var html = '
'; 44 | element.html(html); 45 | app.obj.app.getObject(scope.id, newValue, {noInteraction: noInteraction}).then(function(model){ 46 | app.obj.getObjectModel.push(model); 47 | $( "#" + scope.id ).animate({opacity: 1}, 400); 48 | }); 49 | 50 | }); 51 | } 52 | }); 53 | }; 54 | 55 | return me.def; 56 | }; 57 | 58 | return me.boot(); 59 | }); -------------------------------------------------------------------------------- /js/directives/googleAnnotationChart.js: -------------------------------------------------------------------------------- 1 | ';use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name myApp.directive: googleAnnotationChart 6 | * @description 7 | * # dropDown 8 | * Controller of the myApp 9 | */ 10 | app.obj.angularApp 11 | .directive('googleAnnotationChart', function($parse, $sce, $compile, $timeout, $q, api) { 12 | var me = { 13 | def: { 14 | restrict: 'AE', 15 | transclude: true 16 | } 17 | }; 18 | 19 | me.boot = function() { 20 | // Get all the attributes 21 | me.def.scope = { 22 | data: '=', 23 | dt: '=', 24 | }; 25 | 26 | me.def.link = function(scope, element, attrs) { 27 | google.charts.load('current', {'packages':['annotationchart']}); 28 | scope.$watchCollection('data', function(obj) { 29 | api.getHyperCubeQ(obj.dimensions, obj.measures).then(function(res) { 30 | me.def.cssTemplate(obj); 31 | var table = [obj.headers]; 32 | for (var i=0; i\n\ 89 | '; 90 | angular.element('head').append(css); 91 | } 92 | 93 | me.def.template = '\n\ 94 |
\n\ 95 |
'; 96 | 97 | return me.def; 98 | } 99 | 100 | return me.boot(); 101 | }); -------------------------------------------------------------------------------- /js/directives/visualization.js: -------------------------------------------------------------------------------- 1 | ';use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name myApp.directive: visualization 6 | * @description 7 | * Creating an element rom the Visualization API 8 | * https://help.qlik.com/en-US/sense-developer/3.0/Subsystems/APIs/Content/VisualizationAPI/VisualizationAPI.htm 9 | * Available visualization types 10 | * barchart 11 | * combochart 12 | * gauge 13 | * kpi 14 | * linechart 15 | * piechart 16 | * pivot-table 17 | * scatterplot 18 | * table 19 | * treemap 20 | * Controller of the myApp 21 | */ 22 | app.obj.angularApp 23 | .directive('visualization', function($parse, $sce, $compile, $timeout, api) { 24 | var me = { 25 | def: { 26 | restrict: 'AE', 27 | replace: true, 28 | terminal: true 29 | } 30 | }; 31 | 32 | me.boot = function () { 33 | // Get all the attributes 34 | me.def.scope = { 35 | id: '=', 36 | title: '=', 37 | type: '=', 38 | columns: '=', 39 | height: '=' 40 | }; 41 | 42 | me.def.link = function (scope, element, attrs) { 43 | var html = ''; 44 | scope.$watch('columns',function(newValue,oldValue) { 45 | // Create the template 46 | html = '
\n\ 47 |
'; 48 | // Inject the template into the DOM 49 | element.html(html); 50 | app.obj.app.visualization.create(scope.type,scope.columns,{title:scope.title}).then(function(obj){ 51 | obj.show(scope.id); 52 | }); 53 | }); 54 | }; 55 | 56 | return me.def; 57 | }; 58 | 59 | return me.boot(); 60 | }); -------------------------------------------------------------------------------- /js/lib/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @owner yianni.ververis@qlik.com 3 | * 4 | */ 5 | var me = { 6 | v: '1.0.7', 7 | obj: { 8 | qlik: null, 9 | app: null, 10 | angularApp: null, 11 | model: [], 12 | getObjectModel: [] 13 | } 14 | }; 15 | 16 | me.init = function () { 17 | me.config = { 18 | host: 'localhost', 19 | prefix: "/", 20 | port: 4848, // 443 for Sense Server 21 | id: 'Helpdesk Management.qvf' 22 | }; 23 | me.vars = {}; 24 | } 25 | 26 | me.boot = function () { 27 | me.init(); 28 | me.obj.app = me.obj.qlik.openApp(me.config.id, me.config); 29 | console.log('%c App ' + me.v + ': ', 'color: red', 'Loaded!'); 30 | }; 31 | 32 | app = me; 33 | -------------------------------------------------------------------------------- /js/lib/ga.js: -------------------------------------------------------------------------------- 1 | // YIANNI GA 2 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 3 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 4 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 5 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 6 | 7 | ga('create', 'UA-XXXXXXXX-XX', 'auto'); 8 | ga('send', 'pageview'); 9 | 10 | // QLIK GA 11 | var _gaq = _gaq || []; 12 | _gaq.push(['_setAccount', 'UA-163089-12'],['_setDomainName', '.qlik.com'],['_addIgnoredRef', 'qlikview.com'],['_addIgnoredRef', 'qlik.com'],['_gat._anonymizeIp']); 13 | if (/[?&]c=/.test(location.search)) { _gaq.push(['_setCampNameKey',"c"],['_setCampSourceKey',"s"],['_setCampMediumKey',"m"],['_setCampTermKey',"k"]); } 14 | _gaq.push(function () { var twoQ, twoQResults, cleanPageview, pageTracker = _gat._getTrackerByName(); 15 | try { 16 | twoQ = /(^.*&q=.*)&q=[^&]*(.*)/; twoQResults = twoQ.exec(location.search); 17 | if (twoQResults) { 18 | pageTracker._trackPageview(location.pathname + twoQResults[1] + twoQResults[2]); 19 | } else if (/ |%20/.test(location.href)) { 20 | cleanPageview = location.href.replace(location.protocol+"//"+location.hostname,'').replace(/ |%20/g,"-"); 21 | pageTracker._trackPageview(cleanPageview); 22 | } else { 23 | pageTracker._trackPageview(); 24 | } 25 | } catch (e) { pageTracker._trackPageview(); } 26 | }); 27 | (function() { 28 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; 29 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 30 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 31 | })(); -------------------------------------------------------------------------------- /js/lib/main.js: -------------------------------------------------------------------------------- 1 | var scriptsUrl = 'http://localhost:4848/extensions/angularTemplate/'; 2 | /* 3 | * DEPENDANCIES for Sense 2.2.3 - Angular 1.2.15, Bootstrap 3.1.1, jQuery 2.1.3? 4 | * DEPENDANCIES for Sense 3.0 - Angular 1.5.0, Bootstrap 3.3.6, jQuery 2.1.3 5 | */ 6 | require.config({ 7 | baseUrl: "http://localhost:4848/resources", 8 | paths: { 9 | 'domReady': scriptsUrl +'js/vendor/domReady/domReady', 10 | 'bootstrap': scriptsUrl + 'js/vendor/bootstrap/dist/js/bootstrap.min', 11 | 'app': scriptsUrl + 'js/lib/app', 12 | 'ga': scriptsUrl + 'js/lib/ga', 13 | 'controller.home': scriptsUrl + 'js/controllers/home', 14 | 'directive.getObject': scriptsUrl + 'js/directives/getObject', 15 | 'directive.dropDown': scriptsUrl + 'js/directives/dropDown', 16 | 'directive.exportToCsv': scriptsUrl + 'js/directives/exportToCsv', 17 | 'directive.visualization': scriptsUrl + 'js/directives/visualization', 18 | 'directive.googleAnnotationChart': scriptsUrl + 'js/directives/googleAnnotationChart', 19 | 'service.api': scriptsUrl + 'js/services/api', 20 | 'service.utility': scriptsUrl + 'js/services/utilities' 21 | } 22 | }); 23 | 24 | define([ 25 | 'require', 26 | 'angular', 27 | 'app' 28 | ], function (require, angular) { 29 | 'use strict'; 30 | 31 | // define( "client.services/grid-service", {} ); 32 | app.obj.angularApp = angular.module('myApp', [ 33 | 'ngAnimate', 34 | 'ngRoute', 35 | ]); 36 | app.obj.angularApp.config(function($routeProvider,$locationProvider) { 37 | $routeProvider 38 | .when('/', { 39 | templateUrl: scriptsUrl+"views/home.html", 40 | controller: 'controller.home' 41 | } ) 42 | .otherwise({redirectTo: '/'}) 43 | }) 44 | require([ 45 | 'domReady!', 46 | 'js/qlik', 47 | 'angular', 48 | 'ga', 49 | 'controller.home', 50 | 'service.api', 51 | 'service.utility', 52 | 'directive.getObject', 53 | 'directive.dropDown', 54 | 'directive.exportToCsv', 55 | 'directive.visualization', 56 | 'directive.googleAnnotationChart', 57 | 'bootstrap', 58 | ], function (document, qlik) { 59 | app.obj.qlik = qlik; 60 | qlik.setOnError( function ( error ) { 61 | if (!angular.isUndefined(error) && error.code == 16) { 62 | location.reload(); 63 | } else { 64 | console.log(error); 65 | } 66 | } ); 67 | 68 | angular.bootstrap( document, ["myApp", "qlik-angular"] ); 69 | 70 | app.boot(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /js/services/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name friluftsframjandetApp.controller: api 6 | * @description 7 | * # api 8 | * Controller of the friluftsframjandetApp 9 | */ 10 | app.obj.angularApp 11 | .service('api', function ($q, $rootScope, utility) { 12 | var me = this; 13 | 14 | me.getObjects = function (obj) { 15 | var deferred = $q.defer(), 16 | promises = []; 17 | 18 | setTimeout(function(){ 19 | angular.forEach(obj, function(value, key) { 20 | app.obj.app.getObject(value, value).then(function(model){ 21 | app.obj.model.push(model); 22 | deferred.resolve(value); 23 | }); 24 | promises.push(deferred.promise); 25 | }); 26 | }, 500); 27 | return $q.all(promises); 28 | }; 29 | 30 | me.destroyObjects = function () { 31 | var deferred = $q.defer(); 32 | var promises = []; 33 | if (app.obj.model.length >= 1) { 34 | angular.forEach(app.obj.model, function(value, key) { 35 | value.close(); 36 | deferred.resolve(); 37 | promises.push(deferred.promise); 38 | }); 39 | app.obj.model = []; 40 | } 41 | if (app.obj.getObjectModel.length >= 1) { 42 | angular.forEach(app.obj.getObjectModel, function(value, key) { 43 | value.close(); 44 | deferred.resolve(); 45 | promises.push(deferred.promise); 46 | }); 47 | app.obj.getObjectModel = []; 48 | } 49 | if (app.obj.model.length < 1 && app.obj.getObjectModel.length < 1) { 50 | deferred.resolve(); 51 | promises.push(deferred.promise); 52 | } 53 | return $q.all(promises); 54 | }; 55 | 56 | // To get generic Hypercubes 57 | me.getHyperCube = function (dimensions, measures, callback, limit) { 58 | var qDimensions = [], 59 | qMeasures = []; 60 | if (dimensions.length) { 61 | angular.forEach(dimensions, function(value, key) { 62 | qDimensions.push({ 63 | qDef: { 64 | qGrouping: "N", 65 | qFieldDefs: [ value ], 66 | }, 67 | qNullSuppression: true, 68 | }); 69 | }); 70 | } 71 | if (measures.length) { 72 | angular.forEach(measures, function(value, key) { 73 | qMeasures.push({ 74 | qDef : { 75 | qDef : value 76 | } 77 | }); 78 | }); 79 | } 80 | app.obj.app.createCube({ 81 | qDimensions : qDimensions, 82 | qMeasures : qMeasures, 83 | qInitialDataFetch : [{ 84 | qTop : 0, 85 | qLeft : 0, 86 | qHeight : (limit)?limit:500, 87 | qWidth : 11 88 | }] 89 | }, function(reply) { 90 | utility.log('getMeasureData:', 'Success!'); 91 | callback(reply.qHyperCube.qDataPages[0].qMatrix); 92 | }); 93 | }; 94 | 95 | // Get Hypercube data. Using Promises 96 | me.getHyperCubeQ = function (dimensions, measures) { 97 | var qDimensions = [], 98 | qMeasures = []; 99 | if (dimensions.length) { 100 | angular.forEach(dimensions, function(value, key) { 101 | qDimensions.push({ 102 | qDef: { 103 | qGrouping: "N", 104 | qFieldDefs: [ value ], 105 | } 106 | }); 107 | }); 108 | } 109 | if (measures.length) { 110 | angular.forEach(measures, function(value, key) { 111 | qMeasures.push({ 112 | qDef : { 113 | qDef : value 114 | }, 115 | qSortBy: { 116 | qSortByState: 0, 117 | qSortByFrequency: 0, 118 | qSortByNumeric: 0, 119 | qSortByAscii: 0, 120 | qSortByLoadOrder: 0, 121 | qSortByExpression: 0, 122 | qExpression: { 123 | qv: "" 124 | } 125 | } 126 | }); 127 | }); 128 | } 129 | var deferred = $q.defer(); 130 | app.obj.app.createCube({ 131 | qDimensions : qDimensions, 132 | qMeasures : qMeasures, 133 | qInitialDataFetch : [{ 134 | qTop : 0, 135 | qLeft : 0, 136 | qHeight : 500, 137 | qWidth : 11 138 | }] 139 | }, function(reply) { 140 | utility.log('getHyperCubeQ:', 'Success!'); 141 | deferred.resolve(reply.qHyperCube.qDataPages[0].qMatrix); 142 | }); 143 | return deferred.promise; 144 | }; 145 | 146 | me.getTable = function(dimensions, measures, options) { 147 | return app.obj.app.createTable(dimensions, measures, options); 148 | } 149 | 150 | // To get list of data 151 | me.createList = function (field, callback, limit) { 152 | console.log(field); 153 | app.obj.app.createList({ 154 | qDef: { 155 | qFieldDefs: field 156 | }, 157 | qInitialDataFetch : [{ 158 | qTop : 0, 159 | qLeft : 0, 160 | qHeight : (limit)?limit:500, 161 | qWidth : 1 162 | }] 163 | }, function(reply) { 164 | utility.log('createList:', 'Success!'); 165 | callback(reply.qListObject.qDataPages[0].qMatrix); 166 | }); 167 | }; 168 | 169 | // Add Google tracking 170 | me.ga = function (title) { 171 | ga('send', 'event', 'button', 'click', title, 1); 172 | }; 173 | }); -------------------------------------------------------------------------------- /js/services/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @owner yianni.ververis@qlik.com 6 | * @description 7 | * # app 8 | * Controller of the myApp 9 | */ 10 | app.angularApp 11 | .service('app', function ($q, $rootScope, utility) { 12 | var me = this; 13 | console.log(app.qlik) 14 | me.version = '1.0.4'; 15 | me.obj = { 16 | qlik: null, 17 | app: null, 18 | angularApp: null, 19 | model: [], 20 | getObjectModel: [] 21 | } 22 | me.envirnonment = { 23 | local: { 24 | host: 'localhost', 25 | prefix: "/", 26 | port: 4848, 27 | isSecure: false, 28 | id: 'Helpdesk Management.qvf' 29 | }, 30 | }; 31 | me.config = me.envirnonment.local; 32 | 33 | me.obj.app = me.obj.qlik.openApp(me.config.id, me.config); 34 | 35 | utility.log('App loaded: ', me.version); 36 | }); -------------------------------------------------------------------------------- /js/services/utilities.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name friluftsframjandetApp.controller: controllers.utility 6 | * @description 7 | * # Utility 8 | * Controller of the friluftsframjandetApp 9 | */ 10 | app.obj.angularApp 11 | .service('utility', function ($q, $window, $location) { 12 | var me = this; 13 | 14 | // Convert 10000 into 10,000 15 | me.string2thousands = function (string) { 16 | if (_.isNumber(string)){ 17 | string = string.toString(); 18 | } 19 | if (string.length >= 6 ) { 20 | return string.replace(/(\d+)(\d{3})(\d{3})/, '$1' + ',' + '$2' + ',' + '$3'); 21 | } else { 22 | return string.replace(/(\d+)(\d{3})/, '$1' + ',' + '$2'); 23 | } 24 | } 25 | 26 | // Custom Logger 27 | me.log = function (type, message) { 28 | console.log('%c ' + type + ': ', 'color: red', message); 29 | $window.ga('send', 'pageview', $location.path()); 30 | }; 31 | 32 | }); -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/preview.png -------------------------------------------------------------------------------- /thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yianni-ververis/capabilities-api-angular-template/9f34d7e258836ddab3102009e29f55d9c714aff3/thumb.png -------------------------------------------------------------------------------- /views/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 |
8 |
9 | 10 | 11 |
12 |
13 |
14 |

High Priority Cases

15 |
16 |

{{ kapi[0] }}

17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /wbfolder.wbl: -------------------------------------------------------------------------------- 1 | angularTemplate.qext; 2 | index.html; --------------------------------------------------------------------------------