Learn how to build a front-end web application with minimal effort in seven steps, using the AngularJS framework and the cloud storage service Parse.com.

Introduction

AngularJS has been marketed as a "Superheroic JavaScript MVW Framework". Whatever that is supposed to mean, the hype-marketing was quite successful, and it seems to be widely used today, despite the fact that:

  • the intended meaning of its main technical concepts (such as "controllers", "directives", "services" and "factories") is not very clear, and AngularJS is therefore said to have a steep learning curve;
  • the half-value time for many AngularJS concepts is pretty short since they will be dropped or re-defined in the next version, AngularJS 2, so your investment of time for learning AngularJS 1.x is very much at risk;
  • the claim that AngularJS is a "Model-View-Whatever" (MVW) framework is not warranted, since AngularJS doesn't have a real "model" concept (based on model classes), but only a "view model" concept, so it would be more correct to call it a "View-Model-Whatever" (VMW) framework.

In this tutorial, which has been extracted from the open access book Building Front-End Web Apps with AngularJS and Parse.com, we show how to build a minimal web application with the AngularJS framework and the cloud storage service Parse.com. Such an app is a front-end app, because most parts of the app's code are executed on the front-end, while only the persistent storage functionality is executed on the back-end server(s) in the cloud infrastructure managed by Parse.com. If you want to see how it works, you can run the minimal app discussed in this article from our server.

The minimal version of an AngularJS data management app discussed in this tutorial only includes a minimum of the overall functionality required for a complete app. It takes care of only one object type (Book) and supports the four standard data management operations (Create/Read/Update/Delete), but it needs to be enhanced by adding further important parts of the app's overall functionality:

  • Part 2: Taking care of constraint validation

  • Part 3: Managing unidirectional associations assigning authors and publishers to books

  • Part 4: Managing bidirectional associations also assigning books to authors and to publishers

  • Part 5: Handling subtype (inheritance) relationships in a class hierarchy

Background

We assume that the reader is already familiar with HTML and has some programming experience. For a short introduction to, or refresher of, JavaScript, please check the CodeProject article JavaScript Summary (about which a reader said that "In over 20 years programming, this is the single best overview of any language ever!").

Coding the App

The purpose of our example app is to manage information about books. That is, we deal with a single object type: Book, as depicted in the following figure:

What do we need for such an information management application? There are four standard use cases, which have to be supported by the application:

  1. Create: Enter the data of a book that is to be added to the collection of managed books.
  2. Read: Show a list of all books in the collection of managed books.
  3. Update the data of a book.
  4. Delete a book record.

These four standard use cases, and the corresponding data management operations, are often summarized with the acronym CRUD.

For entering data with the help of the keyboard and the screen of our computer, we can use HTML forms, which provide the user interface technology for web applications.

For maintaining a collection of persistent data objects, we need a storage technology that allows to keep data objects in persistent records on a secondary storage device, either locally or remotely. An attractive option for remote storage is using a cloud storage service, where data is stored in logical data pools, which may physically span multiple servers in a cloud infrastructure owned and managed by a cloud service company. A cloud storage service is typically reachable via a web API, which is often called a "REST API". For our minimal AngularJS app, we will use the cloud service platform Parse.com via its REST API.

Step 1 - Set Up the Folder Structure

In the first step, we set up our folder structure for the application. We pick a name for our app, such as "Public Library", and a corresponding (possibly abbreviated) name for the application folder, such as "publicLibrary". Then we create the following folder structure:

Hide   Copy Code
publicLibrary
|-- index.html
|-- js
| |-- app.js
| `-- controllers.js
|-- partials
| |-- createBook.html
| |-- deleteBook.html
| |-- main.html
| |-- showAllBooks.html
| `-- updateBook.html
`-- src
|-- angular-route.js
`-- angular.js

