Avatar of MrChrisDavids
MrChrisDavids
 asked on

Get more data using variables in AngularJS

AngularJS newbie alert.. So take it easy ;)

I'm working on a server status app and use a controller to extract data from a datasource:

App.controller('serverController', function($scope, $http) {
  $http.get('/server/model/servers.php').success(function(data) {
    $scope.servers = data;
  });
});

Open in new window


Then, on the view, there's a bunch of panels rendering the output:
<div class="row">
        <div class="col-lg-4" ng-repeat="server in servers track by $index">
            <!-- START panel-->
            <div id="panel3" class="panel" ng-class="{1:'panel-danger', 0:'panel-default', 2:'panel-warning'}[server.status]">
                <div class="panel-heading"><i class="fa fa-{{server.os}}"></i> {{server.hostname}} - {{server.ipaddress}}
                    <paneltool tool-collapse="tool-collapse" ng-init="panel3=true"></paneltool>
                </div>
                <!-- .panel-wrapper is the element to be collapsed-->
                <div collapse="panel3" class="panel-wrapper">
                    <div class="panel-body">
                        <div class="row">
                            <div class="col-lg-12"></div>
                            <div class="col-lg-8">
                                <div><p>Uptime: {{server.uptime}}</p></div>
                                <p>{{server.description}}</p>
                            </div>
                            <div class="col-lg-2">
                                <div class="radial-bar radial-bar-{{server.uptimePer}} radial-bar-sm" data-label="{{server.uptimePer}}%"></div>
                            </div>
                        </div>
                        <div ng-if = "server.app_installation_id > 0">
                            <div class="panel-footer"</div>
                        </div>
                    </div>
                </div>
            </div>
            <!-- END panel-->
        </div>
    </div>

Open in new window


Now - what I'd like to add, is in the "panel-footer" div is to do a lookup and inject the installation_name which is available in another datasource ( /server/model/installations.php )

I've tried all kinds of things but can't get it spinning. What'd be the best method of getting this second datasource and how does it get into the view? The link between them is based on the servers.app_installation_id and on the installations data it's just called id. Name variable is just installation_name
JavaScript

Avatar of undefined
Last Comment
Kyle Hamilton

8/22/2022 - Mon
ASKER CERTIFIED SOLUTION
Kyle Hamilton

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
MrChrisDavids

ASKER
The servers look like this (JSON)

{
    "id": "1",
    "ipaddress": "10.10.10.10",
    "hostname": "testserver",
    "location": "semlm",
    "description": "some text",
    "os": "linux",
    "app_installation_id": "3",
    "entryID": "10",
    "server_id": "1",
    "status": "1",
    "uptime_sec": "0"
},

Open in new window


And the applications:
{

    "id": "6",
    "installation": "mgmt"

},

Open in new window

Kyle Hamilton

I would probably do a merge-join on the two objects - think databases. This would require sorting the data sources first.

Is the order of the servers important?
Kyle Hamilton

snippet:

$q.all([servers,installations]).then(function(data){
     $scope.servers = merge_join(data[0], 'app_installation_id', data[1], 'id');
   })

Open in new window


in the view, now you can do:

<div ng-if = "server.app_installation_id > 0">
   <div class="panel-footer">Name: {{server. installation}}</div>
</div>

Open in new window


here is the merge-join function:

function merge_join(leftList, leftKey, rightList, rightKey){
	leftList.sort(function(a,b){
		return a[leftKey] - b[leftKey];
	})
	rightList.sort(function(a,b){
		return a[rightKey] - b[rightKey];
	})

	var left = 0, right = 0;

	while(left < leftList.length && right < rightList.length){
		if(leftList[left][leftKey] == rightList[right][rightKey]){
			angular.extend( leftList[left], rightList[right] );
			left++;
			right++;
		}else if(leftList[left][leftKey] < rightList[right][rightKey]){
			left++;
		}else if(leftList[left][leftKey] > rightList[right][rightKey]){
			right++;
                }
	}

	return leftList
}

Open in new window

All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
MrChrisDavids

ASKER
Server order should be based on hostname.

There's other things I'd like to add on top of this at a later stage, but I assume it'd be a simple expansion of what's been written here already?
Kyle Hamilton

ok, so you can just resort once you've made your join.

not knowing what else you will need to do with this data, I can't say that the merge-join is the best approach.. this is a solution in isolation.

so to resort the joined table, you would do:

snippet:
$q.all([servers,installations]).then(function(data){
    var _servers = merge_join(data[0], 'app_installation_id', data[1], 'id');
   $scope.servers = _servers.sort(function(a,b){
     if (a.hostname < b.hostname) { return -1; }
     if (a.hostname > b.hostname) { return 1; }
     return 0; 
    })
})

