Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Active Record or Data Mapper pattern for Angularjs?

I'm very new to Angular, so hopefully I know enough to ask what seems like a reasonable design question.

I'm charting some data via Angular, and am using $resource. Before bringing Angular in to the project I made a chart factory function for creating chart objects from sample json data I just pasted into the view.

Now that I'm using Angular, it's tempting to put the charting functions into the 'Chart' resource, Active Record style, so that I have this one thing that can draw, save, update, etc.

While the advantage of that pattern is simplicity, the drawback is coupling persistence with behavior. For example, it would be pretty awkward if I wanted to save chart settings to local storage.

Having been bitten by AR enough times in my career already, I want to go with DM by leaving my chart object as-is, and having the controller pass data from the resource into my chart.

However! My hazy understanding of angularjs' dependency injection suggests that I might be able to create a resource or some such that could accept a common persistence interface - is the right word a 'scope'?

Example AR strategy:

App.factory('Chart', [
  '$resource', function($resource) {
    var chart = $resource('/api/charts/:id', {
      id: '@id'
    });

    chart.draw = function() { ... }

    return chart
  }
]);

App.controller('ChartsCtrl', [
  '$scope', 'Chart', function($scope, Chart) {
    $scope.charts = Chart.query(function() {
      $.each($scope.charts, function(i, c) { c.draw() })
    })
  }
])

Example DM strategy:

App.chart = function(resource) {
  return { draw: function() { ... } }
}

App.factory('ChartResource', [
  '$resource', function($resource) {
    return $resource('/api/charts/:id', {
      id: '@id'
    })
  }
])

App.controller('ChartsCtrl', [
  '$scope', 'ChartResource', function($scope, ChartResource) {
    $scope.charts = $.map(ChartResource.query(), function(i, resource) {
      chart = App.chart(resource)
      chart.draw()
      return chart
    }
  }
])

I think there is a third way, though, that I don't see because I don't quite grok how to leverage DI.

In other words, what's the AngularJS way to create an object with swappable persistence strategies?

like image 990
Woahdae Avatar asked Jul 02 '13 21:07

Woahdae


People also ask

What is Active Record Data Mapper?

Using the Active Record approach, you define all your query methods inside the model itself, and you save, remove, and load objects using model methods. Simply said, the Active Record pattern is an approach to access your database within your models. You can read more about the Active Record pattern on Wikipedia.

What is Data Mapper in ORM?

A data mapper is an instance of a class that implements the Opis\Orm\IDataMapper interface. This interface provides several methods that can be used to manipulate the data associated with an entity. This data represents a row that is or will be stored into a table.

Is Sequelize Active Record or Data Mapper?

Sequelize belongs to Object Rational Mappers category while TypeORM can be primarily classified as a Microframework. TypeORM supports Active Record and Data Mapper patterns unlike other JavaScript ORMs allowing you to write high quality, scalable and maintainable applications productively.

What is Active Record implementation?

Active Record Implementation is an architectural pattern found in software engineering that stores in-memory object data in relational databases. Active Record facilitates the creation and use of business objects whose data is required to persistent in the database.


1 Answers

The DataMapper strategy is actually already a form of Dependency Injection. You are passing the desired persistence implementation to the Chart constructor, and you can pass a different one in on a per-new basis. The non-DI way would be to hard-code the persistence implementation, as in your ActiveRecord-style example.

DataMapper isn't DI in the Angular.JS-specific sense of the term. Angular's DI doesn't really let you swap implementations around at run-time. However, you can use the official ngMock module to achieve this. ngMock is supposed to be used for testing, so this probably is not a terrific idea outside of that scenario.

There doesn't seem to be an Angular.JS-specific convention for this sort of thing. In fact, Angular.JS doesn't really have many conventions at all.

Rather than pass in the implementation in the constructor, you could alternatively offer a separate method for changing the persistence mechanism at any time. For example, to use ChartResource for initial network-based retrieval, then swapping to IndexedDB to store them locally:

// ChartResourceIndexedDB: same API as $resource but uses local IndexedDB
app.factory('ChartResourceIndexedDB', /* .. */);

app.controller('ChartsCtrl', [
  '$scope', 'ChartResource', 'ChartResourceIndexedDB',
  function($scope, ChartResource, ChartResourceIndexedDB) {
    $scope.charts = $.map(ChartResource.query(), function(i, resource) {
      chart = App.chart(resource)
      chart.draw();
      chart.setPersistence(ChartResourceIndexedDB);
      chart.save();
      return chart
    }
  }
]);
like image 197
jokeyrhyme Avatar answered Oct 10 '22 00:10

jokeyrhyme