In addition to the index.html file for starting the app, we have three sub-folders:

  1. The js folder is used for all app-specific JavaScript files.

    • app.js has the task to define a name for our AngularJS app, to declare all dependences, also to organize the relationship between views, their corresponding controllers and even URLs.

    • controllers.js is used for implementing various functions.

  2. The partials folder holds all HTML-files for different sub-views, which will be replaced by AngularJS mutually exclusively into the body of index.html.

  3. The src folder holds all official libraries like angular.js and angular-route.js.

1.1. The Start Page

The entrance point of our web app, index.html, is used for including all files and creating an ng-view container element for rendering different views:

Hide   Copy Code
<!DOCTYPE html>
<html ng-app="publicLibraryApp">
<head>
<meta charset="UTF-8" />
<title>Public Library with AngularJS</title>
<script src="src/angular.js"></script>
<script src="src/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
<body>
<div ng-view="ng-view"></div>
</body>
</html>

The AngularJS attribute ng-app of the html element declares the root element of an AngularJS app, here with the name "publicLibraryApp", while the attribute ng-view (typically in a div) defines a container element for rendering view templates in the start page index.html. This approach allows sharing general static content, such as header and footer, among all views. View templates will be rendered into the ng-view div element according to a configuration file app.js.

1.2. Modules and Routes

The app.js file tells Angular the name of our app, defines dependencies, and manages the routes, controllers and URLs:

Hide   Shrink    Copy Code
var publicLibraryApp = angular.module('publicLibraryApp',
['ngRoute', 'plControllers']);
publicLibraryApp.config(['$routeProvider', function ( $routeProvider) {
$routeProvider
.when('/main', {
templateUrl: 'partials/main.html',
controller: 'TestDatasController'
})
.when('/showAllBooks', {
templateUrl: 'partials/showAllBooks.html',
controller: 'BooksController'
})
.when('/createBook', {
templateUrl: 'partials/createBook.html',
controller: 'BooksController'
})
.when('/updateBook', {
templateUrl: 'partials/updateBook.html',
controller: 'BooksController'
})
.when('/deleteBook', {
templateUrl: 'partials/deleteBook.html',
controller: 'BooksController'
})
.otherwise({
redirectTo: '/main'
});
}]); publicLibraryApp.run(['$http', function ( $http) {
// Predefine the API's value from Parse.com
$http.defaults.headers.common = {
'X-Parse-Application-Id': 'Your_Application_ID',
'X-Parse-REST-API-Key': 'Your_REST_API_Key'
}
}]);

We define our app with the name "publicLibraryApp" and dependencies on the module plControllers (to be defined by us) and on the pre-defined AngularJS module ngRoute, which supports URL management providing routing, services and directives for AngularJS apps.

The module plControllers, which will collect all controllers of our app, is defined in the file js/controllers.js:

Hide   Copy Code
var plControllers = angular.module('plControllers', []);

After declaring dependencies, we configure our main module publicLibraryApp withpublicLibraryApp.config for defining "routes", which are mappings from request URL paths to template/controller combinations, and with publicLibraryApp.run for providing application start-up information such as authentication credentials.

In conjunction with the ngView directive, application routes in AngularJS are declared via the $routeProvider, which is provided in module ngRoute to define a Route-ID for each view and corresponding controller.

For example, the third option of $routeProvider.when('/createBook', ...) means that when the request URL path is "/createBook", the template partials/createBook.html will be rendered inside the ng-viewdiv element and the app will be controlled by the user-defined controller BooksController.

1.3 The Start Page View Template main.html

When index.html is requested and the request URL path is neither of "/main", "/showAllBooks", "/createBook", "/updateBook" or "/deleteBook", then the $routeProvider calls otherwise(), which takes care that "/main", in combination with the TestDataController, is used as the default route.

Hide   Copy Code
<menu>
<li><a href="#/showAllBooks">
<button type="button">List all Books</button>
</a></li>
<li><a href="#/createBook">
<button type="button">Add a new Book</button>
</a></li>
<li><a href="#/updateBook">
<button type="button">Update a Book</button>
</a></li>
<li><a href="#/deleteBook">
<button type="button">Delete a Book</button>
</a></li>
<li><button type="button" ng-click="clearDatabase()">
Clear database
</button></li>
<li><button type="button" ng-click="createTestData()">
Create test data
</button></li>
</menu>

