1

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; 
});
2
  • Can you change the data structure or does it have to be left the same? Commented Dec 30, 2014 at 15:37
  • @WayneEllery We can change it, its not being passed by any other documents, but later it may be passed from via JSON. Commented Dec 30, 2014 at 16:51

1 Answer 1

1

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

  • A department can offer multiple majors
  • A department has a website
  • Each major can offer one to many Educations (i.e. Education Level, On/Off Campus, Degree)
  • The department can offer multiple minors with the same data structure as majors

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

Sign up to request clarification or add additional context in comments.

9 Comments

I'll look into this solution. Thanks very much for your help. I'm not familiar with services or factories that much right now, but you had an excellent explanation of each. After I do a quick implementation of this while reading over some docs on the angular site I'll give it the check mark.
Just curious how this is technically working, both factories are simply returning data to the service, so the controller is simply used for view purposes?
Factory returns data to be used by service (factory hard coded programs for now, could switch to using $http as long as the data structure remains the same for the Majors service). The Majors service exposes methods to transform this data to a ViewModel to be used by the controller. The controller's logic remains simple (as it should) and sets ViewModel returned from service to the $scope to be used within the view/template.
Excellent. Thank you. I'm new to the angular way of thinking, but it seems pretty awesome.
Is there a way to do a hybrid approach, where I display the website in an href. I've been deconstructing ever since you answered and still can't find a good way. I'm sure that it has something to do with looping through each of the items and stuffing them into the appropriate arrays. Would I have to do another array and loop through the contents of each website group?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.