javascriptangularjsjavascript-databinding

Angular JS: Multiple Data Bindings Into Table


Okay. I'm pulling together a data table that is going to look through majors and minors of a school. I'm running into issues of trying not to repeat myself in the data where every possible, but am not sure how to get the data pulled into the document, or even how to setup the data into the different arrays. Looking for some advice and help in whichever of these two areas I can find. When I search through docs and API's none of them seem to go deep enough into the data to really get what I'm looking to accomplish.

I have made a plunker to showcase my problem more clearly, or at least I hope to make it clearer.
http://plnkr.co/edit/2pDmQKKwjO6KVullgMm5?p=preview

EDIT:

It would even be okay with me if the degree each degree could be read as a boolean, and same with Education level. I'm just not sure how to go about it without repeating the whole line in a new table row. http://www.clemson.edu/majors


HERE IS THE HTML

<body ng-app="app">
    <h2> Majors and Minors </h2>
    <table ng-controller="MajorsCtrl">
      <tbody>
        <tr>
          <th>Department</th>
          <th>Major</th>
          <th>Education Level</th>
          <th>Location </th>
          <th>Degree</th>
          <th>Department Website </th>
        </tr>
        <tr ng-repeat="major in majors">
          <td>{{major.Department}}</td>
          <td>{{major.Major}}</td>
          <td>{{major.EdLevel}}</td>
          <td>{{major.Type}}</td>
          <td>{{major.Degree}}</td>
          <td>{{major.Website}}</td>
        </tr>
      </tbody>
    </table>
  </body>

HERE IS THE JS

var app = angular.module('app', []);

// Majors and Minors Data That will be injected into Tables 
app.controller('MajorsCtrl', function($scope) {
   // Heres where it gets tricky 
        // Now I have biology with four diff degree types
        // Biology with 2 diff EdLevels
        // How do I combine all of these into 1 Group without repeating

    var majorsInfo =  [
       { 
            Department:'Statistics', 
            Major:'Applied Statitistics', 
            EdLevel:'Graduate', 
            Type:'Campus/Online',
            Degree:'Graduate Certificate',
            Website: 'http://biology.wvu.edu',
        }, 
        { 
            Department:'Biology', 
            Major:'Biology', 
            EdLevel:'Graduate', 
            Type:'Campus',
            Degree:'PH.D' ,
            Website: 'http://biology.wvu.edu',
        }, 
         { 
            Department:'Biology', 
            Major:'Biology', 
            EdLevel:'Graduate', 
            Type:'Campus',
            Degree:'M.S' ,
            Website: 'http://biology.wvu.edu',
        }, 
         { 
            Department:'Biology', 
            Major:'Biology', 
            EdLevel:'Undergraduate', 
            Type:'Campus',
            Degree:'B.A.' ,
            Website: 'http://biology.wvu.edu',
        }, 
         { 
            Department:'Biology', 
            Major:'Biology', 
            EdLevel:'Undergraduate', 
            Type:'Campus',
            Degree:'B.S.' ,
            Website: 'http://biology.wvu.edu',
        }, 
      ]; 


    $scope.majors = majorsInfo; 
});