In this view template, we provide a menu for choosing one of the CRUD data management operations via a corresponding page (such as createBook.html), or for creating test data with the help of the procedurecreateTestData(), or for clearing all data with clearDatabase(). The AngularJS event-handler attribute ng-click, defined for the button elements, allows specifying a controller method invocation to be executed when a corresponding click event has occurred.

1.4 View Models in AngularJS

In an AngularJS app, the view model, which is simply called 'model', is the basis for writing the view and controller code. In the information design model shown in Figure 2.1 above, there is only one class, representing the (business) object type Book. In AngularJS, this class will be turned into a JS object as the value of the $scopeproperty book, representing a view model, like so:

Hide   Copy Code
$scope.book = {
"isbn": "",
"title": "",
"year": 0
};

The $scope object extends the app's $rootScope object (in the sense of prototype-based inheritance). AngularJS uses the $scope object, together with the information contained in the template and controller, to keep models and views separate, but in sync. Any changes made to the view model are propagated to the view, and any changes that occur in the view are propagated to the view model (this mechanism is called two-way data binding).

Step 2 - Using the Cloud Storage Service Parse.com

The cloud storage service of Parse.com can be used via the REST API of Parse.com, which allows any app that can send HTTP request messages to access the persistent data store on Parse.com. All API access is over HTTPS via theapi.parse.com domain. The URL path prefix /1/ indicates that we are currently using version 1 of the API. Using the Parse cloud storage service REST API for our web application, we need five functionalities to handle theBook Object, as shown in Table 2.1.

Table 2.1. The REST API of Parse.com's cloud storage service
Request URL path HTTP method Meaning
/1/classes/<className> POST Creating an object
/1/classes/<className>/<objectId> GET Retrieving an object
/1/classes/<className>/<objectId> PUT Updating an object
/1/classes/<className>/<objectId> DELETE Deleting an object
/1/classes/<className> GET Retrieving all objects (and other queries)

Notice that an objectId is automatically created by the Parse storage service when a new object is saved. In fact, Parse does not only create an objectId, but also the two time stamp values createdAt and updatedAt of typeDate.

For being able to access the Parse cloud storage service, we need to set up a user account on the Parse.comwebsite Then go to "Dashboard" and "Create a new App". On the app's "Settings" page, you can find its "Application ID" and "REST API Key" using the tab "Keys". Authentication is done via HTTP headers, so we need to provide these keys as the values of corresponding HTTP header fields, like so:

Hide   Copy Code
headers: {
'X-Parse-Application-Id': 'Your_Application_ID',
'X-Parse-REST-API-Key': 'Your_REST_API_Key'
}

The header field X-Parse-Application-Id identifies which application you are accessing, and X-Parse-REST-API-Key authenticates the endpoint. When we send GETPOSTPUT or DELETE request messages to Parse.com, these two headers have to be included.

The format of a request message body for POST and PUT, as well as the format of all response message bodies, is JSON. In general, sending data to a (remote) back-end server from a JavaScript front-end program is done with asynchronous remote procedure calls using JavaScript's XML HTTP Request (XHR) API. Since such a remote procedure call can result either in a reply message confirming the successful completion of the call or indicating an error state, we generally need to specify two methods as the arguments for calling the remote procedure asynchronously: the first method success() is invoked when the remote procedure call has been successfully completed, and the second one error() when it failed.

AngularJS provides the $http 'service' for sending HTTP request messages (and receiving their response messages) via JavaScript's XHR API. The $http method takes a single argument that is used to generate HTTP request messages and returns a promise with two $http specific methods: success() and error(). The general pattern for a $http send/receive operation is the following:

Hide   Copy Code
$http({
headers: {...},
method: 'GET|POST|...',
url: 'aRequestUrl'
})
.success( function () {...})
.error( function () {...});

