maJsonStore (JSON store)
-
Hi Richard
The best way to cycle through the array that the watchlist gives you is to use the angular filter pipe. You can filter by any point attribute. such as deviceName, name or tag.
-
@craigweb said in maJsonStore (JSON store):
Hi Richard
The best way to cycle through the array that the watchlist gives you is to use the angular filter pipe. You can filter by any point attribute. such as deviceName, name or tag.
Thanks - is it possible to see some sample/example code (or if you have a site which outlines it) to use the filter?
Cheers
Richard
-
<div flex="100" flex-gt-md="33" layout="column" ng-if="designer.points | filter:{name:'Level'}:true | maFirst" ng-repeat="res in designer.points | filter:{name:'Level'}:false"> <md-card> <md-toolbar class="md-whiteframe-1dp md-hue-3"> <div class="md-toolbar-tools"> <h2 flex=""> <span>{{ res.deviceName }} </span> </h2> </div> </md-toolbar> <md-card-content> <div flex="100" layout="row"> <div layout="column" flex="50"> <h2> Level:</h2> <ma-point-value style=" width: 25%; font-weight: bolder; font-size: 25px;" enable-popup="right" point="res"></ma-point-value> </div> <div class="flex"> <img ng-src="/rest/v2/file-stores/default/img/Reservoir.svg" style="z-index:-2 width: 200px; height: 150px;"> </div> </div> </md-card-content> </md-card> </div>
My watchlist is a dynamic watchlist the queries all points where the "site" tag matches.
I then filter down to only have the points with name = "Level" then use ng-repeat to display them all in a md-card.
Your table might get a little complicated with nested ng-repeats. You might want to read up on the angular docs on how to use ng-repeat-end. https://docs.angularjs.org/api/ng/directive/ngRepeat
-
Thanks - these are working quite well, and I've been making some good progress - I was curious if tags can be set on the edge devices, or if they can only be set on the server?
Cheers
Richard
-
The edge device and the server runs the same software The ES just has limited hardware capacity. So yes they can be set there and the publisher/data source can be set to update any point properties on the server if they are changed on the ES.
-
@craigweb said in maJsonStore (JSON store):
<div flex="100" flex-gt-md="33" layout="column" ng-if="designer.points | filter:{name:'Level'}:true | maFirst" ng-repeat="res in designer.points | filter:{name:'Level'}:false">
My watchlist is a dynamic watchlist the queries all points where the "site" tag matches.
I then filter down to only have the points with name = "Level" then use ng-repeat to display them all in a md-card.
Your table might get a little complicated with nested ng-repeats. You might want to read up on the angular docs on how to use ng-repeat-end. https://docs.angularjs.org/api/ng/directive/ngRepeat
Does the
designer.points
map to all points, that then need to be filtered? Or, after adding the tags, do I also need to create a dynamic watchlist (as is kinda eluded to here: https://forum.infiniteautomation.com/topic/4153/using-ma-state-params/15#) to address?Thanks
Richard
-
This post is deleted! -
Designer.points
has all the points that are in the watchlist that I created, the watchlist points get brought into the page with a<ma-watch-list-get>
So yes you will need to make a watchlist.
The other option is to use a
<ma-point-query query="{name:query.filter, deviceName:query.filter}" limit="query.limit" start="(query.page-1)*query.limit" sort="query.order" points="points" promise="promise"></ma-point-query>
in your page, use {{points}} so show youthe object it returns.
-
So, just trying to tie together Craig's example and Jared's example above, I'm getting confused how to filter by the tags; so far I have:
<ma-point-query query="{deviceName:'SPS001'}" points="points" promise="promise"></ma-point-query> <!-- show me everything --> <!--div style="color: white;background-color:black; border: 1px solid red;" ng-if="points" ><pre>{{points}}</pre></div--> <div md-progress="promise" style="border: 1px solid blue; background-color: white; color: black;" ng-repeat="point in points"> <span>{{point.xid}}</span> <span style="background-color: yellow;">{{point.tags.Display}}</span> </div>
Which successfully shows me everything that matches the device name in the commented out (first) div, or the secondary DIV, which shows me the same points, but just the XID for the point and the tag for "Display" - but how do I get a specific point from the array if I want the "Display" equal to, say, "Battery Capacity" for this one site? I don't know if I need to put this filter within the
ma-point-query
section, or if I load the whole array intopoints
and then filter in theng-repeat
section ... nor the syntax, as thetags
seem to be at a secondary level of the initial query (as per the twospan
s, where one ispoint.xid
and the tags arepoint.tag.Display
).BTW: there is a typo on the page: /ui/docs/ng-mango/ma-point-query in the 'limit' description: "no limit by defualt"
Thanks
Richard
-
I can help you with the point query.
You can either be specific at what you want at the beginning, or take a multitude of points then filter them accordingly with a filter or usingng-if="point.tags.Display=='displayType'"
For your point query, you can work from the watchlist builder to generate an RQL query.
You'll get something like:<ma-point-query query="'eq(deviceName,SPS001)&in(tags.Display,Battery Capacity)'" points="points" promise="promise"></ma-point-query>
Hope that's of some help
Fox
-
Using the query builder as MattFox suggested is definitely a pro tip!
Your question seems to be around where best to do the filtering. IMO you will be better off doing one simple query to the backend and then doing all your filtering on the front end. That is if your page is going to have a lot of different components using those points. If the page is focused on only certain points then you could query for only those points. The choice is yours
The point query gives you an object with an array of points and all the points attributes, it is not live updating. So now you know that in your page you only have points from SPS001, you can now filter down to the specific point you want in sps001 as such. The syntax is like this because of the nested structure.
<ma-point-value point="points | filter:{tags:{Display: 'Battery Capacity'} }"></ma-point-value>
You can have multiple filters like such
<ma-point-value point="points | filter:{deviceName:'myDevice',tags:{Site: 'myTag'} }"></ma-point-value>
If you want to use an ng-repeat then you make your filter less precise that it gives multiple points that match the criteria. You have a couple of options for the order that the ng-repeat will use.
-
Unfortunately I'm still not getting the results I require; if I put the point query as:
<ma-point-query query="'eq(tags.SiteName,SPS001)&&(tags.Display,Customer Load)'" points="points" ></ma-point-query>
I'm getting nothing back, but if I put:
<ma-point-query query="'eq(tags.SiteName,SPS001)'" points="points" ></ma-point-query>
It only returns the tag
SiteName
and knocks out the tagDisplay
, so I can't further filter on it...Also, when I did get this working (in a different capacity, using the
SIteName
tag to filter a specific site), the data populated in myDIV
, but was destroyed almost instantaneously:<tr ng-repeat="site in sites.sites"> <td> <ma-point-query query="'&in(tags.SiteName,{{site.name}})'" points="points" ></ma-point-query> <div style="color: white;background-color:black; border: 1px solid red;" ng-if="points | filter:{tags:'Display'}" ><pre>{{points}}</pre></div> </td>
Is this where the
promise
keyword becomes important?Cheers
Richard
-
Hi Richard
The promise variable will have 3 states fulfilled, rejected and pending. It gives you the status of the query. So no is the answer to your question about the promise.
<ma-point-query query="'eq(tags.SiteName,SPS001)&&(tags.Display,Customer Load)'" points="points" ></ma-point-query>
There is no operatore for your second criteria. This should work
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
As Mattfox sugested useing the watchlist builder to make your RQL queries. Let me show you how. The nice thing about using this method is there is you can see the results immediately on the query preview tab.
As for what you are doing in the table. I think you might be running into scoping issues with using points. Hopefully, @Jared-Wiltshire can comment on that.
ng-if="points | filter:{tags:'Display'}"
Will not work and I'm sure there are plenty of errors in your browser's console when you use this. tags is an object, what you are doing there is filtering the points array for any object that has a tag with a value of string 'Display'As in my previous post if you want to filter with tags you need to supply a nested object the filter ie:
point="points | filter:{tags:{Display: 'Battery Capacity'} }"
If you want to use it in a ng-if you need to use maFirst
point="points | filter:{tags:{Display: 'Battery Capacity'} | maFirst}"
Lastly, I think that your pages are getting quite complex and you would be better off creating a user module where you have a controller to do all your logic and mapping of arrays,
-
@craigweb said in maJsonStore (JSON store):
Hi Richard
There is no operatore for your second criteria. This should work
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
Thanks - that was the syntax I was after; I couldn't see anything like i in the RQL syntax: https://cubicweb.readthedocs.io/en/3.26/book/annexes/rql/language/#rql
Is there somewhere that I can read about this??
Also I note that in my nested repeats, this will work, and display all the relevant info for a single point:
<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query> <!-- show me everything --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div> </div>
Yet if I change the tag.SiteName to
{{site.name}}
thus:<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,{{site.name}})&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query> <!-- show me everything --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div> </div>
I'm getting this:
Although the browsers (Firefox) code inspector displays the code as it should:
So - should that work as I have it, or does Angular not allow variable substitution in this manner? I al;so tried to do it with
' + {{site.name}} + '
and a few variations (but clearly not the right one!)ng-if="points | filter:{tags:'Display'}"
Will not work and I'm sure there are plenty of errorsMy apologies - I left this in with changing the page so much ...
As Mattfox sugested useing the watchlist builder to make your RQL queries. Let me show you how. The nice thing about using this method is there is you can see the results immediately on the query preview tab.
Thanks for that - I'm looking into how that all works ...
Cheers
Richard
-
@richard-mortimer said in maJsonStore (JSON store):
<div style="border:2px solid blue;" ng-repeat="site in sites.sites">
<ma-point-query query="'eq(tags.SiteName,SPS001)&eq(tags.Display,Customer Load)'" points="points" ></ma-point-query>
<!-- show me everything -->
<div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points }}</pre>--</div>
</div>Yikes, you're repeating your queries multiple times and calling the same data. We may need to start moving you into angularJS component territory.
However, if you're after that site name, you need to do this:<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="site in sites.sites"> <ma-point-query query="'eq(tags.SiteName,'+site.name+')&eq(tags.Display,Customer Load)'" points="points[site.name]" ></ma-point-query> <!-- show me everything in this point! --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points[site.name] }}</pre>--</div> </div>
if you have
points="points"
being used every time for each of your ma-point-query items, they'll continue to be updated and overwrite one another as they all share the same scope.
Storing them in an object using the site name as the key makes it a bit easier.
or alternatively!<ma-json-store xid="sitesDataXID" value="sites"></ma-json-store> <div style="border:2px solid blue;" ng-repeat="(index,site) in sites.sites"> <ma-point-query query="'eq(tags.SiteName,'+site.name+')&eq(tags.Display,Customer Load)'" points="points[index]" ></ma-point-query> <!-- show me everything in this point! --> <div style="color: white;background-color:black; border: 1px solid red;">--{{ site.name }}--<pre>{{ points[index] }}</pre>--</div> </div>
This will map everything by the index in your JSON store.
Let me know how you go.Fox
-
I agree with Mattfox and I think Richard will be more familiar since you can code pure javascript functions in the component controller to manipulate your data. instead of pseudo angular/javascript
I believe you are safe to use
points="points"
unlesspoints
is initiated somewhere else in the ng-repeats parent scope. As ng-repeat creates a scope for each iteration but will first look in the parent scope forpoints
-
Sorry to drag up an old subject, but I've got a curious thing happening within my repeats - and I'm not sure if it's an Angular issue, or I've done something to upset it ...
My code used two
ng-if
statements to determine if the value is output value is in watts or kilowatts, thus:<span ng-repeat="point in points[site.name]"><!-- Load --> <ma-get-point-value point-xid="{{ point.xid }}" point="output"></ma-get-point-value> <div ng-if="output.value > 1000">{{ (output.value / 1000) | number:1 }}kW</div> <div ng-if="output.value <= 1000">{{ output.value | number:0 }}W</div> </span><!-- end Load -->
The problem is - I'm sometimes getting two values returned, (for example 984W and 1kW) - if I directyly print the value on the screen via
{{ output.value }}
I'm seeing what I would expect to see (mostly integers, a couple of float types for some points).Am I doing something wrong here, or is there a better way to do this?
[edit] Here's one I managed to catch as it happened:
Thanks
Richard
-
Evening Richard!
Are your points numeric types or alphanumeric?
I say keep it simple and just do
<span ng-repeat="point in points[site.name]"><!-- Load --> <ma-get-point-value point-xid="{{ point.xid }}" point="output"></ma-get-point-value> <div ng-if="parseInt(output.value) > 1000">{{ parseFloat(output.value / 1000).toFixed(1) }}kW</div> <div ng-if="parseInt(output.value) <= 1000">{{ Number(output.value)}}W</div> </span><!-- end Load -->
You don't have to use what I put int the div inner HTML. But as far as the ng-if is concerned at least you'd have a consistent read with your values.
-
@mattfox said in maJsonStore (JSON store):
Evening Richard!
Are your points numeric types or alphanumeric?
I say keep it simple and just do
<span ng-repeat="point in points[site.name]"><!-- Load --> <ma-get-point-value point-xid="{{ point.xid }}" point="output"></ma-get-point-value> <div ng-if="parseInt(output.value) > 1000">{{ parseFloat(output.value / 1000).toFixed(1) }}kW</div> <div ng-if="parseInt(output.value) <= 1000">{{ Number(output.value)}}W</div> </span><!-- end Load -->
I believed they were simply numeric - but whern I add the
parseInt(output.value)
everything disappears (as in - neitherng-if
statement is true), similarly the same with theNumber
andparseFloat
functions ...If I do:
<script>console.log(typeof {{output.value}});</script>
I get:
Cheers
Richard
-
I'll just have a quick look in the mango dash to see fi I can run some tests.
As for typeof you want: note javascript generally has a numeric. doesn't have ints or floats like compiled languages.{{ typeof output.value }} //although i'm not confident if it will work...