Skip to content

Commit 3e9cc9f

Browse files
committed
Merge pull request #122 from GoogleCloudPlatform/angular
Move over appengine-angular-hello-world-python
2 parents ba52d5b + 013673c commit 3e9cc9f

File tree

12 files changed

+438
-0
lines changed

12 files changed

+438
-0
lines changed

appengine/angular/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## App Engine AngularJS "Hello World" Python
2+
3+
A simple [AngularJS](http://angularjs.org/) CRUD application
4+
for [Google App Engine](https://appengine.google.com/).
5+
6+
Author: Fred Sauer <[email protected]>
7+
8+
9+
## Project setup
10+
11+
1. Install the [App Engine Python SDK](https://developers.google.com/appengine/downloads)
12+
13+
14+
## Testing the app locally
15+
16+
To run the app locally:
17+
18+
```
19+
dev_appserver.py .
20+
```
21+
22+
23+
## Deploying
24+
25+
To deploy the application:
26+
27+
1. Use the [Google Cloud Console](https://cloud.google.com/console) to create a project
28+
1. Replace `your-app-id` in `app.yaml` with the project id from the previous step
29+
1. Deploy the application:
30+
31+
```
32+
appcfg.py --oauth2 update .
33+
```
34+
35+
36+
## Contributing changes
37+
38+
See [CONTRIB.md](CONTRIB.md)
39+
40+
41+
# Licensing
42+
43+
See [LICENSE](LICENSE)

appengine/angular/app.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
application: your-app-id
2+
version: 1
3+
runtime: python27
4+
threadsafe: true
5+
api_version: 1
6+
7+
handlers:
8+
- url: /favicon\.ico
9+
static_files: favicon.ico
10+
upload: favicon\.ico
11+
12+
- url: /rest/.*
13+
script: main.APP
14+
15+
- url: (.*)/
16+
static_files: app\1/index.html
17+
upload: app
18+
19+
- url: (.*)
20+
static_files: app\1
21+
upload: app

appengine/angular/app/css/app.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.status {
2+
color: blue;
3+
padding: 1em;
4+
height: 1em;
5+
}

appengine/angular/app/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html ng-app="App">
3+
<head>
4+
<link rel="stylesheet" href="/css/app.css">
5+
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.js"></script>
6+
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular-route.js"></script>
7+
<script src="/js/app.js"></script>
8+
</head>
9+
<body>
10+
<h1>AngularJS Guest List</h1>
11+
<pre class="status" ng-bind="status"></pre>
12+
<div ng-view></div>
13+
</body>
14+
</html>

appengine/angular/app/js/app.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
'use strict';
2+
3+
var App = angular.module('App', ['ngRoute']);
4+
5+
App.factory('myHttpInterceptor', function($rootScope, $q) {
6+
return {
7+
'requestError': function(config) {
8+
$rootScope.status = 'HTTP REQUEST ERROR ' + config;
9+
return config || $q.when(config);
10+
},
11+
'responseError': function(rejection) {
12+
$rootScope.status = 'HTTP RESPONSE ERROR ' + rejection.status + '\n' +
13+
rejection.data;
14+
return $q.reject(rejection);
15+
},
16+
};
17+
});
18+
19+
App.factory('guestService', function($rootScope, $http, $q, $log) {
20+
$rootScope.status = 'Retrieving data...';
21+
var deferred = $q.defer();
22+
$http.get('rest/query')
23+
.success(function(data, status, headers, config) {
24+
$rootScope.guests = data;
25+
deferred.resolve();
26+
$rootScope.status = '';
27+
});
28+
return deferred.promise;
29+
});
30+
31+
App.config(function($routeProvider) {
32+
$routeProvider.when('/', {
33+
controller : 'MainCtrl',
34+
templateUrl: '/partials/main.html',
35+
resolve : { 'guestService': 'guestService' },
36+
});
37+
$routeProvider.when('/invite', {
38+
controller : 'InsertCtrl',
39+
templateUrl: '/partials/insert.html',
40+
});
41+
$routeProvider.when('/update/:id', {
42+
controller : 'UpdateCtrl',
43+
templateUrl: '/partials/update.html',
44+
resolve : { 'guestService': 'guestService' },
45+
});
46+
$routeProvider.otherwise({
47+
redirectTo : '/'
48+
});
49+
});
50+
51+
App.config(function($httpProvider) {
52+
$httpProvider.interceptors.push('myHttpInterceptor');
53+
});
54+
55+
App.controller('MainCtrl', function($scope, $rootScope, $log, $http, $routeParams, $location, $route) {
56+
57+
$scope.invite = function() {
58+
$location.path('/invite');
59+
};
60+
61+
$scope.update = function(guest) {
62+
$location.path('/update/' + guest.id);
63+
};
64+
65+
$scope.delete = function(guest) {
66+
$rootScope.status = 'Deleting guest ' + guest.id + '...';
67+
$http.post('/rest/delete', {'id': guest.id})
68+
.success(function(data, status, headers, config) {
69+
for (var i=0; i<$rootScope.guests.length; i++) {
70+
if ($rootScope.guests[i].id == guest.id) {
71+
$rootScope.guests.splice(i, 1);
72+
break;
73+
}
74+
}
75+
$rootScope.status = '';
76+
});
77+
};
78+
79+
});
80+
81+
App.controller('InsertCtrl', function($scope, $rootScope, $log, $http, $routeParams, $location, $route) {
82+
83+
$scope.submitInsert = function() {
84+
var guest = {
85+
first : $scope.first,
86+
last : $scope.last,
87+
};
88+
$rootScope.status = 'Creating...';
89+
$http.post('/rest/insert', guest)
90+
.success(function(data, status, headers, config) {
91+
$rootScope.guests.push(data);
92+
$rootScope.status = '';
93+
});
94+
$location.path('/');
95+
}
96+
});
97+
98+
App.controller('UpdateCtrl', function($routeParams, $rootScope, $scope, $log, $http, $location) {
99+
100+
for (var i=0; i<$rootScope.guests.length; i++) {
101+
if ($rootScope.guests[i].id == $routeParams.id) {
102+
$scope.guest = angular.copy($rootScope.guests[i]);
103+
}
104+
}
105+
106+
$scope.submitUpdate = function() {
107+
$rootScope.status = 'Updating...';
108+
$http.post('/rest/update', $scope.guest)
109+
.success(function(data, status, headers, config) {
110+
for (var i=0; i<$rootScope.guests.length; i++) {
111+
if ($rootScope.guests[i].id == $scope.guest.id) {
112+
$rootScope.guests.splice(i,1);
113+
break;
114+
}
115+
}
116+
$rootScope.guests.push(data);
117+
$rootScope.status = '';
118+
});
119+
$location.path('/');
120+
};
121+
122+
});
123+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<h2>Invite another guest</h2>
2+
<form ng-submit="submitInsert()">
3+
<p>
4+
<label>First:</label>
5+
<input type="text" ng-model="first" autofocus="true" />
6+
</p>
7+
<p>
8+
<label>Last:</label>
9+
<input type="text" ng-model="last" />
10+
</p>
11+
<input type="submit" class="btn" />
12+
</form>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<h2>Guest list</h2>
2+
<button ng-click="invite()">Invite another guest</button>
3+
<div ng-repeat="guest in guests | orderBy:'last' | orderBy:'first'">
4+
<button ng-click="delete(guest)">delete</button>
5+
<button ng-click="update(guest)">update</button>
6+
{{ $index + 1 }}. <b>{{ guest.first }} {{ guest.last }}</b>
7+
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<h2>Update guest information</h2>
2+
<form ng-submit="submitUpdate()">
3+
<p>
4+
<label>Id:</label>
5+
<input type="text" ng-model="guest.id" disabled="true" />
6+
</p>
7+
<p>
8+
<label>First:</label>
9+
<input type="text" ng-model="guest.first" autofocus="true" />
10+
</p>
11+
<p>
12+
<label>Last:</label>
13+
<input type="text" ng-model="guest.last" />
14+
</p>
15+
<input type="submit" class="btn" />
16+
</form>

appengine/angular/main.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Copyright 2013 Google, Inc
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import json
16+
17+
import model
18+
19+
import webapp2
20+
21+
22+
def AsDict(guest):
23+
return {'id': guest.key.id(), 'first': guest.first, 'last': guest.last}
24+
25+
26+
class RestHandler(webapp2.RequestHandler):
27+
28+
def dispatch(self):
29+
# time.sleep(1)
30+
super(RestHandler, self).dispatch()
31+
32+
def SendJson(self, r):
33+
self.response.headers['content-type'] = 'text/plain'
34+
self.response.write(json.dumps(r))
35+
36+
37+
class QueryHandler(RestHandler):
38+
39+
def get(self):
40+
guests = model.AllGuests()
41+
r = [AsDict(guest) for guest in guests]
42+
self.SendJson(r)
43+
44+
45+
class UpdateHandler(RestHandler):
46+
47+
def post(self):
48+
r = json.loads(self.request.body)
49+
guest = model.UpdateGuest(r['id'], r['first'], r['last'])
50+
r = AsDict(guest)
51+
self.SendJson(r)
52+
53+
54+
class InsertHandler(RestHandler):
55+
56+
def post(self):
57+
r = json.loads(self.request.body)
58+
guest = model.InsertGuest(r['first'], r['last'])
59+
r = AsDict(guest)
60+
self.SendJson(r)
61+
62+
63+
class DeleteHandler(RestHandler):
64+
65+
def post(self):
66+
r = json.loads(self.request.body)
67+
model.DeleteGuest(r['id'])
68+
69+
70+
APP = webapp2.WSGIApplication([
71+
('/rest/query', QueryHandler),
72+
('/rest/insert', InsertHandler),
73+
('/rest/delete', DeleteHandler),
74+
('/rest/update', UpdateHandler),
75+
], debug=True)

appengine/angular/model.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2013 Google, Inc
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.appengine.ext import ndb
16+
17+
18+
class Guest(ndb.Model):
19+
first = ndb.StringProperty()
20+
last = ndb.StringProperty()
21+
22+
23+
def AllGuests():
24+
return Guest.query()
25+
26+
27+
def UpdateGuest(id, first, last):
28+
guest = Guest(id=id, first=first, last=last)
29+
guest.put()
30+
return guest
31+
32+
33+
def InsertGuest(first, last):
34+
guest = Guest(first=first, last=last)
35+
guest.put()
36+
return guest
37+
38+
39+
def DeleteGuest(id):
40+
key = ndb.Key(Guest, id)
41+
key.delete()

0 commit comments

Comments
 (0)