We can add the required Parse authentication headers as default headers in publicLibraryApp.run (injs/app.js), so we don't need to add them to each request. Notice that the $http service has different default headers for different request types: Content-Type: application/json for POST and PUT, and Accept: application/json, text/plain, */* for all request types.

3. Step 3 - Implement the Retrieve/List All Use Case

This use case corresponds to the "Retrieve/Read" operation from the four basic CRUD data management operations.

The user interface for this use case is provided by the view template partials/listAllBooks.html containing a table for displaying information about books with the request URL path #/showAllBooks:

Hide   Copy Code
<h1>Public Library: List all books</h1>
<table ng-init="getAllBooks()">
<thead><tr>
<th>ISBN</th><th>Title</th><th>Year</th>
</tr></thead>
<tbody><tr ng-repeat="book in books">
<td>{{book.isbn}}</td>
<td>{{book.title}}</td>
<td>{{book.year}}</td>
</tr></tbody>
</table>

This view template, as well as any other one, is rendered within the ng-view container element defined inindex.html. By setting the ng-init event handler attribute to getAllBooks(), we make AngularJS invoking the method getAllBooks() defined in BooksController before the table element is rendered, which retrieves all book records from the Parse cloud data store and makes them available in the array $scope.books.

Then, due to the table row rendering loop specified by the ng-repeat attribute in the table/tbody/tr element, the table body is populated with book data rows showing the ISBN, title and year of each book, as specified by the expressions {{book.isbn}}{{book.title}} and {{book.year}}.

Let's take a look at the BooksController and its method getAllBooks():

Hide   Copy Code
plControllers.controller('BooksController',
['$scope', '$http', function ( $scope, $http){
...
$scope.getAllBooks = function () {
$http({
method: 'GET',
url: 'https://api.parse.com/1/classes/Book'
})
.success( function( data){
$scope.books = data.results;
})
.error( function( data) {
alert("ERROR when getting all books", data);
});
};
...
}]);

When the GET request succeeds, all objects retrieved from the Parse class Book are stored in data.results and then copied to $scope.books. When the request fails, an error message is stored in data and then shown in an alert box.

Step 4 - Implement the Create Use Case

This use case corresponds to the "Create" from the four basic data management use cases Create-Retrieve-Update-Delete (CRUD).

For a data management operation with user input, such as the "create object" operation, an HTML page with an HTML form is required as a user interface. The form typically has an input or select field for each attribute of the model class. In the case of an AngularJS app, we don't have model classes and the UI is defined by a view template partials/createBook.html (an HTML fragment with embedded AngularJS expressions), which has a field for each attribute of the view model object $scope.book. The createBook view template invokes theBooksController method addBook(). In our example app, the request URL path of this page is#/createBook, which renders the following template:

Hide   Copy Code
<h1>Public Library: Create a new book record</h1>
<form>
<div>
<label>ISBN:
<input type="text" name="isbn" ng-model="book.isbn" />
</label>
</div>
<div>
<label>Title:
<input type="text" name="title" ng-model="book.title" />
</label>
</div>
<div>
<label>Year:
<input type="number" name="year" ng-model="book.year" />
</label>
</div>
<div>
<button type="submit" name="commit" ng-click="addBook()">
Save
</button>
</div>
</form>

Notice how the AngularJS attribute ng-model is used for binding form fields to attributes of the view model$scope.book, resulting in a two-way data binding. Once the submit button is clicked, the function addBook()will be executed. Let's take a look at the BooksController and its function addBook():

Hide   Copy Code
plControllers.controller('BooksController',
['$scope', '$http', function ( $scope, $http){
...
$scope.addBook = function () {
$http({
method: 'POST',
url: 'https://api.parse.com/1/classes/Book',
data: {
isbn: $scope.book.isbn,
title: $scope.book.title,
year: $scope.book.year
}
})
.success( function(){
alert("SUCCESS: Create book");
$scope.book = {};
})
.error( function( data){
alert("ERROR: Create book", data);
});
};
...
}]);

