0
\$\begingroup\$

I've been writing a somewhat complicated service that involves a lot of API function calls in Angular. After a somewhat tortured first attempt I have tried to take more of an MVC approach rewritten the Angular in the following ways.

  1. All data, once it's retrieved from the API, is stored in a Service with a setter or getter, i. e. (MODEL):

    .service('userFirstName', function() {
      var userFirstNameProp;
      return {
        getter: function() {
          return userFirstNameProp;
        },
        setter: function(value) {
            userFirstNameProp = value;
          }
        };
      }) 
    

    That way the data can be retrived from any controller via the setter or getter with a simple function call to determine whether it's already been properly set.

  2. All API calls are pushed into factories using Angular's $resource module. A sample call might be (CONTROLLER):

    .factory("someQueryFactory", function($resource, userToken) {
      var resource = $resource(serverPath + apiPath, {}, {
        get: {
          method: "GET",
          headers: {
            'uid': function() {
              var uid= curUid.getter();
              return token;
            }
          }
        }
      });
      return resource;
    })
    
  3. Each "page" is given its own route, partial, and controller (VIEW):

    $routeProvider
      .when('/', {
        templateUrl: '/partials/login1.html',
        controller: 'homePageController'
      })
    
  4. Each controller handles the logic of each page. I avoided using directives because the logic of the pages is not too complex and almost entirely consists of seeing if the user's last step has been completed to the satisfaction of the user's next step (i. e. has the user logged in? Then take them to the home page.)

So far so good, but two issues:

  • I notice my controller headers seem fairly bloated, mostly because of the need to include the the modules, services, and factories in them. So maybe something like:

     LSPortal.controller('portalLoginController', ['$scope', '$log', '$location', 'thingOne', 'thingTwo', 'thingThree', 'userStatus', 'userFirstName', 'userLastName', 'userPetName', 'userColor', 'userGender', 'apiQueryOne', 'apiQueryTwo', 'apiQueryThree', function($scope, $log, $location, thingOne, thingTwo, thingThree, userStatus, userFirstName, userLastName, userPetName, userColor, userGender, apiQueryOne, apiQueryTwo, apiQueryThree) { //logic }]);
    
    • I understand I could use something like RestMod or Restangular to reduce multiple query calls, but the API is complex and I want something that's going to be supported going forward in Angular that I don't have to throw out in three months.

    • It's also a lot of getter and setter calls, many times involving doing a bunch of setting at the end of one page only to have to do a lot of getting at the beginning of the next. This seems like an improvement over my formerly somewhat tortured use of $scope, but is it?

  • I've been feeling free to mix AngularJS with regular JS but the more I read the more it seems I should try to keep things in Angular as much as possible. Not sure if this is illustrated above but if someone needs a better example I'd be glad to provide one.

And is there anything else I'm missing? A lot of tutorials go over certain facets of angular but very few seek to put everything together. All help appreciated.

\$\endgroup\$
  • \$\begingroup\$ The desire to improve code is implied for all questions on this site. Question titles should reflect the purpose of the code, not how you wish to have it reworked. See How to Ask. \$\endgroup\$ – Jamal Sep 10 '15 at 2:28
  • \$\begingroup\$ Jamal, while I don't disagree with you after reading "How to Ask" my question was indeed about improving the code in multiple contexts. I don't ask such questions very often and it didn't seem like it belonged on Stack Exchange. So...I've learned from this and will try to position my comments appropriately in the future. \$\endgroup\$ – Dave Kanter Sep 10 '15 at 16:43
4
\$\begingroup\$

I notice my controller headers seem fairly bloated, mostly because of the need to include the the modules, services, and factories in them. So maybe something like

You can use the implicit method of dependency injection, where as long as the variable names match the dependency name, Angular will figure it out for you. However, for production, you'll need ngAnnotate to convert it back to the explicit form before minification.

I understand I could use something like RestMod or Restangular to reduce multiple query calls.

If this means wiring up multiple API calls to generate cohesive data (merging data from multiple calls), then I believe this calls for a service which hides stuff away from the controller. Multiple calls can be done in parallel.

// SomeMergedDataService
getSomethingFromMultipleResources: function(){
  return $q.all($http.get(), $http.get(), ...).then(function(values){
    // `values` is an array containing the results of each ajax call
    // Do the merge logic here
    return mergedData;
  });
};

// meanwhile... in your controller
App.controller('MyController', function(SomeMergedDataService){
  SomeMergedDataService.getSomethingFromMultipleResources().then(function(data){
    // you got merged data!
  });
});

Another way to do this is via the route's resolve property. It's essentially the same thing, except that instead of injecting the service and calling a method to get the data, the injected resolve is the data. A resolved property takes a function that can return a promise. You can feed the result of your $q.all().then() as the return.

a bunch of setting at the end of one page only to have to do a lot of getting at the beginning of the next

One way to do it is by way of $rootScope. You can treat $rootScope as the global space of the application.

If you don't want to pollute $rootScope, you can also do it like how React+Flux, by using services as storage locations. This does entail getters and setters though, which I think you're already doing now.

If you want the state pushed down to the controller level, you can nest controllers. Descendant controllers have access to the $scope properties of the enclosing controller. This means that you can put stuff on the parent controller and have access to it from any descendant.

<div ng-controller="PageController">
  <div ng-controller="HomeController" ng-show="whenRouteIs('HOME')"></div>
  <div ng-controller="AboutController" ng-show="whenRouteIs('ABOUT')"></div>
</div>

I've been feeling free to mix AngularJS with regular JS but the more I read the more it seems I should try to keep things in Angular as much as possible.

It's perfectly fine to mix. They're all JS anyways. It's really up to you. Angular is pretty flexible despite its crazy conventions.

Angular does provide some very handy modules which are usually optimized to how Angular internals work. I especially use $timeout instead of setTimeout, or $log instead of console.

However, I certainly dislike certain built-ins such as $q as it doesn't behave like the native promises. This makes $http and resources weird as it uses $q internally.

A lot of tutorials go over certain facets of angular but very few seek to put everything together.

I suggest you go over and look at JHipster. It's a stack that uses Angular+Spring. I'm not suggesting you go over to Java, but it has a good way of structuring an Angular app front-to-back. I suggest you go try to run it's setup and see how it provides the default services, controllers, templates etc. and how it's made to talk to the server.

\$\endgroup\$
  • \$\begingroup\$ Fantastic answer. Exactly what I was looking for. \$\endgroup\$ – Dave Kanter Jul 7 '15 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.