惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

Minko Gechev's blog

skillgrade Unit Tests for AI Agent Skills You Should Care About AI Generative Development LLM-first Web Framework Reactive framework in ~200 lines of JavaScript Managing Angular Are LLMs going to replace us? Prefetching Heuristics Design Patterns in Open Source Projects - Part II Design Patterns in Open Source Projects - Part I What I learned doing 125 public talks - Part I Dynamic imports solve all the problems, right? 5 Angular CLI Features You Didn't Know About Angular quicklink Preloading Strategy Introducing Bazel Schematics for Angular CLI Building TypeScript Projects with Bazel Joining Google Playing Mortal Kombat with TensorFlow.js. Transfer learning and data augmentation Fast, extensible, configurable, and beautiful linter for Go Introducing Guess.js - a toolkit for enabling data-driven user-experiences on the Web Machine Learning-Driven Bundling. The Future of JavaScript Tooling. JavaScript Decorators for Declarative and Readable Code 3 Tricks For Using Redux and Immutable.js with TypeScript Follow Your Dream Career with Open Source. Personal Story. Redux Anti-Patterns - Part 1. State Management. Faster Angular Applications - Understanding Differs. Developing a Custom IterableDiffer Faster Angular Applications - Part 2. Pure Pipes, Pure Functions and Memoization Faster Angular Applications - Part 1. On Push Change Detection and Immutability Understanding Dynamic Scoping and TemplateRef Implementing a Simple Compiler on 25 Lines of JavaScript Developing Statically Typed Programming Language WebVR for a Gamified IDE 7 Angular Tools That You Should Consider Announcing ngrev - Reverse Engineering Tool for Angular Implementing Angular's Dependency Injection in React. Understanding Element Injectors. Distributing an Angular Library - The Brief Guide Angular in Production Ahead-of-Time Compilation in Angular 2.5X Smaller Angular 2 Applications with Google Closure Compiler Using Stripe with Angular (Deprecated) Building an Angular Application for Production Implementing the Missing "resolve" Feature of the Angular 2 Router Scalable Single-Page Application Architecture Managing ambient type definitions and dealing with the "Duplicate identifier" TypeScript error Static Code Analysis of Angular 2 and TypeScript Projects Enforcing Best Practices with Static Code Analysis of Angular 2 Projects ViewChildren and ContentChildren in Angular Dynamically Configuring the Angular's Router Angular 2 Hot Loader Lazy Loading of Route Components in Angular 2 Aspect-Oriented Programming in JavaScript Flux in Depth. Store and Network Communication. Using JSX with TypeScript Flux in Depth. Overview and Components. Even Faster AngularJS Data Structures Boost the Performance of an AngularJS Application Using Immutable Data - Part 2 Angular2 - First Impressions Build Your own Simplified AngularJS in 200 Lines of JavaScript Persistent State of ReactJS Component Boost the Performance of an AngularJS Application Using Immutable Data Processing Binary Protocols with Client-Side JavaScript Stream your Desktop to HTML5 Video Element Multi-User Video Conference with WebRTC Asynchronous calls with ES6 generators Binary Tree iterator with ES6 generators WebRTC chat with React.js AngularJS in Patterns (Part 3) AngularJS in Patterns (Part 2). Services. Using GitHub Pages with Jekyll! AngularJS in Patterns (Part 1). Overview of AngularJS Singleton in JavaScript Express over HTTPS What I get from the JavaScript MV* frameworks Remote Desktop Client with AngularJS and Yeoman AngularJS Inheritance Patterns AngularAOP v0.1.0 Advanced JavaScript at Sofia University AngularJS style guide Lazy prefetching of AngularJS partials VNC client on 200 lines of JavaScript Aspect-Oriented Programming with AngularJS CSS3 flipping effect Practical programming with JavaScript Why I should use publish/subscribe in JavaScript JavaScript, the weird parts Functional programming with JavaScript plainvm Looking for performance? Probably you should NOT use [].sort (V8) JavaScript image scaling ELang Caching CSS with localStorage Self-invoking functions in JavaScript (or Immediately Invoked Function Expressions) Asus N56VZ + Ubuntu 12.04 (en) Asus N56VZ + Ubuntu 12.04 Debian Squeeze + LXDE on Google Nexus S (or having some fun while suffering) HTML5 image editor Курсови проекти – ФМИ Carousel Gallery SofiaJS...
The magic of $resource (or simply a client-side Active Record)
2014-02-05 · via Minko Gechev's blog