Notice that the $http method is invoked with a data parameter holding the attribute-value slots of the object to be saved. When a user enters data in the HTML form fields of the "create book" user interface, the data binding mechanism of AngularJS copies the data to the view model object $scope.book, so when the submit button is clicked, this data is sent to the Parse data store in the form of a HTTP POST request message using Angular's$http method. The Parse cloud storage service receives the message and saves the book data as an instance of the Parse storage class Book.

Step 5 - Implement the Update Use Case

This use case corresponds to the update operation from the four basic CRUD data management operations. The request URL path of this operation is #/updateBook.

Like for the Create use case, we have a view template for the user interface, partials/updateBook.html, which invokes a BooksController method, updateBook(). The updateBook view template has a select field for choosing the book to be updated, an output field for the standard identifier attribute isbn, and an input field for each attribute of the $scope.book view model that can be updated. Notice that by using an output field for the standard identifier attribute, we do not allow changing the standard identifier of an existing object.

Hide   Shrink    Copy Code
<h1>Public Library: Update a book record</h1>
<form ng-init="getAllBooks()">
<div ng-hide="book">
<label>Select book:
<select ng-model="book" ng-options="book.title for book in books">
<option value="">---</option>
</select>
</label>
</div>
<div ng-show="book">
<div>
<label>ISBN:
<output type="text" name="isbn" ng-bind="book.isbn" />
</label>
</div>
<div>
<label>Title:
<input type="text" name="title" ng-model="book.title" />
</label>
</div>
<div>
<label>Year:
<input type="number" name="year" ng-model="book.year" />
</label>
</div>
<div>
<button type="submit" name="commit" ng-click="updateBook()">
Save Changes
</button>
</div>
</div>
</form>

In the select element, the AngularJS attribute ng-model is used for establishing two-way data binding with$scope.book, while ng-options is used for retrieving the books list from $scope and populating the selectelement with option elements according to the expression book.title for book in books, which defines book titles as the content of the selection list options. As an additional option element, we set the text --- with an empty value for indicating that nothing has been selected.

Notice that the view template has a div section with an ng-hide attribute and another one with an ng-showattribute, both set to book. Their effect is that the first div section will be hidden, and the second will be shown, as soon as the view model object book has been assigned by the user selecting a book.

Notice also, that we can specify an ordinary, instead of a two-way, data binding with ng-bind, for only showing the value of a view model attribute, We are doing this for the output field showing the ISBN of the selected book.

Let's take a look at the BooksController method updateBook():

Hide   Copy Code
plControllers.controller('BooksController',
['$scope', '$http', function ( $scope, $http){
...
$scope.updateBook = function () {
var bookUrl = 'https://api.parse.com/1/classes/Book/';
$http({
method: 'PUT',
url: bookUrl + $scope.book.objectId,
data: {
isbn: $scope.book.isbn,
title: $scope.book.title,
year: $scope.book.year
}
})
.success( function () {
alert("SUCCESS: Update book");
$scope.book = {};
})
.error( function ( data) {
alert("ERROR: Update book", data);
});
};
...
}]);

Notice how we set the url parameter such that it ends with the book's Parse objectId.

Step 6 - Implement the Delete Use Case

This use case corresponds to the delete/destroy operation from the four basic CRUD data management operations. The request URL path of this operation is #/deleteBook.

Like for the Update use case, we have a view template for the user interface, partials/deleteBook.html, which invokes a corresponding BooksController method, destroyBook(). The deleteBook view template has aselect field for choosing the book to be deleted and an output field for each attribute of the $scope.bookview model that is to be shown. Notice that by using output fields, we do not allow changing any value.

