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

  • What is the best way to define colors for AMCharts? We have many different charts and I would like to define the colors globally in a CSS file for example.

    <ma-point-values point="EZR_T" values="EZR_T_Values" rendered="true" from="dateBar.from" to="dateBar.to" rollup="AVERAGE" rollup-interval="{{dateBar.rollupIntervals}} {{dateBar.rollupIntervalPeriod}}"></ma-point-values>
    <ma-serial-chart flex style="width: auto; height: 450px;" 
      series-1-point="EZR_T" 
      series-1-values="EZR_T_Values" 
      series-1-color="green" 
      series-1-type="line" 
      series-1-title="Raumtemperatur"
    
    legend="true" balloon="true" default-balloon-text="[[title]]: [[value]]" export="true" stack-type="none" options="{valueAxes:[{title:'°C'}, {title:'kW'}]}">
    </ma-serial-chart>
    

    I would like to have series-1-color="green" be something like series-1-color="{{green}}" or insert a name of a class from a stylesheet.

    Is there a good way? Thanks for your help!


  • you can do soemthing like this on your main div:
    <div ng-init="color ={green:'green}">
    Then on your chart, you can do series-1-color="color.green"


  • Hi @Jonas the series colors are by default the color set to the chart color on the data point, you can edit the data points using the bulk editor to set their colors.

    You can also use CSS to style other parts of the chart. You can define a global CSS file on the UI settings page. Use the chrome element inspector to discover the CSS class on the SVG element inside the chart.

    edit. See this thread - https://forum.infiniteautomation.com/topic/4150/styling-amcharts
    Also, amcharts CSS reference - https://www.amcharts.com/docs/v3/tutorials/css-class-names/


  • @craigweb I created a colors-page with this code:

    <div ng-init="colorRed='#F44336'"></div>
    <div ng-init="colorRed2='#E57373'"></div>
    <div ng-init="colorOrange='#EF6C00'"></div>
    <div ng-init="colorOrange2='#FF9800'"></div>
    

    And on the page with the AmCharts I used the ma-ui-page-view with the XID of the colors-page but it doesn't work. It only works if the ng-init definitions are on the same page where the chart is defined.

    But I would like to have one place where I can define 20+ colors to use it on different pages / charts and SVG. I learned that CSS would be the place to do it but I cannot get it to work with amcharts CSS reference.


  • @jonas said in A global way to define colors for AMCharts:

    But I would like to have one place where I can define 20+ colors to use it on different pages / charts and SVG. I learned that CSS would be the place to do it but I cannot get it to work with amcharts CSS reference.

    Just a quick thought, but have you added all of your css styles into a file that can be accessed by the user css styles under administration->ui settings->Miscellaneous settings->User stylesheet url?
    That will always be loaded and will enable you to have your global css styles.

    Secondly, assuming you're using google chrome, using developer tools you can inspect the chart and select the html elements corresponding to the chart series in the amchart instance and 'create' styles for it. This can be done with the css styles window on the right hand side of the element window. This will allow you to ensure your naming conventions are correct for applying the styles you want - even copying and pasting them into your new global styles file so no concerns for misstypes etc...


  • @jonas
    If you don't go the styles route which is probably better. You can store the colours in a JSON store. Then use the JSON component to retrieve them in each page.


  • @mattfox Thanks for your input.
    I use a global css stylesheet via the ui-settings.
    But the css class of an AMChart line is always the same. amcharts-graph-stroke or amcharts-graph-series-1 and we have 50+ charts and series-1 isn't always "blue" for example.

    And if I add series-1-class="colorRed" or series-1-className="colorRed" to the chart settings isn't working. But that's what I basically need. Add my own class to every series because I already defined all my colors in my stylesheet.


  • Have you considered using the point default chart colour?


  • @craigweb There are often two different variants of one color. And predefined colors in the client industry. Room temperature, for example, is green. And outside temperature light green.

    I have a look at your recommended JSON component way now.


  • Sounds like you need a chart profile configured in the graph options to set line colours. I've done something similar for highcharts that I've embedded into my dashboard. But am storing in a separate database.
    I can look into coding something for you because I believe it will be beneficial for the mango community. I'll prob bastardise what jared has done from the watchlist page (it's good code!) and incorporate that into saving it as a profile in a json data file.
    Let me know your thoughts

    Fox


  • @mattfox Hi!
    We use a json component now and its working. But if you can build a better solution, we would definitely use it.

    For everyone who is looking for a solution:
    Insert this in your page with the AMChart

    <ma-json-store xid="colours" item="myItem"></ma-json-store>
    

    And our json with the xid "colours" is something like:

    {
        "Red": "#FF4040",
        "Orange": "#CD661D",
        "Grey": "#68838B"
    }
    

    And in the AMChart we use

    series-1-color="{{myItem.jsonData.Orange}}" 
    

  • Nicely done Jonas, I'll throw together a UI directive to do this, shouldn't take tooo long.
    However, what I'll do is make it so that the graphs save with the names series1, series2 etc
    You can separate chart profiles with a non white space name.


  • OK! As promised, here we are. I'm using 3.5.6 so I'm not entirely certain how well this will work. I've stuck to mango components to the best of my ability to allow the best compatibility sans the colour picker. If it doesn't work you may need to alter the HTML to use <ma-color-picker instead.
    You'll need to set up a userModule file to incorporate this.
    I've saved my files in /opt/mango/overrides/web/modules/mangoUI/web/dev/directives/chartProfile
    the userModule.js is in /opt/mango/overrides/web/modules/mangoUI/web/dev
    For the mangoUI settings, the url for the userModule.js file is:

    /modules/mangoUI/web/dev/userModule.js
    

    The user module file contains this:

    define(['angular', 'require','./directives/chartProfile/serialChartProfile.js'],function(angular, require,serialChartProfile) 
    {
    	
     'use strict';
    var userModule = angular.module('userModule', ['maUiApp']);
    
    	 try {			
    	userModule.directive('serialChartProfile', serialChartProfile);	
    	 }catch(e){console.log(e);}
    return userModule;
    });//define
    

    serialChartProfile.js

    /*
    Date: 13/8/19
    File:serialChartProfile.js
    Author: Matt 'Fox' Fox
    Desc: Directive that allows mangoUI users to create chart profiles for their dashboard pages. Name of the JSON store can be set as well as the 
    names of each of the chart profiles.
    Credit goes to Will Geller and Jared Wiltshire for original codebase
    */
    
    define(['angular', 'require'],function(angular, require)
    {
    'use strict';
     serialChartProfile.$inject = ['maJsonStore','$mdDialog','$mdColorPicker','$mdToast'];
     
    function serialChartProfile( JsonStore, $mdDialog, $mdColorPicker,$mdToast ) 
    {	
    	console.log($mdColorPicker);
    	
    var scope = {
    		jsonStore: '<?',//has default option 	    
    		};
    		
    	return {
            restrict: 'E',
    		templateUrl:'/modules/mangoUI/web/dev/directives/chartProfile/chartProfile.html',
            designerInfo: {
                translation: 'ui.components.serialChartProfile',
                icon: 'show_chart',
                category: 'pointValuesAndCharts',
                attributes: {
                    jsonStore: {type: 'text'}
    			},
    		},
            scope: scope,
    		 compile: function() {
    			return linkFn;
    		 }
    	};	
    	
    	
    	function linkFn(scope, $element, attrs) 
    	{
    		scope.profileStore={};
    		
    		/* Default for ensuring we have a json store name */
    		var hasStoreName = !!attrs.jsonStore;
    		
    		if(!hasStoreName)
    		{
    			scope.jsonStore = "chartProfile1";
    			getChartProfiles();
    		}
    		else
    		{
    			if( scope.jsonStore.length < 3 )
    			{
    				scope.jsonStore = "chartProfile1";
    				getChartProfiles();
    			}
                           else
    {
    getChartProfiles();}
    		}
    	
    		scope.axisOptions = [
                {name: 'left', translation: 'ui.app.left'},
                {name: 'right', translation: 'ui.app.right'},
                {name: 'left-2', translation: 'ui.app.farLeft'},
                {name: 'right-2', translation: 'ui.app.farRight'}
            ];
    	
    		
    		function getChartProfiles()
    		{
    			JsonStore.get({
    				 xid: scope.jsonStore
    				
    			}).$promise.then(function (item) {
    				scope.profileStore = item.jsonData;
    				scope.chartProfile = Object.keys(scope.profileStore)[0];
    				
    			}, function () {
    				//create prompt - no data
    				scope.createProfile();
    				
    			}).then(function()
    			{
    			}); 
    		}
    		
    		scope.saveToStore=function() {
    
    			var item = new JsonStore();
    			item.jsonData = scope.profileStore;
    			item.xid = scope.jsonStore;
    			item.name = scope.jsonStore;
    			item.readPermission = "user";
    			item.editPermission = "user";
    
    			var r = item.$save();
    			
    			r.then(function(){$mdToast.show(
    					$mdToast.simple()
    					.textContent("Chart Profiles Saved")
    					.position("top")
    					.hideDelay(3000));
    					
    					
    			  }, function myError(response) {
    				console.log( response.statusText );
    				$mdToast.show(
    					$mdToast.simple()
    					.textContent(response.data)
    					.position("top")
    					.hideDelay(3000));
    			  });
    			
    			if (r.status) {}
    			else {}
    			
    		};
    		
    		scope.showColorPicker = function($event, object, propertyName, rebuild) {
    			if (!object) return;
    
    			this.$mdColorPicker.show({
    				value: object[propertyName] || tinycolor.random().toHexString(),
    				defaultValue: '',
    				random: false,
    				clickOutsideToClose: true,
    				hasBackdrop: true,
    				skipHide: false,
    				preserveScope: false,
    				mdColorAlphaChannel: true,
    				mdColorSpectrum: true,
    				mdColorSliders: false,
    				mdColorGenericPalette: true,
    				mdColorMaterialPalette: false,
    				mdColorHistory: false,
    				mdColorDefaultTab: 0,
    				$event: $event
    			}).then(color => {
    				object[propertyName] = color;
    				
    			});
    		};
    		
    		
    		scope.createProfile = function(ev) {
    			// Appending dialog to document.body to cover sidenav in docs app
    			var confirm = $mdDialog.prompt()
    			  .title('Create New Chart Profile')
    			  .textContent('Enter new profile name:')
    			  .placeholder('PowerUsage')
    			  .ariaLabel('PowerUsage')
    			  .initialValue('chartProfile1')
    			  .targetEvent(ev)
    			  .required(true)
    			  .ok('CREATE')
    			  .cancel('CANCEL');
    
    			$mdDialog.show(confirm).then(function(result) {
    			  scope.profileStore[ result ] = {};
    			  scope.chartProfile = result;
    			}, function() {
    			  //Do nothing and hide.
    			
    			});
    		  };
    		  
    		  scope.deleteProfile = function(ev) {
    				// Appending dialog to document.body to cover sidenav in docs app
    				var confirm = $mdDialog.confirm()
    					  .title('Delete Profile')
    					  .textContent('Are you sure you want to delete this profile?')
    					  .ariaLabel('Delete Chart Profile')
    					  .targetEvent(ev)
    					  .ok('Confirm Delete')
    					  .cancel('Cancel');
    
    				$mdDialog.show(confirm).then(function() {
    					delete(scope.profileStore[scope.chartProfile]);
    				  scope.status = 'Profile Deleted, save settings to confirm';
    				  
    				}, function() {
    				  $scope.status = 'Operation cancelled';
    				});
    			  }; 
    	}
    	
    }
    	return serialChartProfile;
    });//define
    

    chartProfile.html

    <main>
    <!-- <ma-json-store xid="jsonStore" name="jsonStore"></ma-json-store> -->
    <ma-button ng-click="createProfile()" label="Create Profile"></ma-button>
    <ma-button ng-click="saveToStore()" label="Save All Profiles"></ma-button>
    <ma-button ng-click="deleteProfile()" label="Delete Selected Profile"></ma-button>
    <md-select ng-model="chartProfile">
    <md-option ng-repeat="(profile,vals) in profileStore" value="{{profile}}">{{profile}}</md-option>
    </md-select>
     <div class="md-padding">
            <div ng-if="chartProfile.length>3">
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series1"><span>Configure Series 1:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series2"><span>Configure Series 2:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series3"><span>Configure Series 3:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series4"><span>Configure Series 4:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series5"><span>Configure Series 5:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series6"><span>Configure Series 6:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series7"><span>Configure Series 7:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series8"><span>Configure Series 8:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series9"><span>Configure Series 9:</span></md-checkbox>
                <md-checkbox ng-model="profileStore[chartProfile].enabled.series10"><span>Configure Series 10:</span></md-checkbox>
           
    		{{chartProfile.enabled}}
            <div layout layout-align="start center" layout-wrap ng-repeat="(key,val) in profileStore[chartProfile].enabled" ng-if="val">
                <strong flex="100" flex-gt-xs="30" flex-gt-md="15">Configure {{key}}</strong>
    			<md-input-container flex>
                	<label ma-tr="ui.app.lineColor"></label>
                <md-color-picker ng-model="profileStore[chartProfile][key].pointColor"></md-color-picker>
                </md-input-container> 
                
                <!-- 
    			<md-input-container flex>
                	<label ma-tr="ui.app.lineColor"></label>
                    <input ng-model="profileStore[chartProfile][key].pointColor" ng-model-options="{debounce: 1000}">
                </md-input-container> 
    			-->
                
                <md-input-container flex>
                    <label ma-tr="ui.app.chartType"></label>
                    <md-select ng-model="profileStore[chartProfile][key].type">
                        <md-option value=""><em ma-tr="ui.app.clear"></em></md-option>
                        <md-option value="smoothedLine" ma-tr="ui.app.smooth"></md-option>
                        <md-option value="line" ma-tr="ui.app.line"></md-option>
                        <md-option value="step" ma-tr="ui.app.step"></md-option>
                        <md-option value="column" ma-tr="ui.app.bar"></md-option>
                    </md-select>
                </md-input-container>
    
                <md-input-container flex>
                    <label ma-tr="ui.app.pointAxis"></label>
                    <md-select ng-model="profileStore[chartProfile][key].axis">
                        <md-option value=""><em ma-tr="ui.app.clear"></em></md-option>
                        <md-option ng-repeat="axis in axisOptions track by axis.name" ng-value="axis.name">
                            <span ma-tr="{{axis.translation}}"></span>
                        </md-option>
                    </md-select>
                </md-input-container>
            </div>
            
    
            <div>
                <md-checkbox ng-model="profileStore[chartProfile].configAxes"><span ma-tr="ui.app.configureAxes"></span></md-checkbox>
            </div>
            <div ng-if="profileStore[chartProfile].configAxes" ng-repeat="axis in axisOptions track by axis.name" layout="row" layout-align="start center" layout-wrap>
                <strong flex="100" flex-gt-xs="15" flex-gt-md="10" ma-tr="{{axis.translation}}"></strong>
                
                <div flex="100" flex-gt-xs="85" flex-gt-md="90" layout layout-align="start center" layout-wrap>
                    <md-input-container flex="50" flex-gt-xs="33">
                        <label ma-tr="ui.app.axisTitle"></label>
                        <input ma-empty-input="undefined" ng-model="profileStore[chartProfile].valueAxes[axis.name].title" ng-model-options="{debounce: 1000}" >
                    </md-input-container>
                    
                    <div flex="50" flex-gt-xs="33" layout layout-align="start center">
                            <label ma-tr="ui.app.axisColor"></label>
                        <md-color-picker ng-model="profileStore[chartProfile].valueAxes[axis.name].color" ></md-color-picker>
                        <!-- <ma-color-picker ng-model="profileStore[chartProfile].valueAxes[axis.name].color" ></ma-color-picker> -->
                        
                        <!-- <md-input-container flex>
                            <label ma-tr="ui.app.axisColor"></label>
                            <input ma-empty-input="undefined" ng-model="profileStore[chartProfile].valueAxes[axis.name].color" ng-model-options="{debounce: 1000}" >
                        </md-input-container> -->
                    </div>
        
                    <md-input-container flex="50" flex-gt-xs="33">
                        <label ma-tr="ui.app.stackType"></label>
                        <md-select ma-empty-input="undefined" ng-model="profileStore[chartProfile].valueAxes[axis.name].stackType" >
                            <md-option value=""><em ma-tr="ui.app.clear"></em></md-option>
                            <md-option value="none" ma-tr="ui.app.none"></md-option>
                            <md-option value="regular" ma-tr="ui.app.stacked"></md-option>
                            <md-option value="100%">100% <span ma-tr="ui.app.stacked"></span></md-option>
                        </md-select>
                    </md-input-container>
        
                    <md-input-container flex="50" flex-gt-xs="33">
                        <label ma-tr="ui.app.axisMinimum"></label>
                        <input ma-empty-input="undefined" type="number"
                            ng-model="profileStore[chartProfile].valueAxes[axis.name].minimum"
                            ng-model-options="{debounce: 1000}" >
                    </md-input-container>
        
                    <md-input-container flex="50" flex-gt-xs="33">
                        <label ma-tr="ui.app.axisMaximum"></label>
                        <input ma-empty-input="undefined" type="number"
                            ng-model="profileStore[chartProfile].valueAxes[axis.name].maximum"
                            ng-model-options="{debounce: 1000}" >
                    </md-input-container>
        
                    <md-input-container flex="50" flex-gt-xs="33">
                        <label ma-tr="ui.app.axisGridCount"></label>
                        <input ma-empty-input="undefined" type="number" min="0"
                            ng-model="profileStore[chartProfile].valueAxes[axis.name].gridCount"
                            ng-model-options="{debounce: 1000}" >
                    </md-input-container>
                </div>
            </div>
          </div>
    	  <style>
    	  .md-color-picker-input{color:black!important;}</style>
    </main>
    
    <!-- json-store is optional;  -->
    <serial-chart-profile [json-store='jsonStoreName'] ></serial-chart-profile> 
    

    Keep me informed, hopefully others can use this to make their chart templates for different statuses more dynamically!!
    I won't lie, this may require further testing... and more comments!
    Fox