At first sight AngularJS seems like a magical framework, which does some weird but awesome things like dependency injection, data binding only by setting a single property to the magical object named $scope and many other things.

If you look at the source code of Angular you’ll see a lot of smart and simple solutions for these magical problems. In this blog post I’ll describe the magic which stays behind $resource. I decided to write this blog post because of my recent experience in StackOverflow. One of the most misunderstood components of AngularJS was exactly the $resource service. $resource is two levels of abstraction above the XMLHttpRequest object (ok, may be three if we count $httpBackend). I’ve illustrated the usage of $resource through example which can be found at GitHub.

Before continuing with $resource I’m going to explain few important concepts. The first one is:

Active Record

This is architectural pattern named by Martin Fowler. The Active Record object is an object, which carries both data and behavior. Usually most of the data in these objects is persistent, responsibility of the Active Record object is to take care of the communication with the database in order to create, update, retrieve or delete the data. It may delegate this responsibility to lower level classes but calls to instance or static methods of the active record object cause the database communication.

For example:

function User(name, age) {
  this.id = null;
  this.name = name;
  this.age = age;
}

User.prototype.save = function () {
  var self = this;
  return db.process('INSERT INTO User (name, age) VALUES (' + this.name + ', ' + this.age + ')')
    .then(function (user) {
      self.id = user.id;
      return self;
    });
};

User.query = function () {
   return db.process('SELECT * FROM User');
};

In the example above we have User constructor function, which also has methods query and save. We can use it by:

var user = new User('foo', 42);
user.save()
.then(function () {
  //user.id is now available
});

User.query()
.then(function () {
  //all users
});

In the client-side JavaScript everything is a little bit different but the concepts are the same. What are the problems of this pattern? Well, in the relational databases we work with relations, which are represented as tables. We don’t have the standard OO concepts such as inheritance or association. Of course, we can emulate them by using tables but this leads to a problem – we don’t have 1:1 mapping between our tables and classes. Thats why sometimes for ORM is preferred the pattern Data Mapper, but topic is out of the scope of the current post. Fortunately, in the Single-Page Applications, usually we have back-end, which probably uses ORM framework and provides us a nice JSON API, so we don’t have to worry about complex mapping, we can just use client-side Active Record.

Lexical functional scope

This is very basic JavaScript concept but also one used in $resource. As you see in the save method, in example above, we keep reference to this in self. This reference is visible in the callback we pass to the promise returned by db.process. When the promise is resolved we just populate the missing data (in this case id) and return the object.

We can do something similar in query

User.query = function () {
   var users = [];
   users.$promise = db.process('SELECT * FROM User')
     .then(function (collection) {
        collection.forEach(function (user) {
          users.push(user);
        });
      });
   return users;
};

In this case when User.query() is called it immediately returns an empty array with property $promise. When the promise is resolved (i.e. database request successfully completed), we just push each of the users into our array users. Note that the user of our code already has reference to this variable, so when we populate it he will be able to use all the data immediately.

$digest loop

The last thing we will look at before continuing with $resource is AngularJS’s $digest loop. Probably you’re aware that when AngularJS finds some “special” directives in our templates it automatically registers watchers for the expressions used in these directives. Such “special” directives are {{ }}, ng-model, ng-bind. When something cause call of the digest loop AngularJS iterates over all watched expressions and calculates their values until there are no more changes (when we have wider support of Object.observe, probably the $digest loop will be more smarter and efficient).

So if we have:

function MainCtrl($scope) {
  $scope.users = [];
}

with the following markup:

