• Recent
    • Tags
    • Popular
    • Register
    • Login

    Please Note This forum exists for community support for the Mango product family and the Radix IoT Platform. Although Radix IoT employees participate in this forum from time to time, there is no guarantee of a response to anything posted here, nor can Radix IoT, LLC guarantee the accuracy of any information expressed or conveyed. Specific project questions from customers with active support contracts are asked to send requests to support@radixiot.com.

    Radix IoT Website Mango 3 Documentation Website Mango 4 Documentation Website Mango 5 Documentation Website

    How to use mangos own login to secure an AngularJS custom dashboard

    Dashboard Designer & Custom AngularJS Pages
    5
    17
    6.2k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • N
      nyoa
      last edited by

      Thank you a lot. I've used your templates for creating the dashboard so 'mango-3.0/bootstrap' was a remnant from before copying app.js over. The login page isn't required to be standard for mango so it should be for the dashboard only.

      export.js is there for my custom directives. I'll copy it's content here

      /**
       * @copyright 2016 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
       * @author Jared Wiltshire
       */
      
      define([
          'angular',
          'mango-3.0/maMaterialDashboards'
      ], function(angular, maMaterialDashboards) {
      'use strict';
      
      var myApp = angular.module('myApp', ['maMaterialDashboards']);
      
      myApp.run(['$rootScope', function($rootScope) {
          $rootScope.pi = function() {
              return Math.PI;
          }
      }]);
      
      myApp.directive('myCustomComponent', function() {
          return {
              restrict: 'E',
              scope: {
                  name: '@'
              },
              template: '<span>Hello {{name}}!</span>'
          };
      });
      
      myApp.controller('myCtrl',['$scope', 
      			$scope.exportData = function(values){
      				$scope.list = [];
      				for (var i = 0, len = values.length; i < len; i++) {
      					$scope.d = new Date(values*.timestamp);
      					$scope.list.splice(0,0,{"timestamp":$scope.d.toLocaleString(),"value":values*.value});
      				};
      			};
      }]);
      
      angular.element(document).ready(function() {
          angular.bootstrap(document.documentElement, ['myApp']);
      });
      
      }); // define
      

      I haven't done any changes to app.js, although it looks like I should change the "state: 'dashboard.home'" to point to the directory I have my index.html in?

      Here is the content of app.js

      /**
       * @copyright 2016 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
       * @author Jared Wiltshire
       */
      
      define([
          'angular',
          './directives/menu/menuLink',
          './directives/menu/menuToggle',
          './directives/login/login',
          'mango-3.0/maMaterialDashboards',
          'mango-3.0/maAppComponents',
          'angular-ui-router',
          'angular-loading-bar'
      ], function(angular, menuLink, menuToggle, login, maMaterialDashboards, maAppComponents) {
      'use strict';
      
      var myAdminApp = angular.module('myAdminApp', [
          'ui.router',
          'angular-loading-bar',
          'maMaterialDashboards',
          'maAppComponents',
          'ngMessages'
      ]);
      
      myAdminApp
          .directive('menuLink', menuLink)
          .directive('menuToggle', menuToggle)
          .directive('login', login);
      
      myAdminApp.constant('PAGES', [
          {
              state: 'dashboard',
              url: '/dashboard',
              templateUrl: 'views/dashboard/main.html',
              resolve: {
                  auth: ['$rootScope', 'User', function($rootScope, User) {
                      $rootScope.user = User.current();
                      return $rootScope.user.$promise;
                  }]
              }
          },
          {
              state: 'login',
              url: '/login',
              templateUrl: 'views/login.html'
          },
          {
              state: 'dashboard.home',
              url: '/home',
              templateUrl: 'views/dashboard/home.html',
              menuTr: 'dashboards.v3.dox.home',
              menuIcon: 'fa fa-home',
              menuType: 'link'
          },
          {
              state: 'dashboard.apiErrors',
              url: '/api-errors',
              templateUrl: 'views/dashboard/errors.html',
              menuTr: 'dashboards.v3.dox.apiErrors'
          },
          {
              state: 'dashboard.section1',
              url: '/section-1',
              menuText: 'Section 1',
              menuIcon: 'fa fa-building',
              menuType: 'toggle',
              children: [
                  {
                      state: 'dashboard.section1.page1',
                      templateUrl: 'views/section1/page1.html',
                      url: '/page-1',
                      menuText: 'Page 1',
                      menuType: 'link'
                  },
                  {
                      state: 'dashboard.section1.page2',
                      templateUrl: 'views/section1/page2.html',
                      url: '/page-2',
                      menuText: 'Page 2',
                      menuType: 'link'
                  }
              ]
          },
          {
              state: 'dashboard.section2',
              url: '/section-2',
              menuText: 'Section 2',
              menuIcon: 'fa fa-bolt',
              menuType: 'toggle',
              children: [
                  {
                      state: 'dashboard.section2.page1',
                      templateUrl: 'views/section2/page1.html',
                      url: '/page-1',
                      menuText: 'Page 1',
                      menuType: 'link'
                  },
                  {
                      state: 'dashboard.section2.page2',
                      templateUrl: 'views/section2/page2.html',
                      url: '/page-2',
                      menuText: 'Page 2',
                      menuType: 'link'
                  }
              ]
          }
      ]);
      
      myAdminApp.config([
          'PAGES',
          '$stateProvider',
          '$urlRouterProvider',
          '$httpProvider',
          '$mdThemingProvider',
          '$injector',
      function(PAGES, $stateProvider, $urlRouterProvider, $httpProvider, $mdThemingProvider, $injector) {
      
          $mdThemingProvider
              .theme('default')
              .primaryPalette('yellow')
              .accentPalette('red');
      
          $httpProvider.interceptors.push('errorInterceptor');
      
          $urlRouterProvider.otherwise('/dashboard/home');
          addStates(PAGES);
          
          function addStates(pages, parent) {
              angular.forEach(pages, function(page, area) {
                  if (page.state) {
                      var state = {
                          url: page.url
                      }
                      
                      if (page.menuTr) {
                          state.menuTr = page.menuTr;
                      }
                      if (page.menuText) {
                          state.menuText = page.menuText;
                      }
                      
                      if (parent) {
                          state.parentPage = parent;
                      }
                      
                      if (page.templateUrl) {
                          state.templateUrl = page.templateUrl;
                      } else {
                          state.template = '<div ui-view></div>';
                          state['abstract'] = true;
                      }
                      
                      if (page.resolve) {
                          state.resolve = page.resolve;
                      }
                      
                      $stateProvider.state(page.state, state);
                  }
                  
                  addStates(page.children, page);
              });
          }
      }]);
      
      myAdminApp.run([
          'PAGES',
          '$rootScope',
          '$state',
          '$timeout',
          '$mdSidenav',
          '$mdColors',
          '$MD_THEME_CSS',
      function(PAGES, $rootScope, $state, $timeout, $mdSidenav, $mdColors, $MD_THEME_CSS) {
          $rootScope.pages = PAGES;
          $rootScope.Math = Math;
          
          // inserts a style tag to style <a> tags with accent color
          if ($MD_THEME_CSS) {
              var acc = $mdColors.getThemeColor('accent-500-1.0');
              var accT = $mdColors.getThemeColor('accent-500-0.2');
              var accD = $mdColors.getThemeColor('accent-700-1.0');
              var styleContent =
                  'a:not(.md-button) {color: ' + acc +'; border-bottom-color: ' + accT + ';}\n' +
                  'a:not(.md-button):hover, a:not(.md-button):focus {color: ' + accD + '; border-bottom-color: ' + accD + ';}\n';
              
              var style = document.createElement('style');
              style.appendChild(document.createTextNode(styleContent));
              document.head.appendChild(style);
          }
      
          $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
              if (error && (error.status === 401 || error.status === 403)) {
                  event.preventDefault();
                  $state.loginRedirect = toState;
                  $state.go('login');
              }
          });
      
          $rootScope.$on("$stateChangeSuccess", function(event, toState, toParams, fromState, fromParams) {
              var crumbs = [];
              var state = toState;
              do {
                  if (state.menuTr) {
                      crumbs.unshift({maTr: state.menuTr});
                  } else if (state.menuText) {
                      crumbs.unshift({text: state.menuText});
                  }
              } while (state = state.parentPage);
              $rootScope.crumbs = crumbs;
          });
          
          $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
              if ($state.includes('dashboard')) {
                  $rootScope.closeMenu();
              }
          });
      
          $rootScope.closeMenu = function() {
              $mdSidenav('left').close();
          }
      
          $rootScope.openMenu = function() {
              angular.element('#menu-button').blur();
              $mdSidenav('left').open();
          }
      
      }]);
      
      angular.element(document).ready(function() {
          angular.bootstrap(document.documentElement, ['myAdminApp']);
      });
      
      }); // define
      
      1 Reply Last reply Reply Quote 0
      • Jared WiltshireJ
        Jared Wiltshire
        last edited by

        So what you need to do is to combine the contents of export.js and app.js, at the moment you are trying to bootstrap two separate AngularJS apps on the same element (document.documentElement) which wont work.

        Either migrate your controller to app.js and remove export.js or migrate the login stuff as per my previous post into export.js and remove app.js. Decided which way to go based on whether you wish to keep the menu and toolbar or not.

        Developer at Radix IoT

        1 Reply Last reply Reply Quote 0
        • B
          Brad_GMI
          last edited by Brad_GMI

          Here is my app.js if it is of any assistance:

          /**
           * @copyright 2016 {@link http://infiniteautomation.com|Infinite Automation Systems, Inc.} All rights reserved.
           * @author Jared Wiltshire
           */
          
          define([
              'angular',
              './directives/login/login',
              'mango-3.0/maMaterialDashboards',
              'mango-3.0/maAppComponents',
              'angular-ui-router',
              'angular-loading-bar'
          ], function(angular, login, maMaterialDashboards, maAppComponents) {
          'use strict';
          
          var myApp = angular.module('myApp', [
              'ui.router',
              'angular-loading-bar',
              'maMaterialDashboards',
              'maAppComponents',
              'ngMessages'
          ]);
          
          myApp
              .directive('login', login);
          
          myApp.constant('PAGES', [
              {
                  state: 'dashboard',
                  url: '/dashboard',
                  templateUrl: 'views/dashboard/main.html',
                  resolve: {
                      auth: ['$rootScope', 'User', function($rootScope, User) {
                          $rootScope.user = User.current();
                          return $rootScope.user.$promise;
                      }]
                  }
              },
              {
                  state: 'login',
                  url: '/login',
                  templateUrl: 'views/login.html'
              },
          	{
                  state: 'dashboard.home',
                  url: '/home',
                  templateUrl: 'views/dashboard/home.html'
              }
          ]);
          
          myApp.config([
              'PAGES',
              '$stateProvider',
              '$urlRouterProvider',
              '$httpProvider',
              '$mdThemingProvider',
              '$injector',
          function(PAGES, $stateProvider, $urlRouterProvider, $httpProvider, $mdThemingProvider, $injector) {
          
              $mdThemingProvider.theme('default')
              .dark()
          	.primaryPalette('orange')
              .accentPalette('light-blue')
          	.warnPalette('blue');
          
              $httpProvider.interceptors.push('errorInterceptor');
          
              $urlRouterProvider.otherwise('/dashboard/home');
              addStates(PAGES);
              
              function addStates(pages, parent) {
                  angular.forEach(pages, function(page, area) {
                      if (page.state) {
                          var state = {
                              url: page.url
                          }
          
                          if (parent) {
                              state.parentPage = parent;
                          }
                          
                          if (page.templateUrl) {
                              state.templateUrl = page.templateUrl;
                          } else {
                              state.template = '<div ui-view></div>';
                              state['abstract'] = true;
                          }
                          
                          if (page.resolve) {
                              state.resolve = page.resolve;
                          }
                          
                          $stateProvider.state(page.state, state);
                      }
                      
                      addStates(page.children, page);
                  });
              }
          }]);
          
          myApp.run([
              'PAGES',
              '$rootScope',
              '$state',
              '$timeout',
              '$mdSidenav',
              '$mdColors',
              '$MD_THEME_CSS',
          function(PAGES, $rootScope, $state, $timeout, $mdSidenav, $mdColors, $MD_THEME_CSS) {
              $rootScope.pages = PAGES;
              $rootScope.Math = Math;
              
              $rootScope.$on("$stateChangeError", function(event, toState, toParams, fromState, fromParams, error) {
                  if (error && (error.status === 401 || error.status === 403)) {
                      event.preventDefault();
                      $state.loginRedirect = toState;
                      $state.go('login');
                  }
              });
          }]);
          
          angular.element(document).ready(function() {
              angular.bootstrap(document.documentElement, ['myApp']);
          });
          
          }); // define
          
          

          I took out all of the menu portions for the side nav and the extra pages. My dashboard is only one page at the moment.

          N 1 Reply Last reply Reply Quote 0
          • N
            nyoa @Brad_GMI
            last edited by

            @Brad_GMI

            Thank you a lot for your reference. I tried copying your app.js and see if it would work. It does the redirect to /#/login when the user is not logged in but the page shows my index.html for some reason. I'll upload a picture about the situation.

            My index.html is the same it was before with the exception that the only require line I have includes the app.js.

            0_1463067431657_customdashboard.PNG

            1 Reply Last reply Reply Quote 0
            • B
              Brad_GMI
              last edited by Brad_GMI

              Here is how my pages are structured:

              index.html

              <!DOCTYPE html>
              <html lang="en" class="no-js">
              <head>
                  <meta charset="utf-8">
                  <meta http-equiv="x-ua-compatible" content="ie=edge">
                  <title>Demo</title>
                  <meta name="description" content="">
                  <meta name="viewport" content="width=device-width, initial-scale=1">
                  <meta name="mobile-web-app-capable" content="yes">
                  <meta name="apple-mobile-web-app-capable" content="yes">
                  
                  <link rel="icon" type="image/png" sizes="192x192" href="../img/icon192.png">
                  <link rel="icon" type="image/png" sizes="128x128" href="../img/icon128.png">
                  <link rel="apple-touch-icon" type="image/png" sizes="128x128" href="../img/icon128.png">
                  <link rel="apple-touch-icon" type="image/png" sizes="128x128" href="../img/icon128.png">
                  <link rel="manifest" href="manifest.json">
                  
                  <link rel="stylesheet" href="/resources/angular-csp.css"></link>
                  <link rel="stylesheet" href="../vendor/angular-material/angular-material.css">
                  <link rel="stylesheet" href="../vendor/angular-loading-bar/loading-bar.css">
                  <link rel="stylesheet" href="../vendor/material-design-icons/iconfont/material-icons.css">
                  <link rel="stylesheet" href="../vendor/font-awesome/css/font-awesome.css">
                  <link rel="stylesheet" href="../vendor/mdPickers/mdPickers.css">
                  <link rel="stylesheet" href="../vendor/angular-material-data-table/md-data-table.css">
                  <link rel="stylesheet" href="styles/main.css">
              </head>
              
              <body layout="column">
              	<div ng-if="appLoading" class="app-loading">
              		<i class="fa fa-cog fa-spin"></i>
              	</div>
              
                  <div ui-view ng-cloak layout="column" flex></div>
              
                  <script src="/resources/require.js"></script>
                  <script src="/resources/loaderConfig.js"></script>
                  <script src="../js/loaderConfig.js"></script>
                  <script>require(['dashboards/Demo/app']);</script>
              </body>
              </html>
              

              main.html

              <div ui-view flex="noshrink"></div>
              
              

              You could do away with the main.html if you rework the code a bit, but I expect to use it further along in my dashboard development.

              The home.html page holds all of my dashboard content. So in order to make it work with my app.js, copy the <body> portion of your index.html into /views/dashboard/home.html and reduce the main.html to something similar to above.

              1 Reply Last reply Reply Quote 0
              • B
                Brad_GMI
                last edited by

                Your home.html would look something like this:

                <div id="maincontainer" ng-cloak>
                			<header>
                				<img src="images/jamkfi_tunnus_sininen_suomi.png" alt="jamk.fi" style="width:50%;height:50%;margin-left:25%;"> 
                			</header>
                			<div id="textContainer">
                				<img src="images/header.jpg" alt="Fosfaattifosfori PO4-P [µg/l]" style="width:50%;height:50%;margin-left:25%;"> 
                			</div>
                			<div>
                			
                				<ma-get-point-value point-xid="DP_355369" point="point1"></ma-get-point-value>
                
                				<ma-point-values id="testi" point="point1" values="point1Values" from="from" to="to" rollup="AVERAGE" rollup-interval="1 minutes">
                				</ma-point-values>
                
                				<ma-serial-chart id="chartti" style="height: 500px; width: 100%" series-1-values="point1Values" series-1-point="point1" options="{chartCursor:{categoryBalloonDateFormat:'YYYY-MM-DD HH:NN'}, chartScrollbar:{oppositeAxis: false, scrollbarHeight: 40,offset: 25, graph:'DP_355369'}}">
                				</ma-serial-chart>
                				
                				<div id = "control">
                					<div id="selectorDiv" layout="row">
                						<div class="selectorContainer">
                							<md-input-container flex="33" class="no-errors-spacer timeSelector">
                							   <label>Date preset</label>
                							   <ma-date-range-picker from="from" to="to" preset="LAST_1_DAYS" update-interval="1 minutes"></ma-date-range-picker>
                							</md-input-container>
                						</div>
                						<div class="selectorContainer">
                							<md-input-container flex="33" class="no-errors-spacer timeSelector">
                							   <label>From date</label>
                							   <ma-date-picker ng-model="from"></ma-date-picker>
                							</md-input-container>
                						</div>
                						<div class="selectorContainer">
                							<md-input-container flex="33" class="no-errors-spacer timeSelector">
                								<label>To date</label>
                								<ma-date-picker ng-model="to"></ma-date-picker>
                							</md-input-container>
                						</div>
                					</div>
                					<div>
                						<div>
                							<md-button class="md-raised" ng-click="showStats=true;showData=false" ng-hide="showStats">Statistics</md-button>
                							<md-button class="md-raised" ng-click="showStats=false;showData=false" ng-show="showStats">Statistics</md-button>
                							<md-button class="md-raised" ng-click="showData=true;showStats=false" ng-hide="showData">Data</md-button>
                							<md-button class="md-raised" ng-click="showData=false;showStats=false" ng-show="showData">Data</md-button>
                						</div>
                						<div ng-controller="myCtrl">
                							<md-button class="md-raised" ng-click="testi()">Push me</md-button>
                						</div>
                						<div layout="row" ng-show="showStats" class="ng-hide">
                							<ma-point-statistics point="point1" from="from" to="to" statistics="statsObj"></ma-point-statistics>
                
                							<ma-statistics-table id="statsTable" statistics="statsObj"></ma-statistics-table>
                						</div>
                						<div layout="row" ng-show="showData" class="ng-hide">
                							<table width="100%" height="500px">
                								<thead>
                									<tr>
                										<th>Value</th>
                										<th>Time</th>
                									</tr>
                								</thead>
                								<tbody>
                									<tr ng-repeat="item in point1Values">
                										<td>{{item.value}}</td>
                										<td>{{item.timestamp | moment:'format':'lll' }}</td>
                									</tr>
                								</tbody>
                							</table>
                						</div>
                					</div>
                				</div>
                			</div>
                		</div>
                
                1 Reply Last reply Reply Quote 0
                • N
                  nyoa
                  last edited by

                  Thank you a lot brad! Thanks to your reference I got it working. I really can't thank you enough for sharing your experience here!

                  I also got a lot clearer idea of the new dashboard module thanks to this discussion.

                  So to summarize all the steps that a newbie like me had to take to get authentication work in my custom dashboard:

                  1. Copy the files in adminTemplate (found at {mango root}\web\modules\dashboards\web) to my custom dashboard directory
                  2. Change the content of my app.js directory to reflect brads.
                  3. Copy the html data between the <body> tags in my old index.html to home.html found in views/dasboard/ directory
                  4. Replace my old index.html file with adminTemplate index.html with the require line modified to point to my dashboard (eg. <script>require(['dashboards/fosfaattifosfori/app']);</script>)
                  5. Changed my main.html to reflect what brad posted earlier

                  I believe steps 2 and 5 aren't necessary to get the thing working, but without those I'd have the side nav and the extra pages.

                  Thank you a lot for all the help you provided!

                  1 Reply Last reply Reply Quote 0
                  • Jared WiltshireJ
                    Jared Wiltshire
                    last edited by

                    Thanks for posting up your pages and helping out Brad!

                    I've added an example for a simple "Single Page App" with a login page using UI Router to the next version of the dashboards module. You can have a look here for reference in the meantime - https://github.com/infiniteautomation/ma-dashboards/tree/master/Custom Dashboards/web/loginPageTemplate

                    Developer at Radix IoT

                    W 1 Reply Last reply Reply Quote 0
                    • W
                      whileoneloop @Jared Wiltshire
                      last edited by

                      @Jared-Wiltshire the link https://github.com/infiniteautomation/ma-dashboards/tree/master/Custom Dashboards/web/loginPageTemplate isn't working atm :)

                      1 Reply Last reply Reply Quote 0
                      • Jared WiltshireJ
                        Jared Wiltshire
                        last edited by

                        @whileoneloop the link will be https://github.com/infiniteautomation/ma-dashboards/tree/2.7.x/Custom Dashboards/web/loginPageTemplate now

                        Developer at Radix IoT

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post