VinnyGuitara
VinnyGuitara

Reputation: 605

Display values from database AngularJS

I am a novice with Angular. I have a project that a friend and I were working on that acts as a "Reef Tank Controller". It is an arduino/mysql database/angularJS page. My buddy was working on the front end but had to drop out due to work and now I have a semi completed website. From the perspective of the back-end it all works. On the front end I wanted to add a section to control the lighting. I wanted to start out by simply displaying the values of each LED color that is stored in the database. I created a new controller for each LED string I want to display the value of:

'use strict';
/* Controllers */
angular.module('reefController.controllers', ['uiSlider'])
// Main Dashboard Controller
.controller('dashboardController', ['$scope', function($scope) {
   $.ajax({
        url: 'php/reef_controller_selectVals.php',
        type: 'json',
        success: function(data) {

            reefController.config.data = data;

            // Draw Charts

            $.each(data.charts, function(name, chartData) {

                $(chartData.series).each(function(idx, val){
                    // Reformat sensor names to their Human readable versions
                    this.name = reefController.labels[name].sensors[idx];
                });

                var options = $.extend(reefController.config.chartOptions, { 
                    chart: {
                        events: {
                            load: function() {
                                var chart = this;
                                setInterval(function() {                                    
                                    $.ajax({
                                    url: 'php/reef_controller_selectVals.php?incremental=' + chart.options.name,
                                    success: function(data) {  
                                        // Only add data points if the latest date in the DB is different
                                        if (!(chart.xAxis[0].dataMax == data.timestamp)) {
                                            $(chart.series).each(function(i, series) {
                                                series.addPoint([data.timestamp, data[series.options.id]], true, true);
                                            }); 
                                        } 
                                    }
                                });
                                }, reefController.config.timers.chartUpdateInterval);
                            }
                        },
                        renderTo: name
                    },
                    name: name,
                    series: chartData.series,
                    title: { text: reefController.labels[name].chart.title }
                });


                var chart = Highcharts.StockChart(options);
                reefController.config.charts[name] = chart;
            });

            //Set LED Value Labels



            // Set outlets
            var i = 0;
            $('.outlet').each(function(){
                if (!$(this).hasClass('constant')) {
                    $(this).attr('data-outlet-id', data.outlets[i].id)
                            .attr('data-reveal-id', 'date-time-modal')
                            .attr('data-on-time', data.outlets[i].on_time)
                            .attr('data-off-time', data.outlets[i].off_time);

                    if (data.outlets[i].active) {
                        $(this).addClass('active');
                    } else {
                        $(this).removeClass('active');
                    }
                    i++;
                    // Bind click event to outlets
                    $(this).click(function(){
                        var outlet = $(this);
                        var id = outlet.attr('data-outlet-id');
                        var newOutletState = (outlet.hasClass('active')) ? 0 : 1;

                        // Set datepickers to currently defined DB times
                        $('#on_time').val(outlet.attr('data-on-time'));
                        $('#off_time').val(outlet.attr('data-off-time'));

                        // Formatter function for date time pickers
                        var dateFormatter = function(elem, current_time) {
                            elem.html(moment(current_time).format('ddd M/D/YY HH:mm'));
                        };

                        $('#on_time').datetimepicker({
                            format: 'Y-d-m H:i:s',
                            inline: true,
                            defaultDate: outlet.attr('data-on-time'),
                            onChangeDateTime: function(current_time) { dateFormatter($('#on_time_display'), current_time) },
                            onGenerate: function(current_time) { dateFormatter($('#on_time_display'), current_time) }
                        });

                        $('#off_time').datetimepicker({
                            format: 'Y-d-m H:i:s',
                            inline: true,
                            defaultDate: outlet.attr('data-off-time'),
                            onChangeDateTime: function(current_time) { dateFormatter($('#off_time_display'), current_time) },
                            onGenerate: function(current_time) { dateFormatter($('#off_time_display'), current_time) }
                        });

                        // Submit Button
                        $('#submit-btn').click(function() {
                            data = {
                                'id': id,
                                'on_time': $('#on_time').val(),
                                'off_time': $('#off_time').val(),
                                'active': newOutletState
                            };

                            $.ajax("php/reef_controller_loadOutlet.php", {
                                type: 'POST',
                                data: data,
                                dataType: 'json',
                                success: function(data) {
                                    outlet.toggleClass('active');
                                }
                            });

                            $('#date-time-modal').foundation('reveal', 'close');
                        });
                        // Cancel Button
                        $('#cancel-btn').click(function(){
                            $('#date-time-modal').foundation('reveal', 'close');
                        });
                    });
                }
            });

        }
    });
}])

.controller('outletController', ['$scope', function($scope) {
    $.ajax({
        url: 'img/outlet.svg',
        success: function(svg) {
            var svgDoc = document.importNode(svg.documentElement, true);    
            $('.outlet').append(svgDoc);
        }
    });
}])
.controller('temperatureController', ['$scope', function($scope) { }])
    .controller('phController', ['$scope', function($scope) { }])

.controller('whiteLedCtrl', ['$scope', function($scope) {}])
.controller('blueLedCtrl', ['$scope', function($scope) {}])
.controller('variousColorLedCtrl', ['$scope', function($scope) {}]);

In my dashboard.html file I have:

