1

I have a factory, which goes into a controller, and I am trying to get data from that display on an HTML page. I am having trouble specifying an Object's pathway however.

My Factory:

app.factory('APIMethodService', function() {
  var Head = "api.example.com";
  return {
    apis: 
    [{
      accounts: [
        {
          v1: [
            {
              uri: Head+"/v1/accounts/",
              item1: "AccountNumber",
              item2: "MoneyInAccount"
            }],
            v2: [
            {
              uri: Head+"/v2/accounts/",
              item1: "AccountNumber",
              item2: "MoneyInAccount"
            }]
        }
      ],
      customers: [
        {
          v1: [
            {
              uri: Head+"/v1/customers/",
              item1: "CustomerName",
              item2: "CustomerID",
              item3: "CustomerEmail"
            }]
        }
      ]
    }]
  };
});

My Controller:

app.controller('APIController', function($scope, APIMethodService) {
$scope.title = "API";
  $scope.apiList = APIMethodService;
  $scope.accountList = $scope.apiList.accounts.v1;
  $scope.accountList2 = $scope.apiList[0][0];
});

My HTML

<div ng-controller="APIController">

<div id="api" class="row">
  <div class="col-xs-12">
    <div class="row" style="font-size:20px">
      {{title}} Page!
      <table class="table table-striped">
        <tr ng-repeat="api in apiList | orderBy:'uri' | filter:search">
           <td>{{api.uri}}</td>
           <td>{{api.item1}}</td>
           <td>{{api.item2}}</td>
        </tr>
      </table>
    </div>
  </div>
</div>

</div>

The errors I get are in regards to the Controller trying to parse out the individual objects I wish to grab, like accounts or customers, and then any version v#, they may have.

So it will say something such as

TypeError: Cannot read property 'v1' of undefined

I just need some help specifying the proper pathways into my factory service.

1
  • 1
    your service is returning an object with multiple arrays, but you aren't referencing the elements of any of those arrays. I think you want $scope.apiList.apis[0].accounts[0].v1 maybe? Commented Feb 1, 2015 at 18:09

2 Answers 2

1

You have a few problems. First, you are referring to the object returned from the factory incorrectly. APIMethodService is the factory that you're injecting, so you need to first reference the object that that factory is returning like this:

APIMethodService.apis

This will give you your entire JSON object.

From there, the rest of your object is made up of arrays of objects, so referring to 'v1' won't do you any good. You need to specify an index instead. If you want v1, you'll need:

APIMethodService.apis[0].accounts[0].v1

This will give you the v1 array, which again is an array of objects.

Customers would be:

APIMethodService.apis[0].customers[0].v1
Sign up to request clarification or add additional context in comments.

3 Comments

Quick question, so when you say APIMethodService.apis[0].accounts[0].v1, do I need to specify the APIMethodService.apis above it still?
The customers pathway does not seem to work. TypeError: Cannot read property 'customers' of undefined
Also to answer your first question, yes, you need to specify that.
0

The first problem you have is that the factory returns an object with a single property called apis. So basically this $scope.apiList.accounts.v1 should be $scope.apiList.apis.accounts.v1. Bu that's not all as this won't either work since dotting(.) into apis is an array you'd have to use the index. In this case it would be $scope.apiList.apis[0] and then you could .accounts[0].v1 which is also an array containing a single object.

Now if you can I would suggest to you that you'd change how you represent this data structure.

This is how you could do it.

app.factory('APIMethodService', function() {
    var Head = "api.example.com";
    return {
        accounts: {
            v1: {
                uri: Head+"/v1/accounts/",
                items: ["AccountNumber","MoneyInAccount"]
            },
            v2: {
                ... // skipped for brevity
            }
        },
        customer: {
           ... // code skipped for brevity
        }
    };
});

And then it's just a matter of dotting into your APIMethodService-object like APIMethodService.accounts.v1.items[0] if you want the AccountNumber method name.

Constructing your url could then be done like this.

var baseUrl = APIMethodService.accounts.v1.uri; // 'api.example.com'
var url = baseUrl + APIMethodService.accounts.v1.items[0]; // 'AccountNumber'
// url = "api.example.com/v1/accounts/AccountNumber"

Again, this is one way you could do it but this can be further enhanced upon. The examples I provided are simply for demo purposes and this is not in any way the only way to do it.

Expanding upon recieved comments/questions your service (and data representation) could now look like this.

app.factory('APIMethodService', function() {
    var Head = "api.example.com";
    return {
        accounts: {
            v1: {
                uri: Head+"/v1/accounts/",
                items: [
                    {
                        name:'AccountNumber',
                        description:'Show the account number'
                    },
                    {
                        name:'AccountOwner',
                        description:'Show information about the owner of the account'
                    },
                    {
                        name:'MoneyInAccount',
                        description:'Show money in the Account'
                    }
                ]
            },
            v2: {
                ... // skipped for brevity
            }
        },
        customer: {
           ... // code skipped for brevity
        }
    };
});

// Get descriptions
var accountNumberDescription = APIMethodService.accounts.v1.items[0].description; // 'Show the account number'
var accountOwnerDescription = APIMethodService.accounts.v1.items[1].description; // 'Show information about the owner of the account'
var moneyInAccountDescription = APIMethodService.accounts.v1.items[2].description; // 'Show money in the Account'

By using objects with properties like this it's alot easier to understand what you are trying to do. With arrays with indexes you'd have to know or take a look at the source to see what's going on. Here, someone viewing your code they can instantly understand that it is the description you are getting.

5 Comments

I like this layout more (I got mixed up in object-mode). However, I have a question. Would it be more professional to something like the names["AccountNumber","MoneyInAccount"], then having another object like desc["Shows the account number.","Shows money in the account."], or to make an Object for each one? i.e. account["AccountNumber","Shows the account number"]?
I am also confused on why you make a base variable with APIMethodService in it, then for url, you include base + the APIMethodService. again?
Since APIMethodService represents your object I first need a way to get hold of the url. base in this case would be 'api.example.com'. Since APIMethodService just is a object I still need to use it again in order to get hold of the items array containing the actual names of the endpoint. Then I simply concatinate the two string together. You could just as well have done var completeUrl = APIMethodService.acounts.v1.uri + APIMethodService.accounts.v1.items[0]. All you are doing is referencing that object.
To answer your first comment, this comes to personal style I would say. In my honest opinion you should have an object with a structure like this { name: 'AccountNumber', desc:'Shows the account number.' }. Then what you would do is add those to an array. Properties related to one another should be grouped together as they describe the object itself. There are greate libraries out there that can help you with, for instance finding the object who's name equals 'AccountNumber'. A personal favorite of mine is http://underscorejs.org.
I've updated my answer with basically what my two comments where to you as a response.

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.