Hide   Copy Code
<h1>Public Library: Delete a book record</h1>
<form ng-init="getAllBooks()">
<div ng-hide="book">
<label>Select book:
<select ng-model="book" ng-options="book.title for book in books">
<option value="">---</option>
</select>
</label>
</div>
<div ng-show="book">
<div>
<label>ISBN:
<output type="text" name="isbn" ng-bind="book.isbn" />
</label>
</div>
<div>
<label>Title:
<output type="text" name="title" ng-bind="book.title" />
</label>
</div>
<div>
<button type="submit" name="commit" ng-click="destroyBook()">
Delete
</button>
</div>
</div>
</form>

After the user selects a book, the data binding mechanism of AngularJS, specified with ng-bind, takes care of showing the book's ISBN and title in the corresponding output fields.

Let's take a look at the BooksController method destroyBook():

Hide   Copy Code
plControllers.controller('BooksController',
['$scope', '$http', function ( $scope, $http){
...
$scope.destroyBook = function () {
var bookUrl = 'https://api.parse.com/1/classes/Book/';
$http({
method: 'DELETE',
url: bookUrl + $scope.book.objectId
})
.success( function () {
alert("SUCCESS: Delete book");
$scope.book = {};
})
.error( function ( data) {
alert("ERROR: Delete book", data);
});
};
...
}]);

Step 7 - Implement createTestData() and clearDatabase()

In the index.html page, we have buttons for invoking two special methods:

  • createTestData() will insert more books into Parse cloud storage in one time
  • clearDatabase() will clear all the books from Parse cloud storage in one time

Parse provides batch operations that allow creating, updating or deleting up to 50 objects with one request message. We'd like to define a controller TestDataController specifically for creating test data and for clearing the database.

Hide   Copy Code
plControllers.controller('TestDataController',
['$scope', '$http', function ( $scope, $http) {
...
}]);

7.1. Adding Several Objects in One Step

Notice that the data parameter is set to a requests array of Parse requests:

Hide   Shrink    Copy Code
...
$scope.createTestData = function () {
$http({
method: 'POST',
url: 'https://api.parse.com/1/batch',
data: {
requests: [
{
method: 'POST',
path: '/1/classes/Book',
body: {
isbn: "006251587X",
title: "Weaving the Web",
year: 2000
}
},
{
method: 'POST',
path: '/1/classes/Book',
body: {
isbn: "0465026567",
title: "Goedel, Escher, Bach",
year: 1999
}
},
{
method: 'POST',
path: '/1/classes/Book',
body: {
isbn: "0465030793",
title: "I Am A Strange Loop",
year: 2008
}
}
]
}
})
.success( function () {
alert("SUCCESS: Create test data");
})
.error ( function ( data) {
alert("ERROR: Create test data", data);
});
};
...

7.2. Delete Several Objects in One Step

Since the Parse REST API does not provide an option for bulk delete operations, for clearing our database we first retrieve all book records, and then delete them one by one::

Hide   Copy Code
...
$scope.clearDatabase = function () {
$http( {
method: 'GET',
url: 'https://api.parse.com/1/classes/Book'
})
.success( function ( data) {
var books = data.results;
var bookUrl = 'https://api.parse.com/1/classes/Book/';
books.forEach( function ( book) {
$http({
method: 'DELETE',
url: bookUrl + book.objectId
})
.error( function ( data) {
alert("ERROR: Clear database", data);
});
});
})
.error( function ( data) {
alert("ERROR: Get all books", data);
});
};
...

Run the App and Get the Code

You can run the minimal AngularJS/Parse app from our server or download the code as a ZIP archive file.

Points of Attention

The code of this app should be extended by adding constraint validation. You can learn how to do this by getting the book or by reading the online chapter Constraint Validation in an AngularJS Front-End Web App.

History

  • 11th September 2015: Corrected typo in code fragment
  • 9th September 2015: First version created
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