<table style="width: 100%;">
    <tr>
        <td>
            {{ ledValues }}
        </td>
    </tr>
    <tr>
        <td colspan="3" style="background: #eff4f6;"><input type="checkbox" name="overrideLightingSchema" value="overRide">
            &nbsp;&nbsp;Override Current Lighting Pattern
        </td>
    </tr>
    <tr>
        <td colspan="3">
            <select name="lightingSchemas">
                <option value="feedingLightingPattern">Feeding Schema</option>    
                <option value="morningLightingPattern">Morning Schema</option>
                <option value="standardLightingPattern">Standard Schema</option>
                <option value="twilightLightingPattern">Dusk Schema</option>
                <option value="nightTimeLightingPattern">Night Schema</option>
                <option value="deepNightLightingPattern">Late Night Schema</option>
            </select>
        </td>
    </tr>
</table>

Sometimes this displays the values from the database other times it just says:

{{ ledValues }}

It may be an async issue of some sort but my angular, well JS for that matter, is weak. Any help would be great.

Upvotes: 1

Views: 3994

Answers (1)

Kildareflare
Kildareflare

Reputation: 4768

The main issue I can see here is that you are using $ajax to make requests to the server.

You use the response from the server to set your variable...

 reefController.config.data = data;

However since $ajax is not part of Angular this update occurs outside of the scope digest. Therefore Angular does not know to update the binding. You could try wrapping you assignment in $apply.

$scope.$apply(function(){
    reefController.config.data = data;
});

That said, I cannot see where reefController is defined. You probably want to be assigning it to the scope:

$scope.$apply(function(){
    $scope.MyData = data;
});

However, I would actually recommend you replace the $ajax calls with the Angular $http service.

//inject $http
.controller('dashboardController', ['$scope', '$http', function($scope, $http) {

//then use it later on
$http({
  method: 'GET',
  url: 'php/reef_controller_selectVals.php'
}).then(function successCallback(response) {
    // this callback will be called asynchronously
    // when the response is available
    $scope.MyData = data;

  }, function errorCallback(response) {
    // called asynchronously if an error occurs
    // or server returns response with an error status.
  });

Example

Below Is a very quick example of how to use $http to get the data from the server.

The full example, including a fake service (that does not require a server response) can be found here: http://codepen.io/anon/pen/GogWMV

'use strict';

angular.module('reefApp', [ 'uiSlider']); 

/* CONTROLLER */
angular.module('reefApp')
.controller('dashboardController', dashboardController);

  /* Define controller dependencies. 
  Note how we do not use $scope as we are using controller as vm syntax 
  And we assign our scope variables to 'ctrl' rather than scope directly.
  see john papa styleguide (https://github.com/johnpapa/angular-styleguide#style-y032)
  */
dashboardController.$inject = ['ledService'];

function dashboardController(ledService)
{
  var ctrl = this;

  //define an array to hold our led data
  ctrl.ledData = [];

  //call method to get the data
  getLedData();

  //this method uses our service to get data from the server
  //when the response is received it assigns it to our variable
  //this will in turn update the data on screen
  function getLedData()
  {
      ledService.getLedData()
      .then(function(response){
        ctrl.ledData = response.data;
      });
  }
}   

/* LED SERVICE */
/* the service is responsible for calling the server to get the data.
*/
angular.module('reefApp')
.factory('ledService', ledService);

ledService.$inject = ['$http'];

function ledService($http)
{
    var endpointUrl = "http://addressOfYourEndpoint";

    /* expose our API */
    var service =  {      
      getLedData: getLedData,      
    }
    return service;

   function getLedData()
   {
       /* this is how you would call the server to get your data using $http */     
       /* this will return a promise to the calling method (in the controller) 
          when the server returns data this will 'resolve' and you will have access to the data
          in the controller:
   Promises: http://andyshora.com/promises-angularjs-explained-as-cartoon.html*/
       return  $http.get(endpointUrl); 
   }
} 

Taking this further, best practice would be to hold a reference to the data returned from the server inside the service; one reason is the service is a singleton - so this data service and it's data can be shared across controllers.

function ledService($http)
{
    var endpointUrl = "http://addressOfYourEndpoint";

    /* expose our API */
    var service =  {      
      ledData: [],
      getLedData: getLedData,      
    }
    return service;

   function getLedData()
   {
       return  $http.get(endpointUrl)
        .then(function(response)
        {
           /* assign response to service variable, before promise is returned to caller */
           service.ledData = response.data;
         }); 
   }
} 

Then in our controller...

  function getLedData()
  {
      ledService.getLedData()
      .then(function(response){
        ctrl.ledData = ledService.ledData;
      });
  }

Update

To collect more data, you could add a service for each piece of data you want to collect - or add more methods to existing service. Let's assume you add a phService.

You then inject this into your controller. And add call a new method to use the service to the data and assign to the model. It can then be shown in the view.

dashboardController.$inject = ['ledService', 'phService'];

function dashboardController(ledService, phService)
{
  var ctrl = this;

  //our data will be stored here
  ctrl.ledData = [];
  ctrl.phData = [];

  //call methods to get the data
  getLedData();
  getPhData();

  function getLedData()
  {
      ledService.getLedData()
      .then(function(response){
        ctrl.ledData = response.data;
      });
  }

  function getPhData()
  {
      phService.getPhData()
      .then(function(response){
        ctrl.phData = response.data;
      });
  }
}  

Then in the view (HTML):

   <tr ng-repeat="ph in ctrl.phData">
        <td> PHValue</td>
        <td >
            {{ ph }}
        </td>
    </tr>

Upvotes: 2

Related Questions