• 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

    Styling a Custom Component in userModule

    Dashboard Designer & Custom AngularJS Pages
    4
    17
    4.1k
    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.
    • P
      pyeager
      last edited by

      I have gotten the depiction of a machine in a dashboard to work as desired, but I am having trouble wrapping my head around how to make it component.

      Here is what I am doing right now:

      Some css to style the background of the image to indicate status:

      .idle { background-color: rgba( 255, 255, 255, 0.5); }
      .heat { background-color: rgba( 255, 255, 0, 0.5 ); }
      .air { background-color: rgba( 3, 186, 252, 0.5 ); }
      .heatair { background-image: url("/rest/v2/file-stores/default/HeatAir.png"); }
      .a0 { background-image: url("/rest/v2/file-stores/default/A0.png"); }
      .s1 { background-image: url("/rest/v2/file-stores/default/Stage1.png"); }
      .s2 { background-image: url("/rest/v2/file-stores/default/Stage2.png"); }
      .s3 { background-image: url("/rest/v2/file-stores/default/Stage3.png"); }
      .manual { background-color: rgba( 0, 0, 0, 0.5); }
      

      A call to ma-get-point-value to get the status of the machine:

      <ma-get-point-value id="a5ed9ee7-925f-4950-a46a-e4b02113c062" style="position: absolute; left: 610px; top: 530px;" point="sequence1" point-xid="DP_6122694c-e93e-48d0-a423-2b23d88b45f9"></ma-get-point-value>
      

      And ng-class to set the class based on the status of the machine:

      <img id="ed3362f5-9f30-47f8-877b-6eb1fa0f3f19" style="position: absolute; left: 125px; top: 170px; width: 30px; height: 28px;" src="/rest/v2/file-stores/default/HKDStickL.png" ng-click="designer.parameters = {dn: 'sg-lsb-01v'}" ng-class="{0:'idle', 1:'heat', 2:'air', 3:'heatair', 4:'a0', 5:'s1', 6:'s2', 7:'s3', 8:'manual'} [sequence1.value]">
      

      This works perfectly, except for one thing. Adding machines is more work than I expect my users to do acccurately. The obvious answer seems to be a custom component.

      I have built a userModule and started to write the code to implement it, but I am having trouble figuring out how to style the component based on a data point.

      MattFoxM 1 Reply Last reply Reply Quote 0
      • MattFoxM
        MattFox @pyeager
        last edited by MattFox

        @pyeager
        Take the datapoint value as a binding input. If you're using a component, use the

        if(e.ptVal && e.ptVal.currentValue!=undefined)
        runValCheck();
        }
        

        After you check your value, (in this case
        this.ptVal ) use the angular.addClass and removeClass functioms to alter your component's styling. With this in mind have a second variable to hold the last class value so you know what to remove.
        I'm a few hours a away from work. Can assist further when at the PC.

        Fox we

        Do not follow where the path may lead; go instead where there is no path.
        And leave a trail - Muriel Strode

        P 1 Reply Last reply Reply Quote 0
        • P
          pyeager @MattFox
          last edited by

          @mattfox I'm brand new to angular. Where does the code you provided go?

          1 Reply Last reply Reply Quote 0
          • MattFoxM
            MattFox
            last edited by MattFox

            Ah right sorry,
            From what it looks like, you want a generic solution to provide users a drag and drop component I'll give you the bones of a component to fill in. The html should probably be in a separate template file.
            Is your userModule in the mango file store or on disk?

            Fox

            EDIT: Have you made a start? If so we can build from that.

            Do not follow where the path may lead; go instead where there is no path.
            And leave a trail - Muriel Strode

            P 1 Reply Last reply Reply Quote 0
            • P
              pyeager @MattFox
              last edited by

              @mattfox said in Styling a Custom Component in userModule:

              From what it looks like, you want a generic solution to provide users a drag and drop component I'll give you the bones of a component to fill in. The html should probably be in a separate template file.

              Yes, I need to build a drag and drop component. I have already figured out how to use a separate template file. Beyond that, the code is little changed from the example code.

              define(['angular', 'require'], function(angular, require) {
              'use strict';
              
              var userModule = angular.module('userModule', ['maUiApp']);
              
              userModule.component('stickGun', {
                  bindings: {
                      name: '@?',
                      face: '@?'
                  },
                  templateUrl: '/rest/v2/file-stores/public/userModule.html',
                  styleUrls: ['/rest/v2/file-stores/public/userModule.css']
              });
              
              return userModule;
              
              }); // define
              

              Is your userModule in the mango file store or on disk?

              It is in the public folder in the file store. That also appears to be on disk at /opt/mango/filestore/public

              1 Reply Last reply Reply Quote 0
              • MattFoxM
                MattFox
                last edited by MattFox

                Cool thanks Pyeager, I'll get something knocked together now. Secondly, you're best to use a separate template url per component. I'd strongly suggest creating a /rest/v2/file-stores/public/stickGun.html html file, so you know what the contents are without having to open it up.

                That also appears to be on disk at /opt/mango/filestore/public

                Sorry I meant outside of mango in the overrides or something. Just in case upgrades or something causes an errant spanner to be thrown... ;) - It also allows you to add source control so you can store your files in a git or svn based solution, very handy for versioning, and who to blame when something breaks (haha)

                Fox

                Do not follow where the path may lead; go instead where there is no path.
                And leave a trail - Muriel Strode

                1 Reply Last reply Reply Quote 0
                • MattFoxM
                  MattFox
                  last edited by

                  Here's something to get you started, I'm not sure what the purpose of the ng-click is for your image, if you are able to explain i'll likely be able to assist with that part.
                  I am not sure what name and face are for either.
                  Basically with the component, you're bringing all of the dynamic variables to the surface as bindings for the component. From here you do all of your calcs and fancy doodads inside the component's controller.

                  Your html:

                  <section>
                  <style>
                  .idle { background-color: rgba( 255, 255, 255, 0.5); }
                  .heat { background-color: rgba( 255, 255, 0, 0.5 ); }
                  .air { background-color: rgba( 3, 186, 252, 0.5 ); }
                  .heatair { background-image: url("/rest/v2/file-stores/default/HeatAir.png"); }
                  .a0 { background-image: url("/rest/v2/file-stores/default/A0.png"); }
                  .s1 { background-image: url("/rest/v2/file-stores/default/Stage1.png"); }
                  .s2 { background-image: url("/rest/v2/file-stores/default/Stage2.png"); }
                  .s3 { background-image: url("/rest/v2/file-stores/default/Stage3.png"); }
                  .manual { background-color: rgba( 0, 0, 0, 0.5); }
                  </style>
                  <ma-get-point-value point="sequence1" point-xid="{{$ctrl.pointXid}}"></ma-get-point-value>
                  <!-- ng-click="designer.parameters = {dn: 'sg-lsb-01v'}" What is this for?? -->
                  <img id="{{$ctrl.pointXid}}_img" style="width: 30px; height: 28px;" src="/rest/v2/file-stores/default/HKDStickL.png" ng-click="designer.parameters = {dn: 'sg-lsb-01v'}" ng-class="{0:'idle', 1:'heat', 2:'air', 3:'heatair', 4:'a0', 5:'s1', 6:'s2', 7:'s3', 8:'manual'} [sequence1.value]">
                  </section>
                  

                  Your component

                  userModule.component('stickGun', {
                      bindings: {
                          name: '>?',  //1-way binding optional
                          face: '>?',
                  		pointXid:'>' //required
                      },
                      templateUrl: '/rest/v2/file-stores/public/stickGun.html',
                      styleUrls: ['/rest/v2/file-stores/public/userModule.css'], //not hugely needed, especically if you use the user styles file to store all your css in. Unless you plan to take this component to other places...
                  	controller:function(){
                  		var ctrl = this;
                  		this.$onChanges = function(e)
                  		{
                  			//binding checks are done in here. this is for an example of what I stated prior if you are using optional bindings
                  			if(e.pointXid && e.pointXid.currentValue!=undefined)
                  			{
                  				ctrl.pointXid = e.pointXid.currentValue;
                  			}
                  		};
                  		
                  	}
                  });
                  

                  Do not follow where the path may lead; go instead where there is no path.
                  And leave a trail - Muriel Strode

                  P 1 Reply Last reply Reply Quote 1
                  • P
                    pyeager @MattFox
                    last edited by

                    @mattfox Thanks!

                    It's quitting time. so I'll play with it tomorrow.

                    When the image is clicked, ng-click set which device's details are shown on a card.

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

                      @pyeager This is what I would do, based off your code. Note that styleUrls seems to be Angular 2+ syntax, not AngularJS (1.x). Put your CSS in the user styles style sheet (under UI settings).

                      User module -

                      define(['angular', 'require'], function(angular, require) {
                      'use strict';
                      
                      var userModule = angular.module('userModule', ['maUiApp']);
                      
                      // doesn't do anything, but here for completeness
                      class StickGunController {
                      }
                      
                      userModule.component('stickGun', {
                          bindings: {
                              name: '@?', // dont know what these are for, but sure
                              face: '@?',
                              point: '<?'
                          },
                          templateUrl: '/rest/v2/file-stores/public/stickGun.html',
                          controller: StickGunController
                      });
                      
                      return userModule;
                      
                      }); // define
                      

                      Template file (/rest/v2/file-stores/public/stickGun.html) -

                      <ma-get-point-value point="$ctrl.point"></ma-get-point-value>
                      <img src="/rest/v2/file-stores/default/HKDStickL.png" ng-class="{0:'idle', 1:'heat', 2:'air', 3:'heatair', 4:'a0', 5:'s1', 6:'s2', 7:'s3', 8:'manual'}[$ctrl.point.value]">
                      

                      CSS file -

                      stick-gun {
                          display: block;
                      }
                      stick-gun > img {
                          width: 100%;
                          height: 100%;
                      }
                      stick-gun > img.idle { background-color: rgba( 255, 255, 255, 0.5); }
                      stick-gun > img.heat { background-color: rgba( 255, 255, 0, 0.5 ); }
                      stick-gun > img.air { background-color: rgba( 3, 186, 252, 0.5 ); }
                      stick-gun > img.heatair { background-image: url("/rest/v2/file-stores/default/HeatAir.png"); }
                      stick-gun > img.a0 { background-image: url("/rest/v2/file-stores/default/A0.png"); }
                      stick-gun > img.s1 { background-image: url("/rest/v2/file-stores/default/Stage1.png"); }
                      stick-gun > img.s2 { background-image: url("/rest/v2/file-stores/default/Stage2.png"); }
                      stick-gun > img.s3 { background-image: url("/rest/v2/file-stores/default/Stage3.png"); }
                      stick-gun > img.manual { background-color: rgba( 0, 0, 0, 0.5); }
                      

                      Developer at Radix IoT

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

                        Oh, and to use, you just add this to your page -

                        <stick-gun point="myPoint"></stick-gun>
                        

                        Developer at Radix IoT

                        1 Reply Last reply Reply Quote 0
                        • MattFoxM
                          MattFox
                          last edited by

                          Thanks Jared, appreciate the support!

                          Do not follow where the path may lead; go instead where there is no path.
                          And leave a trail - Muriel Strode

                          1 Reply Last reply Reply Quote 0
                          • P
                            pyeager
                            last edited by

                            Thank you both for your help.

                            I might need just a little more.

                            Specifying a data point by xid, as both of your examples do, is a bit less than optimal for a couple of reasons.

                            For one thing, there are multiple data points with which the component needs to interact.

                            Also, specifying one or more data points by xid is less than user friendly. My preferred approach is to just name the component instance in such a way that queries can be constructed from the instance name, removing the need to specify an xid for each data point.

                            ...and additionally...

                            This may be asking a lot, but ideally adding this component to a dashboard would also create a few data sources, likely from a template. That would make the drag and drop addition of the component and definition of element-specific details pretty much all that is necessary to add a machine to the system.

                            Jared WiltshireJ MattFoxM 2 Replies Last reply Reply Quote 0
                            • Jared WiltshireJ
                              Jared Wiltshire @pyeager
                              last edited by

                              @pyeager said in Styling a Custom Component in userModule:

                              Specifying a data point by xid, as both of your examples do, is a bit less than optimal for a couple of reasons.

                              My example does not specify a data point by XID, it passes a single data point through.

                              @pyeager said in Styling a Custom Component in userModule:

                              For one thing, there are multiple data points with which the component needs to interact.

                              In this case you could pass a whole array of data points through (from a query or Watchlist) and filter the points out inside the component.

                              @pyeager said in Styling a Custom Component in userModule:

                              Also, specifying one or more data points by xid is less than user friendly. My preferred approach is to just name the component instance in such a way that queries can be constructed from the instance name, removing the need to specify an xid for each data point.

                              I agree you should not directly bind components to a specify XID. This is why our dashboard designer supports selecting a watchlist then binding components to data points using their name.

                              If you want to use a name attribute for your components this would work in a similar fashion. Like I said above, pass the whole points array through to the component and then find the point you want inside the component using the name attribute.

                              e.g. controller for your component

                              class StickGunController {
                                $onChanges(changes) {
                                  if (changes.points && Array.isArray(this.points)) {
                                    this.point = this.points.find(pt => pt.name === this.name);
                                  }
                                }
                              }
                              

                              @pyeager said in Styling a Custom Component in userModule:

                              This may be asking a lot, but ideally adding this component to a dashboard would also create a few data sources, likely from a template. That would make the drag and drop addition of the component and definition of element-specific details pretty much all that is necessary to add a machine to the system.

                              Certainly possible, I don't have time to get right into this right now but you could use the maDataSource service to check for the existence of the DS and create it if one matching the name attribute doesn't exist.

                              Developer at Radix IoT

                              P 1 Reply Last reply Reply Quote 1
                              • P
                                pyeager @Jared Wiltshire
                                last edited by

                                @jared-wiltshire said in Styling a Custom Component in userModule:

                                Like I said above, pass the whole points array through to the component and then find the point you want inside the component using the name attribute.

                                How does one pass the whole points array to the component?

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

                                  By defining "points" as an input of the component, as I did in this post: https://forum.infiniteautomation.com/topic/3503/how-to-chart-with-data-points-in-the-x-axis-instead-of-time/6

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

                                    @pyeager said in Styling a Custom Component in userModule:

                                    How does one pass the whole points array to the component?

                                    Are you asking how to define the binding in the component or how to pass the points to the attribute when you are using it?

                                    To define the binding, you literally just have to change point to points in the example I posted above. I gave you a controller that will locate the point from the array using the name attribute.

                                    If you meant the latter, I suggest you peruse the examples that are built into Mango (enable the examples menu item using the menu editor if you have not already).

                                    Developer at Radix IoT

                                    1 Reply Last reply Reply Quote 0
                                    • MattFoxM
                                      MattFox @pyeager
                                      last edited by

                                      @pyeager said in Styling a Custom Component in userModule:

                                      For one thing, there are multiple data points with which the component needs to interact.

                                      You never stated this initially

                                      @jared-wiltshire said in Styling a Custom Component in userModule:

                                      Certainly possible, I don't have time to get right into this right now but you could use the maDataSource service to check for the existence of the DS and create it if one matching the name attribute doesn't exist.

                                      If this is remotely similar to making a new point using the maPoint service perhaps I can be of assistance here

                                      Do not follow where the path may lead; go instead where there is no path.
                                      And leave a trail - Muriel Strode

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