Solution

  • This seems to be a question about modeling the data. I took a few assumptions:

    I modeled a set of "enums" and Programs/Departments after your data. I used enums for ease of updating the values in multiple locations:

    app.factory("EducationEnums", function () {
      var EdLevels = {
        GRAD: "Graduate",
        UGRAD: "Undergraduate"
      };
      var Types = {
        CAMPUS: "Campus",
        ONLINE: "Online",
        HYBRID: "Campus/Online"
      };
      var Degrees = {
        PHD: "PH.D",
        MS: "M.S.",
        BS: "B.S.",
        BA: "B.A.",
        GCERT: "Graduate Certificate"
      };
    
      return {
        EdLevels: EdLevels,
        Types: Types,
        Degrees: Degrees
      }
    });
    
    app.factory("Programs", function (EducationEnums) {
      var EdLevels = EducationEnums.EdLevels;
      var Types = EducationEnums.Types;
      var Degrees = EducationEnums.Degrees;
    
      return [
        {
          Department: "Biology",
          Website: "http://biology.wvu.edu",
          //Majors offered by department
          Majors: [{
            Major: "Biology",
            Education: [
              {
                EdLevel: EdLevels.GRAD,
                Type: Types.CAMPUS,
                Degree: Degrees.PHD
              },
              {
                EdLevel: EdLevels.GRAD,
                Type: Types.CAMPUS,
                Degree: Degrees.MS
              },
              {
                EdLevel: EdLevels.UGRAD,
                Type: Types.CAMPUS,
                Degree: Degrees.BA
              },
              {
                EdLevel: EdLevels.UGRAD,
                Type: Types.CAMPUS,
                Degree: Degrees.BS
              }
            ]
          }],
          Minors: [{
            //Minors can go here
          }]
        },
        {
          Department: "Statistics",
          Website: "http://biology.wvu.edu",
          Majors: [{
            Major: "Applied Statistics",
            Education: [
              {
                EdLevel: EdLevels.GRAD,
                Type: Types.HYBRID,
                Degree: Degrees.GCERT
              },
              {
                EdLevel: EdLevels.UGRAD,
                Type: Types.CAMPUS,
                Degree: Degrees.BS
              }
            ]
          }],
          Minors: [{
            //Minors can go here
          }]
        }
      ]
    });
    

    Next, I made a Majors service that uses this Programs data to build ViewModels (to be bound to scope in the controllers). Here you can build your original table, or you can build a matrix (like the Clemson site):

    app.service("Majors", function (Programs, EducationEnums) {
      var Degrees = this.Degrees = EducationEnums.Degrees;
    
      //Build ViewModel for all details
      this.getMajorDetails = function () {
        var arr = [];
        var programLen;
        var majorLen;
        var eduLen;
    
        for (var i = 0; i < (programLen = Programs.length); ++i) {
          var p = Programs[i];
          var dept = p.Department;
          var ws = p.Website;
          var Majors = p.Majors;
    
          for (var j = 0 ; j < (majorLen = Majors.length); ++j) {
            var maj = Majors[j].Major;
            var edu = Majors[j].Education;
    
            for (var k = 0; k < (eduLen = edu.length); ++k) {
              arr.push({
                Department: dept,
                Major: maj,
                EdLevel: edu[k].EdLevel,
                Type: edu[k].Type,
                Degree: edu[k].Degree,
                Website: ws
              });
            }
          }
        }
    
        return arr;
      }
    
      //Build ViewModel for Degrees offered (similar to Clemson)
      this.getMajorMatrix = function () {
        var arr = [];
        var programLen;
        var majorLen;
        var eduLen;
    
        for (var i = 0; i < (programLen = Programs.length); ++i) {
          var p = Programs[i];
          var Majors = p.Majors;
    
          for (var j = 0; j < (majorLen = Majors.length); ++j) {
            var maj = Majors[j].Major;
            var edu = Majors[j].Education;
            var obj = {
              Major: maj
            };
    
            for (var k = 0; k < (eduLen = edu.length); ++k) {
              var degree = edu[k].Degree;
              if (degree === Degrees.PHD) {
                obj.PHD = true;
              }
              else if (degree === Degrees.MS) {
                obj.MS = true;
              }
              else if (degree === Degrees.BS) {
                obj.BS = true;
              }
              else if (degree === Degrees.BA) {
                obj.BA = true;
              }
            }
    
            arr.push(obj);
          }
        }
    
        return arr;
      }
    });
    

    Your controller can just call the Majors service methods to bind the ViewModel to the $scope:

    app.controller('MajorsCtrl', function($scope, Majors) {
        $scope.majorDetails = Majors.getMajorDetails(); 
    });
    
    app.controller("MajorMatrixCtrl", function ($scope, Majors) {
      $scope.Degrees = Majors.Degrees;
      $scope.majorMatrix = Majors.getMajorMatrix();
    });
    

    Separting like this would allow you to later update the Programs factory to not just use static data, but could pull from a service via $http for instance. The Programs data can be manipulated to achieve your desired ViewModel through the Majors service (and Minors service if you choose to write a separate one).

    Updated Plunkr