When declaring a module, we need to pass two parameters to the method. The first is the name of the module we are creating. The second is the list of dependencies,
otherwise known as injectables.
angular.module('myApp', []);
Generically, we call these dependencies services, as they provide specific services to our application
Angular comes with many services like
$location, for interacting with the browser’s location,
$route, for switching views based onlocation (URL) changes, and
$http, for communicating with servers
An example:
var shoppingModule=angular.module('ShoppingModule',[]);
shoppingModule.factory('shopList',function(){
var items={};
items.query=function(){
// In real apps, we'd pull this data from the server...
return [
{title: 'Paint pots', description: 'Pots full of paint', price: 3.95},
{title: 'Polka dots', description: 'Dots with polka', price: 2.95},
{title: 'Pebbles', description: 'Just little rocks', price: 6.95}
];
};
return items;
});
var ShoppingController=function(shopList,$scope){
$scope.items=shopList.query();
}
{{greeting}}
Then there’s an attribute-based directive called ng-bind:
Both are equivalent in their output
For the data binding you do in your index.html page, however, use ng-bind instead. That way,your users will see nothing until the data has loaded
function StartUpController($scope) {
$scope.funding = { startingEstimate: 0 };
computeNeeded = function() {
$scope.funding.needed = $scope.funding.startingEstimate * 10;
};
$scope.$watch('funding.startingEstimate', computeNeeded);
}
In this way, funding.needed will automatically update whenever funding. starting Estimate changes
错误小结:
function DeathrayMenuController($scope) {
$scope.menuState.show = false;//未初始化menuState;应在前添加语句$scope.menuState={ };
$scope.toggleMenu = function() {
$scope.menuState.show = !$scope.menuState.show;
};
}
Nested(嵌套) controllers that can share model and functions through an
inheritance tree\
...
the actual nesting happens in scopes. The $scope passed to a nested controller prototypically inherits from its parent controller’s $scope.
1.There are also some indirect ways to set up the model from the template itself
setting properties in expressions is the same as setting a property of the controller’s scope.
2.Using ng-model on a form input.
ng-model
the ng-model attribute, which tells AngularJS to create a two-way binding between the value of the input element and the done property of the corresponding data object
Exa:
{{item.action}}
{{item.done}}
$watch(watchFn, watchAction, deepWatch)
watchFn
This parameter is a string with an Angular expression or a function that returns
the current value of the model that you want to watch
watchAction
This is the function or expression to be called when the watchFn changes
deepWatch
If set to true, this optional boolean parameter tells Angular to examine each property within the watched object for changes
Examples:
Way-1:
var calculateTotals = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.total=total;
$scope.bill.discount=total>100?10:0;
$scope.bill.subtotal=total-$scope.bill.discount;
}
$scope.$watch('items',calculateTotals,true);
Way-2:
$scope.$watch(function(){
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
$scope.bill.total=total;
$scope.bill.discount=total>100?10:0;
$scope.bill.subtotal=total-$scope.bill.discount;
});
if you’ve got an object with two properties a and b in your scope, and
want to execute the callMe() function on change
1.Put them into an array or object and pass in deepWatch as true.
$scope.$watch('things.a + things.b', callMe(...));
2.Watch a concatenated value of the properties
$scope.$watch('things', callMe(...), true);
we’ll use a service. A service persists across controllers for the duration of the application’s lifetime and is the appropriate place for us to hide business logic away from the controller.
We need to configure our app when it boots up, so we need to create our service using the .provider() method. This method is the only service creation method that we can inject into .config() functions.
.provider('Weather', function() {
var apiKey = "";
this.setApiKey = function(key) {
If(key)
this.apiKey = key;
};
this.$get = function($http) {
return {
// Service object
}
}
})
Firstly a provider is a function that must return an object containing the $get property. The mentioned $get property is a factory function, that when invoked should return a service instance.We can think of providers as objects that embed factory functions in their $get property
.config(function(WeatherProvider) {
WeatherProvider.setApiKey('');
})
The important thing to notice here is a dependency on the WeatherProvider objects with the Provider suffix representing the recipes that are ready to be executed
在controller中调用
.controller('myCtrl',function($scope, Weather){
$scope.hi = Weather;
});
First of all:We need to reference angular-route in our HTML after we reference Angular itself.
we need to reference the ngRoute module as a dependency in our app module:
angular.module('myApp',['ngRoute']);
You create routes in your application by calling functions on the $routeProvider service as a configuration block. It goes something like this pseudo-code:
var someModule = angular.module('someModule', [...module dependencies...])
someModule.config(function($routeProvider) {
$routeProvider
.when('url', {
controller:aController,
templateUrl:'/path/to/tempate'
})
.when(...other mappings for your app...)
.otherwise(...what to do if nothing else matches...);
)};
$routeParams
if we start a route param with a colon (:), AngularJS will parse it out and pass it into the $routeParams
$routeProvider
.when('/inbox/:name', {
controller: 'InboxController',
templateUrl: 'views/inbox.html'
})
then Angular will populate the $routeParams with the key of :name, and the value of key will be populated with the value of the loaded URL
If the browser loads the URL /inbox/all, then the $routeParams object will look like:
{ name: 'all' }
As a reminder, to get access to these variables in the controller, we need to inject the $routeProvider in the controller:
app.controller('InboxController', function($scope, $routeParams) {
// We now have access to the $routeParams here
//取出name值使用 $routeParams.name
});
$routeProvider
Route
Resolve: An optional map of dependencies which should be injected into the controller
· key – {string}: a name of a dependency to be injected into the controller.
· Factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. Use $route.current.params to access the new route parameters
$routeProvider
.when('/', {
controller: 'ListCtrl',
resolve: {
recipes: function(MultiRecipeLoader) {//recipes注入到ListCtrl控制器中
return MultiRecipeLoader();//factory 服务 返回 promise 类型数据
}
},
templateUrl:'views/list.html'
})
ListCtrl:
app.controller('ListCtrl', ['$scope', 'recipes',function($scope, recipes) {
$scope.recipes = recipes;
}]);
Responding to Route Changes
The Events Defined by the $route Service
$routeChangeStart: Triggered before the route is changed
$routeChangeSuccess: Triggered after the route has changed
$routeUpdate: Triggered when the route is refreshed; this is tied to the reloadOnSearch configuration property, which I describe in the “Configuring Routes” section
$routeChangeError: Triggered if the route cannot be changed
exa:
$scope.$on("$routeChangeSuccess", function () {
// ...statements for responding go here...
});
ngView
ngView is a directive that complements(补足,完善) the $route service by including the rendered template of the current route into the main layout (index.html) file. Every time the current route changes, the included view changes with it according to the configuration of the $route service.
templateUrl
templateUrl – {string=|function()=} – path or function that returns a path to an html template that should be used by ngView.
格式:
同一路径下:templateUrl:'list.html'
不同路径:templateUrl:'views/list.html'或者
使用绝对路径 templateUrl:’../app/views/list.html’
API Overview
A basic pseudo-code template for creating any directive follows:
var myModule = angular.module(...);
myModule.directive('namespaceDirectiveName', function factory(injectables) {
var directiveDefinitionObject = {
restrict: string,
priority: number,
template: string,
templateUrl: string,
replace: bool,
transclude: bool,
scope: bool or object,
controller: function controllerConstructor($scope,$element,$attrs,$transclude){...},
require: string,
link: function postLink(scope, iElement, iAttrs) { ... },
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
}
};
return directiveDefinitionObject;
});
The name of the directive should always be pascalCased(驼峰式), and the function we provide should return an object
In our case, we declare the directive in HTML using my-directive, the directive definition must be myDirective
The following are valid formats for declaring the directive we built above:
The restrict option tells Angular which declaration format(s) to look for
when compiling our HTML.
we can specify that we want our directive to be invoked if it is an element (E), an attribute (A), a class (C), or a comment (M):
If you omit(忽略) the restrict property, the default is A, and your directive can be used only as an attribute.
template:
Specify an inline template as a string. Not used if you’re specifying your template as a URL
.directive('someDirective' function() {
return {
template: 'some stuff here'
}
})
The result when the directive is invoked will be
some stuff here
If we set replace as true:
.directive('someDirective' function() {
return {
replace: true // MODIFIED
template: 'some stuff here'
}
})
then the result when the directive is invoked will be:
some stuff here
Scope Option (boolean|object)
scope is optional. It can be set to true or to an object, {}.
By default, it is set to false.
When scope is set to true, a new scope object is created that prototypically inherits from its parent scope.
.directive("scopeDemo", function () {
return {
template:"Name: ",
scope:true
}
})
To create a directive with isolate scope we’ll need to set the scope property of the directive to an empty object, {}. Once we’ve done that, no outer scope is available in the template of the directive
This is the consequence of the isolated scope; because there is no inheritance from the controller’s scope, there are no values defined for any of the properties specified by the ng-model directive
One-way binding
The first change is in the scope definition object, where I set up a one-way mapping between an attribute and a property in the directive scope
scope: {
local: "@nameprop"
}
The second change is to define the nameprop attribute on the elements to which I apply my custom directive,
The final change is to update the template so that it displays the value of the local property
Data Value: {{local}}
Two-way binding
To create a two-way binding, I replace the ‘ @ ’ character with the ‘ = ’ character when I create the isolated scope
from the previous example:
scope: { local: "@nameprop" }
becomes this:
scope: { local: "=nameprop" }
However.,When using a one-way binding, I provided a binding expression complete
with the {{ and }} characters, but AngularJS needs to know which property to update with changes in a two-way binding, so I have to set the attribute value to a property name,
Transclude(内嵌)
When set to true, the directive will delete the original content, but make it available for reinsertion within your template through a directive called ng-transclude
appModule.directive('hello', function() {
return {
template: 'Hi there ',
transclude: true
};
});
applying it as:
hello>Bob
We would see: “Hi there Bob.”
link
We use the link option to create a directive that manipulates the DOM.
Compile
As we are creating a new element, we need to use the compile function rather than just the link function and a template, since our
element cannot be nested underneath an element
Validating User Input
we can access the validation state of the form through a property called $valid. Angular will set this to true when all the inputs in the form are valid
Sign Up
Communicating with Servers
$http
function Controller($scope,$http){
$http.get('http://localhost:8080/stu_restful/rest/student/getTheStudent?name=coco')
.success(function(data,stastus,headers,config){
$scope.student=data.student;
});
}
或者
$http.get('http://localhost:8080/stu_restful/rest/student/getTheStudent',
{params:{name:'coco'}}
).success(function(data,stastus,headers,config){
$scope.student=data.student;
});
第三种:
$http({
method: 'GET',
url:"http://localhost:8080/stu_restful/rest/student/getTheStudent",
params:{name:'zc'}
}).success(function(data,stastus,headers,config){
$scope.student=data.student;
});
Use Promise
What’s a Promise
A promise is a method of resolving a value (or not) in an asynchronous manner.Promises are incredibly useful in dealing with remote objects, and we can think of them as a proxy for our remote objects.
the $http method returns a promise object, we can use the then method to handle the callback when the response is ready
angular.module('myApp',[])
.controller('Controller',function($scope,$http){
var promise=$http({
method:'GET',
url:'http://localhost:8080/stu_restful/rest/student/getTheStudent',
params:{name:'zc'}
});
promise.then(function(resp){
var data=resp.data;
$scope.student=data.student;
},function(resp){
console.log("error");
});
});
Returned promise is like this:
Object {data: Object, status: 200, headers: function, config: Object}
取出数据 运用 promise.data 即可
使用RESTful资源
The ngResource module is an optional Angular module that adds support for interacting with RESTful back-end data sources.
we need to reference the ngResource module as a dependency in our app module:
angular.module('myApp', ['ngResource']);
//The $resource service itself is a factory that creates a resource object. The returned $resource object has methods that provide a high-level API to interact with back-end servers
var User = $resource('/api/users/:userId.json',
{
userId: '@id'
});
We can think of the User object as an interface to our RESTful services
User.get({
id: '123'
}, function(resp) {
// Handle successful response here
}, function(err) {
// Handle error here
});
• Users.query(params, successcb, errorcb): It issues an HTTP GET request and expects an array in the JSON response. It is used to retrieve
a collection of items.
• Users.get(params, successcb, errorcb): It issues an HTTP GET request and expects an object in the JSON response. It is used to retrieve a single item.
• Users.save(params, payloadData, successcb, errorcb): It issues an HTTP POST request with request body generated from the payload.
• Users.delete(params,successcb, errorcb) (and its alias: Users.remove): It issues an HTTP DELETE request.
$resource instances
• $save()
• $remove()
• $delete()
// Using the $save() instance methods
User.get({id: '123'}, function(user) {
user.name = 'Ari';
user.$save(); // Save the user
});
// This is equivalent to the collection-level
// resource call
User.save({id: '123'}, {name: 'Ari'});
$resource Instances Are Asynchronous
// $scope.user will be empty
$scope.user = User.get({id: '123'});
We can wait for the data to come back as expected using the callback method that the methods provide:
User.get({id: '123'}, function(user) {
$scope.user = user;
});
例子:
angular.module('myApp', ['ngResource'])
.factory('stuResource', ['$resource', function($resource) {
return $resource('http://localhost:8080/stu_restful/rest/student/getTheStudent:userId'
,{userId:'@id'}
);
}])
.controller('Controller',function($scope,stufac){
stuResource.get({name: 'zc'}, function(user) {
console.log(user);
$scope.student= user.student;
});
});
The :id part, which corresponds to the map object that is the second argument, tells AngularJS that if the data object it is working with has an id property, then it should be appended to the URL used for the Ajax request.
What about that second object? The {id: @id}? Well, as they say, a line of code is
worth a thousand explanations, so let’s take a simple example.
Say we have a recipe object, which has the necessary information already stored
within it, including an id.
Then, we can save it by simply doing the following:
// Assuming existingRecipeObj has all the necessary fields,
// including id (say 13)
var recipe = new Recipe(existingRecipeObj);
recipe.$save();
This will make a POST request to /recipe/13.
The @id tells it to pick the id field from its object and use that as the id parameter.
restful ,$resource POST 请求实例:
//POST test
var addStu = $resource('/stu_restful/rest/student/updateStu:sName',
//带连字符 “_”的 参数 命名采用驼峰式
{s_name:'@std_name'}
);
addStu.save({std_name: 'zc'}, function(resp) {
console.log(resp);
//$scope.student= resp.student;
},function(err){
console.log(err);
});
传递多个参数:
var addStu = $resource('/stu_restful/rest/student/updateStu:sName:sLesson',
//带连字符 “_”的 参数 命名采用驼峰式
{s_name:'@std_name',s_lesson:'@std_lesson'}
);
addStu.save({std_name: 'zc',std_lesson:'Chinese'}, function(resp) {
console.log(resp);
//$scope.student= resp.student;
},function(err){
console.log(err);
});
对比:
1.
var User = $resource('/stu_restful/rest/student/student1/:Uid', {Uid:'@stu_id'}
);
User.get({stu_id: 2})
返回的Url:http://localhost:8080/stu_restful/rest/student/student1?stu_id=2
2.
var User=$resource('/stu_restful/rest/student/student1/:Uid',
{Uid :'@stu_id'}
);
User.get({Uid:2},
返回的Url:http://localhost:8080/stu_restful/rest/student/student1/2
· HTTP GET "class" actions: Resource.action([parameters], [success], [error])
· non-GET "class" actions: Resource.action([parameters], postData, [success], [error])
· non-GET instance actions: instance.$action([parameters], [success], [error])
项目中出现的问题与解决:
1.List 转化 json 数组
public String getStudents() {
stuList=studentService.getStudentList("student");
JSONArray json=JSONArray.fromObject(stuList);
return json.toString();
}
2.实现数据的编辑并保存,id唯一情况下 用delete+post方法编辑并保存数据,替代不能使用的put方法(时好时坏。。。)
app.controller('EditCtrl', ['$scope', '$location', 'recipe',
function($scope, $location, recipe) {
$scope.recipe = recipe;
//console.log(recipe);
var Id =recipe.id;//保存id号
$scope.save = function() {
recipe.$remove(function(res) {//删除原数据
});
//console.log(recipe);
$scope.recipe.$save(function(res) {//保存新数据
$location.path('/view/' + Id);
});
};
}]);
3.增加新数据,并使id号自增
app.controller('NewCtrl',['$scope','$location','Recipe','recipes',function($scope, $location, Recipe,recipes) {
//console.log(recipes.length);
var id=recipes.length;//数据库中存在数据条数即为
$scope.recipe = new Recipe({
ingredients: [ {} ]
});
$scope.recipe.id=id;
$scope.save = function() {
$scope.recipe.$save(function(recipe) {
$location.path('/view/' + id);
});
};
}]);
GetRecipe 项目分析
var services = angular.module('guthub.services', ['ngResource']);
services.factory('Recipe', ['$resource',function($resource) {
return $resource('/stu_restful/rest/Recipe/recipes/:id', {id: '@id'});
}]);
services.factory('MultiRecipeLoader', ['Recipe', '$q',function(Recipe, $q) {
return function() {
var delay = $q.defer();
Recipe.query(function(recipes) {
delay.resolve(recipes);
}, function() {
delay.reject('Unable to fetch recipes');
});
return delay.promise;
};
}]);
With just that single line of code—return $resource—(and of course, a dependency
on the guthub.services module), we can now put Recipe as an argument in any of
our controllers, and it will be injected into the controller.
The Templates(视图界面)
There is no ng-controller defined,and there really was no Main Controller defined. This is where route mapping comes into play.
The / route redirected to the list template and had the List Controller associated with it. Thus, when any referencesare made to variables and the like, it is within the scope of the List Controller.
ng-submit
The final thing to note is the ng-submit directive on the form. The directive states that the edit() function on the scope is called in case the form is submitted. The form submission happens when any button without an explicit function attached (in this case, the Edit button) is clicked
provider, factory和service
provider, factory和service都是写Angularjs的service中常用的关键字,很容易混淆,写了一个简单的例子显示他们之间的区别:
分别用service,factory和provider定义三个service:
var wtcModule = angular.module('wtc', []);
wtcModule.service('testService',function(){
this.lable = 'this is service';
});
wtcModule.factory('testFactory', function () {
return{
lable: function(){return 'this is factory';}
}
});
wtcModule.provider('testProvider', function(){
this.$get = function(){
return 'this is provider';
}
});
在页面上留出三个占位符:
{{ output1 }}
{{ output2 }}
{{ output3 }}
写好outputCtrl:
var wtcModule = angular.module('wtc');
wtcModule.controller('outputCtrl', function($scope,testService, testFactory, testProvider){
$scope.output1 = testService.lable;
$scope.output2 = testFactory.lable();
$scope.output3 = testProvider;
});
最后页面的显示结果为;
说明:
注入service,相当于注入service定义时的function实例。
Services are singleton objects that provide any functionality
注入factory,相当于注入factory定义时的函数调用入口。
factory function returns a service object
注入provider,相当于注入provider内$get定义的函数实例的调用
angular.module( 'customService' , [])
.provider('logService',function(){
return {
messageCountenable:function(setting){}
$get: function(){
return {
messageCount:0,
log: function(msg){
console.log("(LOG + " + this.messageCount++ + ") " + msg);
}
};
}
}
});
Provider:
The factory function is required to return a provider object that defines a method called $get, which in turn is required to return the service object.
AngularJS makes the provider object available for dependency injection, using the name of the service combined with the word Provider, so for the example the provider object can be obtained by declaring a dependency on logServiceProvider.
angular.module("exampleApp", ["customDirectives", "customServices"])
.config(function(logServiceProvider){
logServiceProvider.
})
Mobile
Beginning with Angular version 1.2.2, we now have the ability to use touch events using the new ngTouch module
Either way, we need to reference the library in our index.html as a script:
Finally, we need to include ngTouch as a dependency in our app:
angular.module('myApp', ['ngTouch']);
ngSwipe
One of the nice features the ngSwipe* directives give us is that they work both with touch-based devices as well as with mouse clicking and dragging.
Example:
ng-repeat="mail in emails">
ng-show="!mail.showActions"
ng-swipe-left="mail.showActions=true">
class="from">
From: {{ mail.from }}
class="body">
{{ mail.body }}
ng-show="mail.showActions"
ng-swipe-right="mail.showActions=false">
class="actions">
Archive
Trash
MVC pattern:
Common Design Pitfalls(陷阱):
1.Putting the Logic in the Wrong Place
• Putting business logic in views, rather than in controllers
• Putting domain logic in controllers, rather than in model
• Putting data store logic in the client model when using a RESTful service
here are the three rules:
• View logic should prepare data only for display and never modify the model.
• Controller logic should never directly create, update, or delete data from the model.
• The client should never directly access the data store
对比
angular.module("sportsStore", [ ]) 和 angular.module("sportsStore")
第二个: Omitting the second argument tells AngularJS that you want to locate a module that has already been defined.
In this situation, AngularJS will report an error if the module specified doesn’t exist, so you need to make sure the module has already been created.
The Fluent API
The result of the methods defined by the Module object is the Module object itself.
(I call the angular.module method and get a Module object as the result)
This is an odd-sounding but neat trick that allows for a fluent API, where multiple calls to methods are chained together
angular.module("exampleApp", [])
.controller(...)
.config(...)
Filters
Filters are themselves functions, which receive a data value and format it so it can be displayed.
myApp.filter('dayName',function(){
var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"];
return function(input){
return angular.isNumber(input)?dayNames[input]:input;
};
});
The data binding or expression is followed by a bar (the | character) and then the name of the filter, as follows:
Today is {{day | dayName}}
I have added a $filter argument to my directive factory function, which tells AngularJS that I want to receive the filter service object when my function is called. The $filter service gives me access to all of the filters that have been defined.
the Module Life Cycle
var myApp = angular.module("exampleApp",["exampleApp.Controllers", "exampleApp.Filters",
"exampleApp.Services", "exampleApp.Directives"]);
myApp.constant("startTime", new Date().toLocaleTimeString());
myApp.config(function (startTime) {
console.log("Main module config: " + startTime);
});
myApp.run(function (startTime) {
console.log("Main module run: " + startTime);
});
............
angular.module("exampleApp.Services", [])
.service("days", function (nowValue) {
.........
})
.config(function() {
console.log("Services module config: (no time)");
})
.run(function (startTime) {
console.log("Services module run: " + startTime);
});
Services module config: (no time)
Main module config: 16:57:28
Services module run: 16:57:28
Main module run: 16:57:28
The config method accepts a function that is invoked after the module on which the method is called is loaded. The config method is used to configure a module, usually by injecting values that have been obtained from the server, such as connection details or user credentials.
The run method also accepts a function, but it will be invoked only when all of the modules have been loaded
You can see this in the way that the callbacks for the exampleApp.Services module are made
before those for the main exampleApp module. This allows modules to configure themselves before they are used to resolve module dependencies.
I am able to use the startTime constant in three of the four callbacks, but I can’t use in the config callback for the exampleApp.Services module because the module dependencies have yet to be resolved. At the moment the config callback is invoked, the startTime constant is unavailable.
ng-repeat
1.
{{item.action}}
{{item.complete}}
2
{{prop}}
3
{{ key }}={{ value }}
don’t try to use apply the ng-include directive as a void element (in other words, src="'table.html'" />)
Form