AngularJS Front-End App with Cloud Storage Tutorial Part 1: Building a Minimal App in Seven Steps的更多相关文章

  1. Java Client for Google Cloud Storage

    关于Google Cloud Storage Google Cloud Storage有益于大文件的存储与服务(serve).此外,Cloud Storage提供了对访问控制列表(ACLs)的使用,提 ...

  2. Getting Started(Google Cloud Storage Client Library)

    在运行下面的步骤之前,请确保: 1.你的项目已经激活了Google Cloud Storage和App Engine,包括已经创建了至少一个Cloud Storage bucket. 2.你已经下载了 ...

  3. Activating Google Cloud Storage

    先决条件 你需要下面的内容: 1.一个Google账户,比如来自Gmail.如果你没有,请在Google account signup site注册. 2.一个新的或已经存在的Google Devel ...

  4. Downloading the Google Cloud Storage Client Library

    Google Cloud Storage client是一个客户端库,与任何一个生产环境使用的App Engine版本都相互独立.如果你想使用App Engine Development server ...

  5. google cloud storage products

    https://cloud.google.com/products/storage/ BigTable Cloud Bigtable 是 Google 面向大数据领域的 NoSQL 数据库服务.它也是 ...

  6. android 7.0拍照问题file:///storage/emulated/0/photo.jpeg exposed beyond app through ClipData.Item.getUri

    Android7.0调用相机时出现新的错误: android.os.FileUriExposedException: file:///storage/emulated/0/photo.jpeg exp ...

  7. Azure Front Door(一)为基于.net core 开发的Azure App Service 提供流量转发

    一,引言 之前我们讲解到使用 Azure Traffic Manager.Azure LoadBalancer.Azure Application Gateway,作为项目的负载均衡器来分发流量,转发 ...

  8. 背水一战 Windows 10 (101) - 应用间通信: 通过协议打开指定的 app 并传递数据以及获取返回数据, 将本 app 沙盒内的文件共享给其他 app 使用

    [源码下载] 背水一战 Windows 10 (101) - 应用间通信: 通过协议打开指定的 app 并传递数据以及获取返回数据, 将本 app 沙盒内的文件共享给其他 app 使用 作者:weba ...

  9. Android官方文档翻译 二 1.Building Your First App

    Building Your First App 创建你的第一个App项目 Dependencies and prerequisites 依赖关系和先决条件 * Android SDK * ADT Pl ...

随机推荐

  1. MFC滚动条实现要点

    MFC滚动条实现要点 1.鼠标拖动滚动条从而滚动窗口,需要实现CDialog::OnVScroll()函数.根据传入参数nPos,计算滚动距离.最后再调用ScrollWindow()和SetScrol ...

  2. selenium高级用法

    http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp# WebDriver: Advanced Usage Explicit and Im ...

  3. hdu_2665_Kth number(主席树)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2665 题意:给你一个区间,让你找这个区间第K大的数 题解:主席树模版题,也可以用划分树 #includ ...

  4. LeetCode OJ 118. Pascal's Triangle

    Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Retur ...

  5. mongodb常见问题

    1.count统计结果错误 这是由于分布式集群正在迁移数据,它导致count结果值错误,需要使用aggregate pipeline来得到正确统计结果,例如: db.collection.aggreg ...

  6. icon大小

    ldpi mdpi hdpi xhdpi xxhdpi

  7. asp.net如何删除文件夹及文件内容操作

    static void DeleteDirectory(string dir) { && Directory.GetFiles(dir).Length == ) { Directory ...

  8. sql-删除delete涉及到三个表,这个时候就要使用from,比如这样

    delete y from dbo.XZXK_BANJIE b ,YJDC_YELLOWRED_CONTENT y , dbo.XZXK_SHOULI s where b.shoulioid=s.sh ...

  9. EL表达式处理字符串 是否 包含 某字符串 截取 拆分...............

    EL表达式处理字符串 是否 包含 某字符串 截取 拆分............... JSP页面页头添加<%@ taglib uri="/WEB-INF/taglib/c.tld&qu ...

  10. java解析xml文件并输出

    使用java解析xml文件,通过dom4j,代码运行前需先导入dom4j架包. ParseXml类代码如下: import java.io.File; import java.util.ArrayLi ...