1. Switch directories
git checkout step-onenpm start
2.
and step 10 no difference, here the main changes are the code, the code has done a lot of optimization, here is no longer posted.
3. Implementing the Code
the difference between step-10 and step-11:https://github.com/angular/angular-phonecat/compare/step-10...step-11
Dependencies (Dependent JS):
{ "name": "Angular-seed" "description": "A starter Project for AngularJS" " version ":" 0.0.0 " " homepage ":" Https://github.com/angular/angular-seed ", "license": "MIT" " private ": true " dependencies " "angular" : "1.2.x" " angular-mocks ":" ~1.2.x " , "bootstrap": "~3.1.1" " Angular-route ":" ~1.2.x ", "Angular-resource": "~1.2.x" }}
Here is Bower to manage JS library, add Angular-resource.js, this is through the NPM install or Bower install command to download angular-resource.js file.
Templates (template layer)
App/index.html
... < src= "Bower_components/angular-resource/angular-resource.js"></ Script> <src= "Js/services.js">< /script> ...
Note: Angular-resource.js is introduced here, about Ngresource can be viewed Api:https://docs.angularjs.org/api/ngresource
Service (services Tier)
app/js/services.js
.
var phonecatservices = angular.module (' phonecatservices ', [' Ngresource ']);p honecatservices.factory ( ' Phone ', [' $resource ', function($resource) { return $resource (' phones/:p Honeid.json ', {}, { query: {method:' GET ', Params:{phoneid: ' Phones '}, IsArray:true } }); }]);
We use the module API to register a custom service through a factory method. We pass in the name of the service Phone
and the factory function. Factory functions and controller constructors are similar in that they all declare dependent services through function arguments. The phone service declares that it relies on $resource
services.
$resource service allows you to create a restful client with just a few lines of code. Our application uses this client instead of the underlying $http service.
In fact, its internal implementation or through the $http way to obtain data, here is just a layer of additional packaging.
Usage of $resource :
$resource (URL, [paramdefaults], [Actions], options);
Controller (Controllers)
app/js/controllers.js
.
varPhonecatcontrollers = Angular.module (' phonecatcontrollers '), []); ... phonecatcontrollers.controller (' Phonelistctrl ', [' $scope ', ' Phone ',function($scope, Phone) {$scope. Phones=Phone.query (); $scope. Orderprop= ' Age ';}]); Phonecatcontrollers.controller (' Phonedetailctrl ', [' $scope ', ' $routeParams ', ' Phone ',function($scope, $routeParams, phone) {$scope. Phone= Phone.get ({phoneid: $routeParams. Phoneid},function(phone) {$scope. Mainimageurl= Phone.images[0]; }); $scope. SetImage=function(IMAGEURL) {$scope. Mainimageurl=ImageUrl; }}]);
by refactoring the underlying $http service, put it in a new service Phone
, we can greatly simplify the sub-controller ( PhoneListCtrl
and the PhoneDetailCtrl
). Angularjs's $resource is $http
better suited to interacting with a restful data source than it is. And now it's easier to understand what the controller code is doing.
Note the replacement of the code:
-phonecatcontrollers.controller (' Phonelistctrl ', [' $scope ', ' $http '- function- $http. Get (' Phones/phones.json '). Success (function- $scope. Phones =- -+phonecatcontrollers.controller (' Phonelistctrl ', [' $scope ', ' Phone '+ function + $scope. Phones = Phone.query (); = ' age '; }]);
---means replaced) (i.e., deleted), + + + represents the replacement (i.e., the newly added).
The packaging for actions in $resource here is as follows:
{' Get ': {method: ' GET '}, ' Save ': {method: ' POST '}, ' query ': {method: ' GET ', IsArray:true}, ' Remove ': {method: ' delete '}, ' delete ': {method: ' delete '}};
Here is the Get method. we use this simple statement to query all the phones.
Another very important note is that in the above code, when calling the phone service, we do not pass any callback functions. Although this seems to be the result of a synchronous return, it is not actually at all. The synchronization returned is a "future"-an object that is populated with data when the XHR is returned accordingly. Given the ANGULARJS data binding, we can use the future and bind it to our template. Then, when the data arrives, our view is automatically updated.
Sometimes, relying solely on future objects and data binding is not enough to meet our needs, so in these cases we need to add a callback function to handle the server's response. PhoneDetailCtrl
the controller is interpreted by setting it in a callback function mainImageUrl
.
4. Testing
Modify our unit tests to verify that our new service initiates HTTP requests and processes them as expected. The test also checks to see if our controller is working properly with the service.
$resource service enhances the resulting object by adding methods for updating and deleting resources. If we are going to use toEqual
a match, our test will fail because the test value will not be exactly the same as the response. To solve this problem, we need to use a recently defined toEqualData
Jasmine match. When a toEqualData
match is compared to two objects, it only takes into account the properties of the object and ignores all methods.
test/unit/controllersSpec.js
:
Describe (' Phonecat controllers ',function() {Beforeeach (function(){ This. Addmatchers ({toequaldata:function(expected) {returnAngular.equals ( This. Actual, expected); } }); }); Beforeeach (Module (' Phonecatapp ')); Beforeeach (Module (' Phonecatservices ')); Describe (' Phonelistctrl ',function(){ varscope, CTRL, $httpBackend; Beforeeach (Inject (function(_$httpbackend_, $rootScope, $controller) {$httpBackend=_$httpbackend_; $httpBackend. Expectget (' Phones/phones.json '). Respond ([{name:' Nexus S '}, {name: ' Motorola DROID '}]); Scope= $rootScope. $New(); CTRL=$controller (Phonelistctrl, {$scope: scope}); })); It (' should create ' phones ' model with 2 phones fetched from XHR ',function() {expect (scope.phones). Toequaldata ([]); $httpBackend. Flush (); Expect (Scope.phones). Toequaldata ([{name:' Nexus S '}, {name: ' Motorola DROID '}]); }); It (' should set the default value of Orderprop model ',function() {expect (Scope.orderprop). ToBe (' Age '); }); }); Describe (' Phonedetailctrl ',function(){ varscope, $httpBackend, CTRL, Xyzphonedata=function() { return{name:' Phone xyz ', images: [' Image/url1.png ', ' image/url2.png '] } }; Beforeeach (Inject (function(_$httpbackend_, $rootScope, $routeParams, $controller) {$httpBackend=_$httpbackend_; $httpBackend. Expectget (' Phones/xyz.json '). Respond (Xyzphonedata ()); $routeParams. Phoneid= ' xyz '; Scope= $rootScope. $New(); CTRL=$controller (Phonedetailctrl, {$scope: scope}); })); It (' Should fetch phone detail ',function() {expect (Scope.phone). Toequaldata ({}); $httpBackend. Flush (); Expect (Scope.phone). Toequaldata (Xyzphonedata ()); }); });});