Open in new window

Kyle Hamilton

You could also use an angular sort filter in the view, but personally, I try not to do any computations or operations in the view. It becomes an issue if you have a lot of data, so if your data set is relatively small, it mightn't make any difference where you do the sorting. However, I still think data manipulation should be done in the controller.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
MrChrisDavids

ASKER
Hmm ..

I'm getting that leftList.sort is not a function?
Kyle Hamilton

sort is an array method. so you have to make sure that leftList is an array. Based on your earlier code snippet, I'm assuming that '/server/model/servers.php' returns an array of objects. But I can't see your output. So you could check what the data actually looks like by logging it to the console, or looking at the network tab in Chrome tools, to make sure that you are providing your list as the function argument

$q.all([servers,installations]).then(function(data){
     console.log("data: ", data)
     $scope.servers = merge_join(data[0], 'app_installation_id', data[1], 'id');
   })

Open in new window

MrChrisDavids

ASKER
/server/model/servers.php returns a JSON document - which I assume should be treated as an array?

With two servers listed it's like this:

[

{

    "id": "2",
    "ipaddress": "10.10.10.10",
    "hostname": "nooslbps01",
    "location": "Oslo",
    "description": null,
    "os": "linux",
    "app_installation_id": "6",
    "entryID": "98",
    "server_id": "2",
    "status": "0",
    "uptime_sec": "2354495"

},

    {
        "id": "1",
        "ipaddress": "11.11.11.11",
        "hostname": "vss01lanet",
        "location": "Landskrona",
        "description": null,
        "os": "linux",
        "app_installation_id": "3",
        "entryID": "49",
        "server_id": "1",
        "status": "0",
        "uptime_sec": "24339332"
    }

]

Open in new window


Looking at the network tab, though, indicates that it doesn't make a hit on that page at all.

Recap - this is what app.js looks like:
App.service('ServersService', Servers);
Servers.$inject = ['$http'];
function Servers($http){
  var self = this;
  self.getServers = function(){ // returns a promise you can use in your controller
    return $http.get('/server/model/servers.php')
  }
  self.getInstallations = function(){
    return $http.get('/server/model/installations.php')
  }
}


App.controller('serverController', Server);
Server.$inject = ['$scope','$q', 'ServersService']
function merge_join(leftList, leftKey, rightList, rightKey){
  leftList.sort(function(a,b){
    return a[leftKey] - b[leftKey];
  })

  rightList.sort(function(a,b){
    return a[rightKey] - b[rightKey];
  })

  var left = 0, right = 0;

  while(left < leftList.length && right < rightList.length){
    if(leftList[left][leftKey] == rightList[right][rightKey]){
      angular.extend( leftList[left], rightList[right] );
      left++;
      right++;
    }else if(leftList[left][leftKey] < rightList[right][rightKey]){
      left++;
    }else if(leftList[left][leftKey] > rightList[right][rightKey]){
      right++;
    }
  }

  return leftList
}

function Server($scope, $q, ServersService) {
  var servers = ServersService.getServers();
  var installations = ServersService.getInstallations();

  $q.all([servers,installations]).then(function(data){
    var _servers = merge_join(data[0], 'app_installation_id', data[1], 'id');
    $scope.servers = _servers.sort(function(a,b){
      if (a.hostname < b.hostname) { return -1; }
      if (a.hostname > b.hostname) { return 1; }
      return 0; 
    })
  })
}

Open in new window


And in the view I've just added:
                        <div ng-if = "server.app_installation_id > 0">
                            <div class="panel-footer">Name: {{server.installation}}</div>
                        </div>

Open in new window

I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
Kyle Hamilton

did you log the data like I posted? post the result of the console log
MrChrisDavids

ASKER
"data: " Array [ Object, Object ]

Open in new window

Kyle Hamilton

that's no use. what browser are you using? If you use Chrome, you will get the deserialized objects. that's what we need to see
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Kyle Hamilton

to cut to the chase, you'll probably need to do this:

$scope.servers = merge_join(data[0].data, 'app_installation_id', data[1].data, 'id');

but he point is that you should use dev tools to trouble shoot your code. chrome and firefox provide the best dev tools.
MrChrisDavids

ASKER
I'm using Chrome and Firefox - spitting out the same results..

However - they're clickable..

The output isn't quite pastable into EE though as it ends up on a single line. :(

The array contains two objects. The first data object contains a data: array with 2 objects (the two servers). The other data object contains a data: array with the 10 installations.
MrChrisDavids

