From 52950cfdef2102e0b9ddaa54a12de405f505f019 Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 17:42:03 +0800 Subject: [PATCH 01/12] Update README.md --- README.md | 298 +----------------------------------------------------- 1 file changed, 3 insertions(+), 295 deletions(-) diff --git a/README.md b/README.md index e7aece1083..53227c262f 100644 --- a/README.md +++ b/README.md @@ -1,297 +1,5 @@ -# angular-seed — the seed for AngularJS apps +# Angular JS Tutorial -This project is an application skeleton for a typical [AngularJS](http://angularjs.org/) web app. -You can use it to quickly bootstrap your angular webapp projects and dev environment for these -projects. +Getting Started with AngularJS -The seed contains a sample AngularJS application and is preconfigured to install the Angular -framework and a bunch of development and testing tools for instant web development gratification. - -The seed app doesn't do much, just shows how to wire two controllers and views together. - - -## Getting Started - -To get you started you can simply clone the angular-seed repository and install the dependencies: - -### Prerequisites - -You need git to clone the angular-seed repository. You can get git from -[http://git-scm.com/](http://git-scm.com/). - -We also use a number of node.js tools to initialize and test angular-seed. You must have node.js and -its package manager (npm) installed. You can get them from [http://nodejs.org/](http://nodejs.org/). - -### Clone angular-seed - -Clone the angular-seed repository using [git][git]: - -``` -git clone https://github.com/angular/angular-seed.git -cd angular-seed -``` - -If you just want to start a new project without the angular-seed commit history then you can do: - -```bash -git clone --depth=1 https://github.com/angular/angular-seed.git -``` - -The `depth=1` tells git to only pull down one commit worth of historical data. - -### Install Dependencies - -We have two kinds of dependencies in this project: tools and angular framework code. The tools help -us manage and test the application. - -* We get the tools we depend upon via `npm`, the [node package manager][npm]. -* We get the angular code via `bower`, a [client-side code package manager][bower]. - -We have preconfigured `npm` to automatically run `bower` so we can simply do: - -``` -npm install -``` - -Behind the scenes this will also call `bower install`. You should find that you have two new -folders in your project. - -* `node_modules` - contains the npm packages for the tools we need -* `app/bower_components` - contains the angular framework files - -*Note that the `bower_components` folder would normally be installed in the root folder but -angular-seed changes this location through the `.bowerrc` file. Putting it in the app folder makes -it easier to serve the files by a webserver.* - -### Run the Application - -We have preconfigured the project with a simple development web server. The simplest way to start -this server is: - -``` -npm start -``` - -Now browse to the app at `http://localhost:8000/app/index.html`. - - - -## Directory Layout - -``` -app/ --> all of the source files for the application - app.css --> default stylesheet - components/ --> all app specific modules - version/ --> version related components - version.js --> version module declaration and basic "version" value service - version_test.js --> "version" value service tests - version-directive.js --> custom directive that returns the current app version - version-directive_test.js --> version directive tests - interpolate-filter.js --> custom interpolation filter - interpolate-filter_test.js --> interpolate filter tests - view1/ --> the view1 view template and logic - view1.html --> the partial template - view1.js --> the controller logic - view1_test.js --> tests of the controller - view2/ --> the view2 view template and logic - view2.html --> the partial template - view2.js --> the controller logic - view2_test.js --> tests of the controller - app.js --> main application module - index.html --> app layout file (the main html template file of the app) - index-async.html --> just like index.html, but loads js files asynchronously -karma.conf.js --> config file for running unit tests with Karma -e2e-tests/ --> end-to-end tests - protractor-conf.js --> Protractor config file - scenarios.js --> end-to-end scenarios to be run by Protractor -``` - -## Testing - -There are two kinds of tests in the angular-seed application: Unit tests and End to End tests. - -### Running Unit Tests - -The angular-seed app comes preconfigured with unit tests. These are written in -[Jasmine][jasmine], which we run with the [Karma Test Runner][karma]. We provide a Karma -configuration file to run them. - -* the configuration is found at `karma.conf.js` -* the unit tests are found next to the code they are testing and are named as `..._test.js`. - -The easiest way to run the unit tests is to use the supplied npm script: - -``` -npm test -``` - -This script will start the Karma test runner to execute the unit tests. Moreover, Karma will sit and -watch the source and test files for changes and then re-run the tests whenever any of them change. -This is the recommended strategy; if your unit tests are being run every time you save a file then -you receive instant feedback on any changes that break the expected code functionality. - -You can also ask Karma to do a single run of the tests and then exit. This is useful if you want to -check that a particular version of the code is operating as expected. The project contains a -predefined script to do this: - -``` -npm run test-single-run -``` - - -### End to end testing - -The angular-seed app comes with end-to-end tests, again written in [Jasmine][jasmine]. These tests -are run with the [Protractor][protractor] End-to-End test runner. It uses native events and has -special features for Angular applications. - -* the configuration is found at `e2e-tests/protractor-conf.js` -* the end-to-end tests are found in `e2e-tests/scenarios.js` - -Protractor simulates interaction with our web app and verifies that the application responds -correctly. Therefore, our web server needs to be serving up the application, so that Protractor -can interact with it. - -``` -npm start -``` - -In addition, since Protractor is built upon WebDriver we need to install this. The angular-seed -project comes with a predefined script to do this: - -``` -npm run update-webdriver -``` - -This will download and install the latest version of the stand-alone WebDriver tool. - -Once you have ensured that the development web server hosting our application is up and running -and WebDriver is updated, you can run the end-to-end tests using the supplied npm script: - -``` -npm run protractor -``` - -This script will execute the end-to-end tests against the application being hosted on the -development server. - - -## Updating Angular - -Previously we recommended that you merge in changes to angular-seed into your own fork of the project. -Now that the angular framework library code and tools are acquired through package managers (npm and -bower) you can use these tools instead to update the dependencies. - -You can update the tool dependencies by running: - -``` -npm update -``` - -This will find the latest versions that match the version ranges specified in the `package.json` file. - -You can update the Angular dependencies by running: - -``` -bower update -``` - -This will find the latest versions that match the version ranges specified in the `bower.json` file. - - -## Loading Angular Asynchronously - -The angular-seed project supports loading the framework and application scripts asynchronously. The -special `index-async.html` is designed to support this style of loading. For it to work you must -inject a piece of Angular JavaScript into the HTML page. The project has a predefined script to help -do this. - -``` -npm run update-index-async -``` - -This will copy the contents of the `angular-loader.js` library file into the `index-async.html` page. -You can run this every time you update the version of Angular that you are using. - - -## Serving the Application Files - -While angular is client-side-only technology and it's possible to create angular webapps that -don't require a backend server at all, we recommend serving the project files using a local -webserver during development to avoid issues with security restrictions (sandbox) in browsers. The -sandbox implementation varies between browsers, but quite often prevents things like cookies, xhr, -etc to function properly when an html page is opened via `file://` scheme instead of `http://`. - - -### Running the App during Development - -The angular-seed project comes preconfigured with a local development webserver. It is a node.js -tool called [http-server][http-server]. You can start this webserver with `npm start` but you may choose to -install the tool globally: - -``` -sudo npm install -g http-server -``` - -Then you can start your own development web server to serve static files from a folder by -running: - -``` -http-server -a localhost -p 8000 -``` - -Alternatively, you can choose to configure your own webserver, such as apache or nginx. Just -configure your server to serve the files under the `app/` directory. - - -### Running the App in Production - -This really depends on how complex your app is and the overall infrastructure of your system, but -the general rule is that all you need in production are all the files under the `app/` directory. -Everything else should be omitted. - -Angular apps are really just a bunch of static html, css and js files that just need to be hosted -somewhere they can be accessed by browsers. - -If your Angular app is talking to the backend server via xhr or other means, you need to figure -out what is the best way to host the static files to comply with the same origin policy if -applicable. Usually this is done by hosting the files by the backend server or through -reverse-proxying the backend server(s) and webserver(s). - - -## Continuous Integration - -### Travis CI - -[Travis CI][travis] is a continuous integration service, which can monitor GitHub for new commits -to your repository and execute scripts such as building the app or running tests. The angular-seed -project contains a Travis configuration file, `.travis.yml`, which will cause Travis to run your -tests when you push to GitHub. - -You will need to enable the integration between Travis and GitHub. See the Travis website for more -instruction on how to do this. - -### CloudBees - -CloudBees have provided a CI/deployment setup: - - - - -If you run this, you will get a cloned version of this repo to start working on in a private git repo, -along with a CI service (in Jenkins) hosted that will run unit and end to end tests in both Firefox and Chrome. - - -## Contact - -For more information on AngularJS please check out http://angularjs.org/ - -[git]: http://git-scm.com/ -[bower]: http://bower.io -[npm]: https://www.npmjs.org/ -[node]: http://nodejs.org -[protractor]: https://github.com/angular/protractor -[jasmine]: http://jasmine.github.io -[karma]: http://karma-runner.github.io -[travis]: https://travis-ci.org/ -[http-server]: https://github.com/nodeapps/http-server +Trying out a sample AngularJS application with mock data based on [Getting Started with AngularJS](http://raibledesigns.com/rd/entry/getting_started_with_angularjs) From 83b9e3e1d5643e2f303a0374438a0b6e5462e746 Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 17:49:23 +0800 Subject: [PATCH 02/12] Create an index.html file under app/search --- app/search/index.html | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/search/index.html diff --git a/app/search/index.html b/app/search/index.html new file mode 100644 index 0000000000..a509f895d6 --- /dev/null +++ b/app/search/index.html @@ -0,0 +1,4 @@ +
+ + +
From 07455c4f48d6f81cc98825c3f8929e9c224d6f0d Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 18:25:19 +0800 Subject: [PATCH 03/12] Create search.js, put under search folder and define the routes(URLs) and controller for the search feature --- app/search/search.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/search/search.js diff --git a/app/search/search.js b/app/search/search.js new file mode 100644 index 0000000000..d19ea707e8 --- /dev/null +++ b/app/search/search.js @@ -0,0 +1,12 @@ +angular.module('myApp.search', ['ngRoute']) + .config(['$routeProvider', function($routeProvider) { + $routeProvider + .when('/search', { + templateUrl: 'search/index.html', + controller: 'SearchController' + }) + }]) + + .controller('SearchController', function() { + console.log("In Search Controller..."); + }); From 2a68caf72c0df0b427ac99a6ac7fb5a6a1382512 Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 18:26:39 +0800 Subject: [PATCH 04/12] Add 'myApp.search' module on app/app.js --- app/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/app.js b/app/app.js index 21eccdb8ea..d3896d7226 100644 --- a/app/app.js +++ b/app/app.js @@ -5,7 +5,8 @@ angular.module('myApp', [ 'ngRoute', 'myApp.view1', 'myApp.view2', - 'myApp.version' + 'myApp.version', + 'myApp.search' ]). config(['$routeProvider', function($routeProvider) { $routeProvider.otherwise({redirectTo: '/view1'}); From f322bf60c7c9e48bbe98e8c831f9d0ddbff9b77d Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 18:27:28 +0800 Subject: [PATCH 05/12] Link search.js in app/index.html --- app/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/app/index.html b/app/index.html index d0aacaaac4..e2ff2a6798 100644 --- a/app/index.html +++ b/app/index.html @@ -36,6 +36,7 @@ + From 9304c56fdc9ee2b4bc9b36fba2ff61ca18d06e0c Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 18:29:36 +0800 Subject: [PATCH 06/12] Add /search href in app/index.html --- app/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/app/index.html b/app/index.html index e2ff2a6798..d0636a2ab7 100644 --- a/app/index.html +++ b/app/index.html @@ -18,6 +18,7 @@ + + + diff --git a/app/search/index.html b/app/search/index.html index a509f895d6..733bae9951 100644 --- a/app/search/index.html +++ b/app/search/index.html @@ -2,3 +2,7 @@ + +
+
{{ searchResults | json}}
+
\ No newline at end of file diff --git a/app/search/mock-api.js b/app/search/mock-api.js new file mode 100644 index 0000000000..4ef472f839 --- /dev/null +++ b/app/search/mock-api.js @@ -0,0 +1,80 @@ +// We will be using backend-less development +// $http use $httpBackend to make its calls to the serve +// $resource uses $http, so it uses $httpBackend too +// We will mock $httpBackend, capturing routes and returning data + +angular.module('myApp') + .service('ServerDataModel', function ServerDataModel() { + this.data = [ + { + id: 1, + name: "Peyton Manning", + phone: "(3030 567-8910", + address: { + street: "1234 Main Street", + city: "Greenwood Village", + state: "CO", + zip: "80111" + } + }, + { + id: 2, + name: "Demaryius Thomas", + phone: "(720) 213-9876", + address: { + street: "5555 Marion Street", + city: "Denver", + state: "CO", + zip: "80202" + } + }, + { + id: 3, + name: "Von Miller", + phone: "(917) 323-2333", + address: { + street: "14 Mountain Way", + city: "Vail", + state: "CO", + zip: "81657" + } + } + ]; + + this.getData = function () { + return this.data; + }; + + this.search = function (term) { + if (term == "" || term == "*") { + return this.getData(); + } + + // find the name that matches the term + var list = $.grep(this.getData(), function (element, index) { + term = term.toLowerCase(); + return (element.name.toLowerCase().match(term)); + }); + + if (list.length === 0) { + return []; + } else { + return list; + } + }; + }) + + .run(function ($httpBackend, ServerDataModel) { + + $httpBackend.whenGET(/\/search\/\w+/).respond(function (method, url, data) { + // parse the matching URL to pull the term (/search/:term) + var term = url.split('/')[2]; + + var results = ServerDataModel.search(term); + + return [200, results, {Location: '/search/' + term}]; + }); + + $httpBackend.whenGET(/search\/index.html/).passThrough(); + $httpBackend.whenGET(/view/).passThrough(); + }); \ No newline at end of file diff --git a/bower.json b/bower.json index 19e683e032..d1a633c718 100644 --- a/bower.json +++ b/bower.json @@ -10,6 +10,7 @@ "angular-route": "1.2.x", "angular-loader": "1.2.x", "angular-mocks": "~1.2.x", - "html5-boilerplate": "~4.3.0" + "html5-boilerplate": "~4.3.0", + "jquery": "~1.10.x" } } From dd8bdd16dc2b80c2f796d3e05fde4d9d8ae1e1ee Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 22:17:32 +0800 Subject: [PATCH 10/12] Add margin to body element --- app/app.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/app.css b/app/app.css index c92524070a..de249c3527 100644 --- a/app/app.css +++ b/app/app.css @@ -1,5 +1,9 @@ /* app css stylesheet */ +body { + margin: 20px; +} + .menu { list-style: none; border-bottom: 0.1em solid black; From 390adff51b09339a03ff401d8eb234dcf76b5b83 Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 22:38:21 +0800 Subject: [PATCH 11/12] To make the results more readable, change the above HTML to use a and Angular's ng-repeat directive. This section has shown you how to fetch and display search results --- app/app.css | 18 ++++++++++++++++++ app/search/index.html | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/app/app.css b/app/app.css index de249c3527..74d1d9dfb6 100644 --- a/app/app.css +++ b/app/app.css @@ -32,3 +32,21 @@ body { content: ""; padding: 0; } + +table { + border-collapse: collapse; + border-spacing: 0; + margin: 10px 0; + text-align: left; + border: 1px solid #BBB; +} + +th, +td { + border-bottom: 1px solid #BBB; + padding: 10px; +} + +th { + padding: 15px 10px; +} \ No newline at end of file diff --git a/app/search/index.html b/app/search/index.html index 733bae9951..fd02017934 100644 --- a/app/search/index.html +++ b/app/search/index.html @@ -3,6 +3,26 @@ +
+ + + + + + + + + + + + + + +
NamePhoneAddress
{{person.name}}{{person.phone}}{{person.address.street}}
+ {{person.address.city}}, + {{person.address.state}} +
+
{{ searchResults | json}}
\ No newline at end of file From e75776d8857ec79f3c459215fb4b1063c861f18e Mon Sep 17 00:00:00 2001 From: Andrew Abogado Date: Mon, 9 Feb 2015 23:08:12 +0800 Subject: [PATCH 12/12] Add an Edit Feature --- app/search/edit.html | 22 ++++++++++++++++ app/search/index.html | 2 +- app/search/mock-api.js | 59 ++++++++++++++++++++++++++++++++++++++++++ app/search/search.js | 41 ++++++++++++++++++++++++++++- 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 app/search/edit.html diff --git a/app/search/edit.html b/app/search/edit.html new file mode 100644 index 0000000000..fd8e1b16a3 --- /dev/null +++ b/app/search/edit.html @@ -0,0 +1,22 @@ +
+
+ + +
+
+ + +
+
+ Address: +
+
+ , + + +
+
+
+ +
+
\ No newline at end of file diff --git a/app/search/index.html b/app/search/index.html index fd02017934..f14476edbf 100644 --- a/app/search/index.html +++ b/app/search/index.html @@ -13,7 +13,7 @@ - {{person.name}} + {{person.name}} {{person.phone}} {{person.address.street}}
{{person.address.city}}, diff --git a/app/search/mock-api.js b/app/search/mock-api.js index 4ef472f839..685587aef6 100644 --- a/app/search/mock-api.js +++ b/app/search/mock-api.js @@ -62,6 +62,35 @@ angular.module('myApp') return list; } }; + + this.find = function (id) { + // find the game that matches that id + var list = $.grep(this.getData(), function (element, index) { + return (element.id == id); + }); + if (list.length === 0) { + return {}; + } + // even if list contains multiple items, just return first one + return list[0]; + }; + + this.update = function (id, dataItem) { + // find the game that matches that id + var people = this.getData(); + var match = null; + for (var i = 0; i < people.length; i++) { + if (people[i].id == id) { + match = people[i]; + break; + } + } + if (!angular.isObject(match)) { + return {}; + } + angular.extend(match, dataItem); + return match; + }; }) .run(function ($httpBackend, ServerDataModel) { @@ -76,5 +105,35 @@ angular.module('myApp') }); $httpBackend.whenGET(/search\/index.html/).passThrough(); + $httpBackend.whenGET(/view/).passThrough(); + + $httpBackend.whenGET(/\/search/).respond(function (method, url, data) { + var results = ServerDataModel.search(""); + + return [200, results]; + }); + + $httpBackend.whenGET(/\/edit\/\d+/).respond(function (method, url, data) { + // parse the matching URL to pull out the id (/edit/:id) + var id = url.split('/')[2]; + + var results = ServerDataModel.find(id); + + return [200, results, {Location: '/edit/' + id}]; + }); + + $httpBackend.whenPOST(/\/edit\/\d+/).respond(function(method, url, data) { + var params = angular.fromJson(data); + + // parse the matching URL to pull out the id (/edit/:id) + var id = url.split('/')[2]; + + var person = ServerDataModel.update(id, params); + + return [201, person, { Location: '/edit/' + id }]; + }); + + $httpBackend.whenGET(/search\/edit.html/).passThrough(); + }); \ No newline at end of file diff --git a/app/search/search.js b/app/search/search.js index 4eebfb116d..5438164d6c 100644 --- a/app/search/search.js +++ b/app/search/search.js @@ -1,26 +1,65 @@ angular.module('myApp.search', ['ngRoute']) + .config(['$routeProvider', function($routeProvider) { $routeProvider .when('/search', { templateUrl: 'search/index.html', controller: 'SearchController' }) + .when('/search/:term', { + templateUrl: 'search/index.html', + controller: 'SearchController' + }) + .when('/edit/:id', { + templateUrl: 'search/edit.html', + controller: 'EditController' + }); }]) - .controller('SearchController', function($scope, SearchService) { + .controller('SearchController', function($scope, $location, $routeParams, SearchService) { + if ($routeParams.term) { + SearchService.query($routeParams.term).then(function (response) { + $scope.term = $routeParams.term; + $scope.searchResults = response.data; + }); + } + $scope.search = function () { console.log("Search term is: " + $scope.term); SearchService.query($scope.term).then(function (response) { $scope.searchResults = response.data; }); }; + + $scope.edit = function (person) { + $location.path("/edit/" + person.id); + }; + console.log("In Search Controller..."); }) + .controller('EditController', function ($scope, $location, $routeParams, SearchService) { + SearchService.fetch($routeParams.id).then(function (response) { + $scope.person = response.data; + }); + + $scope.save = function() { + SearchService.save($scope.person).then(function(response) { + $location.path("/search/" + $scope.person.name); + }); + } + }) + .factory('SearchService', function($http) { var service = { query: function (term) { return $http.get('/search/' + term); + }, + fetch: function (id) { + return $http.get('/edit/' + id); + }, + save: function(data) { + return $http.post('/edit/' + data.id, data); } }; return service;