Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this architecture still valid under Angular 1.2 and above

There are many code examples using $resource out there. I came across this one, and the code is quite clearly laid out: https://github.com/apotry/stockwatch

I like this example because:

  • It interacts with a Rails backend, which is how I am using Angular
  • It uses $resource
  • It is using a non-rest route (ohlc)
  • The code is quite clean

To call the functions defined is quite simple as shown in the controller code below, but is it a good idea to embed the save in the factory?

My question is: now that Angular 1.2+ includes promises, is this type of code still valid and considered a good practice? How would this code respond to an error state?

Here the resources are defined

app.factory('Stock', ['$resource', function($resource) {
  function Stock() {
    this.service = $resource('/api/stocks/:stockId', {stockId: '@id'}, {'update': { method: 'PUT' }});
  };
  Stock.prototype.all = function() {
    return this.service.query();
  };
  Stock.prototype.delete = function(stId) {
    return this.service.remove({stockId: stId});
  };
  Stock.prototype.create = function(attr) {
    return this.service.save(attr);
  };
  Stock.prototype.update = function(attr) {
    return this.service.update(attr);
  };
  Stock.prototype.ohlc = function(stId) {
    return $resource('/api/stocks/:stockId/ohlc', {stockId: '@id'}).get({stockId: stId});
  }
  return new Stock; 
}]);

Here is an example of the delete, create and a custom route (ohlc):

  $scope.requestOHLC = function (stockid) {
    return Stock.ohlc(stockid);
  }

  $scope.createStock = function() {
    $scope.fetchYahooFinanceData($filter('uppercase')    ($scope.newCompany["symbol"])).then(function(result) {
      $scope.error = false;
      $scope.stocks.push(Stock.create(result));
      $scope.newCompany = '';
    }, function(error) {
      $scope.error = true;
    });
  };

  $scope.deleteStock = function(id, idx) {
    Stock.delete(id);
    $scope.stocks.splice(idx, 1);
  };

EDIT

I am trying to work out a simple and clear practice for using $resource based rest routes in angular.

Different from the above code, but based on it. Assume the code below uses a service which is basically the same as the factory above. In this example I call one REST resource to create a new resource (rails table entry), and then pass the newly created id to another call. Note that createPanelHeader references the $scope.selector.paneldata.primer3_parameter_id scope variable. Not sure if this is a good practice either.

I found this would not work unless I uses $promise.then but this seemed a bit convoluted. Am I on the right track?

// Section: Create Panel header
createPrimer3Parameter = function() {
    primer3_parameter = Primer3Parameter.create().$promise.then(function(primer3_parameter){
    $scope.selector.paneldata.primer3_parameter_id = primer3_parameter.id;
    createPanelHeader();
    }, function() {
      alert('Error creating primer3parameter');
    })
};

COMMENT

I am really just trying to work out a simple method for accessing REST resources from a Rails API, with at most 1 level of nesting. I think I am missing something as it seems remarkably difficult.

What I am hearing so far is not to use $resource, even under 1.2. That I should instead use raw $http or Restangular.

Also, there seem to be some 1.2 changes that affect Restangular. The solution feels a bit like a hack to me:

https://github.com/mgonto/restangular#using-values-directly-in-templates

UPDATE

I didn't really come away 100% clear, so I have posted a Bounty: https://bountify.co/write-an-angular-service-for-these-rails-routes-using-restangular

like image 538
port5432 Avatar asked Jan 11 '23 10:01

port5432


1 Answers

is this type of code still valid and considered a good practice?

This code is valid, but considered deprecated as of 1.2.0-rc3. It will work in all version of angular 1.2 and 1.3 up to but not including 1.3.0-beta10, where automatic promise unwrapping has been removed.

$scope.stocks.push(Stock.create(result));

In the line above you've created an array of promise objects on $scope. Then in index.html.erb you are referencing the promise directly via the stock iterator:

<li ng-repeat="stock in stocks">
      <div id='symbol'>
        {{stock.symbol}}
      </div>

The non-deprecated way of handling promises does not allow you to bind directly to the promise like this.

How would this code respond to an error state?

The app is handling errors here:

}, function(error) {
  $scope.error = true;
});

and here:

<div ng-show="error">
    There was a problem with the Yahoo Finance API. Please try again later.
</div>

For error handling, you're not binding with a promise directly so this works fine in all versions of angular.

A better way?

First, shelf the javascript directory structure in the stockwatch example. Then, follow this directory structure instead. Finally, integrate Restangular into your project. Create a stockModel factory that internally instantiates a Restangular object, but returns the object that will be populated later after the promise resolves (model). Instead of binding a promise in your partial, bind the unpopulated result object.

.factory('stocksModel', function (Restangular) {
  var model = {};
  var rest_stocks = Restangular.all('stocks');

  model.doSomethingRESTful = function (...) {
    // return a promise in case the controller needs it
    return rest_carts.some_restangular_method(...)
      .then(function() {
        model.some_data_which_was_acquired_RESTfully = ...;
      });
  };

  return model;
});

In your controller:

$scope.stocks = stocksModel;

In your partial:

{{stocks.some_data_which_was_acquired_RESTfully}}
like image 162
Gil Birman Avatar answered Jan 26 '23 02:01

Gil Birman