<ul ng-controller="MainCtrl">
  <li ng-repeat="user in users">{{user.name}}</li>
</ul>

Now if somewhere else, something keeps the reference to the users array it can simply:

$scope.$apply(function () {
  users.push({
    name: 'foo'
  });
});

which will cause rendering of a single list item with the value "foo".

$resource

We won’t cover the whole API of $resource but we will look at how we can implement our model (not view model) with it. First of all, lets suppose we have a nice RESTful API for creating, retrieving, updating and deleting users:

POST   /users
POST   /users/:id # We won't use PUT, but we can
DELETE /users/:id
GET    /users
GET    /users/:id

Now we can create our model by:

var User = $resource('/users/:id');

$resource will create a constructor function for our model instances. Each of the model instances will have methods, which could be used for the different CRUD operation…and that’s all?! We implemented the communication with the back-end by invoking $resource with a single parameter!

But how about custom actions like login, for example? The $resource API allows us to include them explicitly.

I would recommend you to wrap the constructor function produced by $resource in a service. You will be able to reuse it across the application, mock it easier when writing tests and extend its functionality without modifying additional code (we follow the open-closed principle):

module.factory('User', function ($resource) {
  return $resource('/users/:id');
});

Now let’s get all users:

resourceDemo.controller('MainCtrl', function ($scope, User) {
  $scope.users = User.query();
});

And now visualize all users…:

<table>
  <tr ng-repeat="user in users">
    <td>{{user.id}}</td>
    <td><a ng-href="#/users/{{user.id}}">{{user.name}}</a></td>
    <td>{{user.job}}</td>
  </tr>
</table>

You might think that the operation query is synchronous because of the way we set the value of $scope.users. This will be very bad, right? Well, it is asynchronous, MainCtrl simply uses two of the concepts we discussed above: functional lexical scope, $digest loop.
When the query method is invoked it returns a reference to an empty array, the array has just a single property called $promise, you can use it if you want to manipulate the users after the request was successful, or not. When the request completes AngularJS populates the array with all users and calls the $digest loop. The query action will make a GET request to /users.

There is something cool in AngularJS’s route definition, which allows us to change the current page once all dependencies of the next page are resolved – the resolve hash. This feature is very helpful especially if we want to open a page with single user view:

//...
.when('/users/:userid', {
  templateUrl: 'partials/user.html',
  controller: 'UserCtrl',
  resolve: {
    user: function ($route, User) {
      return User.get({ userid: $route.current.params.userid }).$promise;
    }
  }
});
//...

As I mentioned above, when we invoke “static” method of the constructor function, it returns an empty object with property $promise, which will be resolved once the request is completed. This way we make sure the user will see the page once the user with id $route.current.params.userid is available.

And this is our User controller:

resourceDemo.controller('UserCtrl', function ($scope, user) {
  $scope.user = user;
});

You may noticed the setTimeout which is added in the express web app. It cause slower response, only to illustrate how useful could be our next enhancement – cache. We can cache some requests by passing additional parameters to the $resource function. For example:

resourceDemo.factory('User', function ($cacheFactory, $resource) {
  var User = $resource('/users/:userid', {}, {
    get: { cache: true, method: 'get' }
  });
  return User;
})

The example above will make each call of the get method cachable (it won’t affect the query method). After adding this line you’ll notice that, after the first time, on each subsequent click on somebody’s name you’ll be immediately redirected to the user’s view, you won’t have to wait the request to complete because AngularJS does not make any requests – it takes the value from the cache.

Most of these methods also accept callbacks, but I would recommend you to stick to the promisee usage.

Once again – you can grab the sample application here.

Conclusion

$resource allows us to use Active Record like pattern of communication with our RESTful service. It will definitely save you a lot of lines of code, if your back-end provides proper API. It gives you features like caching, interceptors, specifying response type and timeout, it even provides flag “with credentials“.

However, when you have more complex/custom back-end API, you may consider to implement all the communication by your own since, otherwise, you’ll need to override most of the methods of $resource, anyway.