Help getting ma-watch-list-get points into a controller/service
-
- Do not use the
ng-controller
attribute - Do not use
module.controller()
- Don't set properties on the
$rootScope
- Prefer
.controller({opts})
instead of.directive(function() { return {opts}; })
- Instead of
controller: 'PushCtrl'
usecontroller: ['$scope', function($scope) { // do something }]
or (my preference):
class PushCtrl { static get $$ngIsClass() { return true; } static get $inject() { return ['$scope']; } constructor($scope) { this.$scope = $scope; } } // then .controller: PushCtrl // no single quotes
Basically the way you are using the PushCtrl controller in
ng-controller
then also as the controller for the<push-lamp>
directive makes no sense. And<push-lamp points="point"
is never going to work as it is the wrong variable name. - Do not use the
-
Also use the
$onChanges()
hook of the controller rather than setting a timeout. -
Basically I think it boils down to this -
<div ng-init="page = {}"></div> <ma-watch-list-get ng-model="indicatorWl.watchlist" parameters="indicatorWl.parameters" on-points-change="page.points = $points" id="wl-indicator-id" watch-list-xid="WL_Indicator"></ma-watch-list-get> <ma-get-point-value points="page.points"></ma-get-point-value> <div> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT1'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT2'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT3'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OMX'}:true | maFirst"></push-lamp> </div>
User module:
module.component('pushLamp', { template: `<div class="indicator" ng-class="$ctrl.point.renderedValue" <br/>{{$ctrl.point.xid}}; {{$ctrl.point.name}}<br/>{{$ctrl.point.tags.indText}}</div>`, bindings: { point: '<' } });
-
Thank you @Jared-Wiltshire. interesting. Ok I'll give it a try. Could you clarify the use of $onChanges() with the controller? would it be in this case
$onChanges($ctrl.point) {...}
?
-
$onChanges(changes) { if (changes.point) { // point changed } }
Have a read of https://docs.angularjs.org/guide/component for more details.
-
aha. thanks. I also have to toggle a different binary point when this element is clicked. Do I need to use the api to do that, or is there a different way to set a point's value (which point's xid can be passed in as an attr of the <push-lamp> or calculated?
and, would I just include that function in the component's controller?Thank you - I'm drinking 1.5 from a firehose (and Tom Motto!) -
Thanks very much - that all worked.
<div ng-init="page = {}"></div> <ma-watch-list-get ng-model="indicatorWl.watchlist" parameters="indicatorWl.parameters" on-points-change="page.points = $points" id="wl-indicator-id" watch-list-xid="WL_Indicator"></ma-watch-list-get> <ma-get-point-value points="page.points"></ma-get-point-value> <div class="ma-designer-root" id="30987a71-8c32-4308-a9ae-3fdff231c44b" style="width: 1366px; height: 768px; position: relative;"> <div> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT1'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT2'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OT3'}:true | maFirst"></push-lamp> <push-lamp point="page.points | filter:{xid:'DP_ind_H2OMX'}:true | maFirst"></push-lamp> </div> </div>
user module:
define(['angular', 'require'], function (angular, require) { 'use strict'; var userModule = angular.module('userModule', ['maUiApp']) .component('pushLamp', { template: `<div class="indicator" ng-click="$ctrl.pushed(this.$ctrl.point.xid)" ng-class="$ctrl.point.renderedValue"<br/>{{$ctrl.point.xid}}; {{$ctrl.point.name}}<br/>{{$ctrl.point.tags.indText}}</div>`, bindings: { point: '<' }, controller: function() { var self = this; self.$onChanges = function (changes) { if (changes.point) { console.log(self.point.value); } } self.pushed = function(pid) { var swXid = 'DP_sw_' + pid.substr(7); // toggle point.xid=swXid console.log(swXid, ' was clicked'); } } }); return userModule; }); // define
If I could just get your opinion on preferred way to toggle point... also would appreciate any comments on view syntax/order.
Best.
-
@bobday said in Help getting ma-watch-list-get points into a controller/service:
If I could just get your opinion on preferred way to toggle point... also would appreciate any comments on view syntax/order.
You should use the
maPoint
class'toggleValue()
method. So in your controller'spushed()
method you could just callself.point.toggleValue()
orself.point.setValue(true)
etc.However I note that you want to toggle a different point, so I would suggest this-
- Change your
point
attribute/binding topoints
and pass the whole points array though - Locate the two points you wish to use in the
$onChanges()
method when the points array changes
So bringing it together -
define(['angular', 'require'], function (angular, require) { 'use strict'; var userModule = angular.module('userModule', ['maUiApp']) .component('pushLamp', { template: `<div class="indicator" ng-click="$ctrl.pushed()" ng-class="$ctrl.point.renderedValue"> {{$ctrl.point.xid}}<br>{{$ctrl.point.name}}<br>{{$ctrl.point.tags.indText}} </div>`, bindings: { points: '<', target: '@' }, controller: function() { this.$onChanges = function (changes) { if (changes.points && Array.isArray(this.points)) { this.point = this.points.find(p => p.xid === 'DP_ind_' + this.target); this.switchPoint = this.points.find(p => p.xid === 'DP_sw_' + this.target); } } this.pushed = function() { if (this.switchPoint) { this.switchPoint.toggleValue(); } } } }); return userModule; }); // define
PS the template was not properly formed and you had an extra
this.
at the front of this expression -
ng-click="$ctrl.pushed(this.$ctrl.point.xid)"
Suggestions -
- Find the points via tags rather than by XIDs (In the past we relied on encoding information into the XID, however with the advent of tags we can do this more easily)
- Use an ES6 class for the controller as per my previous post
- Change your
-
@Jared-Wiltshire - thanks for all that. That works if I put the switchpoints on the watchlist of course - I hadn't thought of that - I was looking for the correct method to get/set a point directly.
I'm still not getting the point.value or any of the other properties added via <ma-point-value> in the bound points variable via $ctrl. Odd, though, if I log this.point, everything shows up in the object - just shows undefined if I try to access this.point.value. Shows up via template though.
I'm stumped...
<div ng-init="page = {}"></div> <ma-watch-list-get ng-model="indicatorWl.watchlist" parameters="indicatorWl.parameters" on-points-change="page.points = $points" id="wl-indicator-id" watch-list-xid="WL_Indicator"></ma-watch-list-get> <ma-get-point-value points="page.points"></ma-get-point-value> <div> <push-lamp points="page.points" target="H2OT1"></push-lamp> <push-lamp points="page.points" target="H2OT2"></push-lamp> ... </div>
console.log(this.point.xid, this.point.value); //console: DP_ind_H2OT1 undefined DP_ind_H2OT2 undefined ... console.log(this.point); //console: {id: 164, xid: "DP_ind_H2OT1", name: "T1-Water", enabled: true, deviceName: "buttonTest", …} chartColour: "" chartRenderer: {timePeriod: {…}, type: "chartRendererImage"} convertedValue: 13 dataSourceEditRoles: [] dataSourceId: 33 dataSourceName: "buttonTest" dataSourceTypeName: "VIRTUAL" dataSourceXid: "DS_buttonTest" deviceName: "buttonTest" enabled: true id: 164 lastPayload: {xid: "DP_ind_H2OT1", event: "UPDATE", value: {…}, renderedValue: "green", convertedValue: 13, …} loggingProperties: {loggingType: "INTERVAL", intervalLoggingType: "AVERAGE", intervalLoggingPeriod: {…}, tolerance: 0, discardExtremeValues: false, …} name: "T1-Water" originalId: "DP_ind_H2OT1" plotType: "SPLINE" pointFolderId: 0 pointLocator: {offset: 0, min: 12, max: 53, period: 0, values: Array(0), …} preventSetExtremeValues: false purgeOverride: false readPermission: "" renderedColor: "#32cd32" renderedValue: "green" rollup: "NONE" running: true setPermission: "" simplifyTarget: 5000 simplifyTolerance: 10 simplifyType: "NONE" tags: {indText: "H2O", loc: "tank1", xid: "vm.pid", type: "indicator"} templateXid: null textRenderer: {useUnitAsSuffix: false, format: "0", rangeValues: Array(6), type: "textRendererRange"} time: 1574382356912 unit: "" unreliable: false useIntegralUnit: false useRenderedUnit: false value: 13 xid: "DP_ind_H2OT1" _textRenderer: {rangeValues: Array(6)} dataType: (...) isEnabled: (...) tagsString: (...) valueGetterSetter: (...) __proto__: Object
EDIT Indeed, when looping through this.point (from within the controller),
angular.forEach(this.point, function(value, key){ console.log(key + ':', value); });
none of the point-value properties exist in the point object:
//console id: 164 xid: DP_ind_H2OT1 name: T1-Water enabled: true deviceName: buttonTest readPermission: setPermission: pointFolderId: 0 purgeOverride: false unit: useIntegralUnit: false useRenderedUnit: false pointLocator: {offset: 0, min: 12, max: 53, period: 0, values: Array(0), …} chartColour: plotType: SPLINE loggingProperties: {loggingType: "INTERVAL", intervalLoggingType: "AVERAGE", intervalLoggingPeriod: {…}, tolerance: 0, discardExtremeValues: false, …} textRenderer: {useUnitAsSuffix: false, format: "0", rangeValues: Array(6), type: "textRendererRange"} chartRenderer: {timePeriod: {…}, type: "chartRendererImage"} rollup: NONE simplifyType: NONE simplifyTolerance: 10 simplifyTarget: 5000 preventSetExtremeValues: false templateXid: null dataSourceId: 33 dataSourceXid: DS_buttonTest dataSourceName: buttonTest dataSourceTypeName: VIRTUAL dataSourceEditRoles: [] tags: {indText: "H2O", loc: "tank1", type: "indicator"} originalId: DP_ind_H2OT1
Assume I have a scoping or async issue, but can't seem to solve it.
I'll follow your suggestions when I have this licked - thank you!
-
@bobday said in Help getting ma-watch-list-get points into a controller/service:
@Jared-Wiltshire - thanks for all that. That works if I put the switchpoints on the watchlist of course - I hadn't thought of that - I was looking for the correct method to get/set a point directly.
You can definitely still just fetch the datapoint by XID using the
maPoint
service. But you would also then have to get its current value before setting the new value...@bobday said in Help getting ma-watch-list-get points into a controller/service:
I'm still not getting the point.value or any of the other properties added via <ma-point-value> in the bound points variable via $ctrl. Odd, though, if I log this.point, everything shows up in the object - just shows undefined if I try to access this.point.value. Shows up via template though.
OK, I'm not following exactly where you are putting the
console.log()
statements so it is pretty hard for me to say what is happening. The first block of output you posted does have a value property, and it looks like it is from inside the component controller as you usedconsole.log(this.point);
if this was in the template it would be$ctrl.point
.Do this -
- Put
<pre ng-bind="page.points[0].value"></pre>
in your page and verify it has a value - Put a
console.log(this.point.value)
inside your component controller'spushed()
method and verify it has a value, you probably cant do this in the$onChanges()
method as it may not have a value at that time - Put
<pre ng-bind="$ctrl.point.value"></pre>
in your component template and verify it has a value
- Put
-
@Jared-Wiltshire Ok - sorry for the confusion.
<pre ng-bind="page.points[0].value"></pre>
renders the value on the page;
console.log(this.point.value)
in pushed() renders the value when element is clicked;
<pre ng-bind="$ctrl.point.value"></pre>
in template renders the value, (as well as does{{$ctrl.point.value}}
);@Jared-Wiltshire said: ...The first block of output you posted does have a value property, and it looks like it is from inside the component controller as you used console.log(this.point);
Yes, however, this from SO:
The output of console.log(anObject) is misleading; the state of the object displayed is only resolved when you expand the > in the console. It is not the state of the object when you console.log'd the object.
...which I learned 3 years ago, but completely forgot!
using
console.log(JSON.stringify(this.point)
(inside the controller) returns:{"id":169,"xid":"DP_ind_H2OMX","name":"MX-Water","enabled":true,"deviceName":"buttonTest","readPermission":"","setPermission":"","pointFolderId":0,"purgeOverride":false,"unit":"","useIntegralUnit":false,"useRenderedUnit":false,"pointLocator":{"offset":0,"min":12,"max":53,"period":0,"values":[],"change":10,"startValue":"12","maxChange":0,"volatility":0,"roll":false,"amplitude":0,"phaseShift":0,"attractionPointXid":"DP_ch_current","modelType":"PL.VIRTUAL","changeType":"INCREMENT_ANALOG","dataType":"NUMERIC","settable":false,"relinquishable":false},"chartColour":"","plotType":"SPLINE","loggingProperties":{"loggingType":"INTERVAL","intervalLoggingType":"AVERAGE","intervalLoggingPeriod":{"periods":1,"type":"MINUTES"},"tolerance":0,"discardExtremeValues":false,"overrideIntervalLoggingSamples":false,"cacheSize":1},"textRenderer":{"useUnitAsSuffix":false,"format":"0","rangeValues":[{"from":0,"to":9,"text":"off","colour":null},{"from":10,"to":19,"text":"green","colour":"green"},{"from":20,"to":29,"text":"yellow","colour":"yellow"},{"from":30,"to":39,"text":"red","colour":"red"},{"from":40,"to":49,"text":"blue","colour":"blue"},{"from":50,"to":59,"text":"white","colour":"#cdcdcd"}],"type":"textRendererRange"},"chartRenderer":{"timePeriod":{"periods":1,"type":"DAYS"},"type":"chartRendererImage"},"rollup":"NONE","simplifyType":"NONE","simplifyTolerance":10,"simplifyTarget":5000,"preventSetExtremeValues":false,"templateXid":null,"dataSourceId":33,"dataSourceXid":"DS_buttonTest","dataSourceName":"buttonTest","dataSourceTypeName":"VIRTUAL","dataSourceEditRoles":[],"tags":{"indText":"H2O","loc":"mixer","type":"indicator"},"originalId":"DP_ind_H2OMX"}
clearly without the value, etc. properties.
EDIT Solved - I think ;-). The reason the
.value
shows up inpushed()
is that it is on the object by the time the element is clicked, while not so, immediately after the controller.console.log
places a static prototype on the console, but does not evaluate its properties until the triangle is clicked, per the comment above. Putting the log in a$timeout
solved the problem. The console.log is a bit of red herring here, as it all works in the template so far...Apologies for the lack of diligence on my part.
Incidentally, what is the reason for the array check in:
if (changes.points && Array.isArray($ctrl.points))
? -
Glad you got it sorted. The array check is simply to ensure that you passed the correct object to the attribute, also a check against undefined values which might happen if you set the attribute to a variable name which might not be set initially.
-
Thanks for all the help.