ASKER
Adding .data solved the issue! :)
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
Kyle Hamilton

gr8 :)
Kyle Hamilton

if you ever want to format javascript out of a single line like that, you can use http://jsbeautifier.org/

I find it incredibly useful to quickly format something I wouldn't otherwise be able to look at without getting a headache
MrChrisDavids

ASKER
Thanks for all the help in this one! It's allowed me to get a much better grasp on things and continue onwards.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Kyle Hamilton

:)
MrChrisDavids

ASKER
Hmm .. Maybe I'm not grasping this enough, just yet.

Although this is outside the scope of the initial question - let's say I'd like to add another set of data listing latency for the servers.

The data set looks like:
[
{"id": "13841",
"server_id": "1",
"rtt_ms": "30.53",
"time": "2015-04-13 06:01:01"},
{"id": "13843",
"server_id": "1",
"rtt_ms": "30.75",
"time": "2015-04-13 06:02:02"},
{
"id": "13844",
"server_id": "2",
"rtt_ms": "27.57",
"time": "2015-04-13 06:02:02"}
]

Open in new window


How'd one go about adding these entries as a sub-variable, or something similar, to each server object? The data containing latency returns some 50 entries per server and all of them needs to be available in the view. :o

Or better still - if the data set could be retrieved "per" server object using a URL and a parameter? That'd be much more efficient.
Kyle Hamilton

it depends. if the latency data is shown upon some action from the user, a mouse click, then fetching it one by one works.
if on the other hand, you are displaying all of the data at once, then you are better off fetching all the data in one http request. although not knowinv anything about the backend api, this statement is kind of a guess.
if you want to fetch it all at once, add it to the $q.all array
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
MrChrisDavids

ASKER
The view being worked on currently is listing all servers at once; so it'd be better getting all in the same go then.

But adding it to the $q.all? Maybe I'm missing something - but the latency array needs to be added as a sub-array to the existing one, I guess? Or at least that's what I've been trying to do. Wrong approach?
Kyle Hamilton

but is it listing all 50 latency objects for each server at once?
Kyle Hamilton

are there any more data sets that you will be aggregating in addition to the three already mentioned?
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
MrChrisDavids

ASKER
50 latency entries, per server, at once. It's used to build a graph, per server.

There'll most likely be more data sets that'll be added, yes.
Kyle Hamilton

   var servers = ServersService.getServers();
   var installations = ServersService.getInstallations();
   var latencyData = ServersService.getLatencyData();

   $q.all([servers,installations,latencyData]).then(function(data){
     // do something with the data...
   })

Open in new window


That's the pattern. Add as many data sources as you want. Keep in mind though, that the view will wait to render until all your data sources have been fetched. The good thing is, that they are being fetched asynchronously.


You can further extend the merging algorithm, or you could try something else.

Sometimes for convenience, I convert my Array data structure to an Object, where the keys are the uniting element, in this case, the server_id. Then I don't have to merge the data sets, I simply access the desired object by key:

For example:

function arrToObj(array) {
    var obj = {};
    array.map(function (element) {
     if(!obj[element.server_id]){
       obj[element.server_id] = []
     }
      obj[element.id].push(element);
    })
    return obj;
}

Open in new window



so this:

[{
  "id": "13841",
  "server_id": "1",
  "rtt_ms": "30.53",
  "time": "2015-04-13 06:01:01"
}, {
  "id": "13843",
  "server_id": "1",
  "rtt_ms": "30.75",
  "time": "2015-04-13 06:02:02"
}, {
  "id": "13844",
  "server_id": "2",
  "rtt_ms": "27.57",
  "time": "2015-04-13 06:02:02"
}]

Open in new window


becomes:

{
  "1": [{
    "id": "13841",
    "server_id": "1",
    "rtt_ms": "30.53",
    "time": "2015-04-13 06:01:01"
  },
  {
    "id": "13843",
    "server_id": "1",
    "rtt_ms": "30.75",
    "time": "2015-04-13 06:02:02"
  }],
  "2": {
    "id": "13844",
    "server_id": "2",
    "rtt_ms": "27.57",
    "time": "2015-04-13 06:02:02"
  }
}

Open in new window


you can now access an array of latency objects by key.

Does that make sense?
MrChrisDavids

ASKER
Makes perfect sense!

I'll see if I can manage to get it into the code, if not - I'll update here ;)
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
MrChrisDavids

ASKER
Hmm .. While the concept makes perfect sense; i'm struggling to make it happen :(
Kyle Hamilton

why dont you post another question..

i dont think i can get to it today.