AngularJS1 + Django REST Frameworkに再挑戦 その6 Create Comment in Backend with $http
概要
AngularJS1 + Django REST Frameworkに再挑戦 その6 Create Comment in Backend with $http
参考動画
Django + AngularJS | Coding For Entrepreneurs
Create Comment in Backend with $http
$httpを使って、バックエンドで、コメント作成機能を実装する。
blog-detail.component.jsに、commentsの中身をscopeに渡すように設定する。
Post.get({"slug": $routeParams.slug}, function(data){ $scope.post = data $scope.comments = data.comments })
- blog-detail.htmlのcommentsの箇所を修正する。
<h3>Comments</h3> <div class='row' ng-show='comments.length > 0'> <div class='col-sm-6'> <input class='form-control' type='text' ng-model='query' placeholder='Filter Comments' /> <br/> </div> </div> <ul ng-show='comments.length > 0'> <li ng-repeat='comment in comments | filter: query'> {{ comment.content }} <a href='#' class='btn btn-sm btn-default' confirm-click='Do you want to delete this?' confirmed-click='deleteComment(comment)'>X</a> </li> </ul>
- blog-detail.component.jsに、$cookiesを継承するように設定しておく。
controller: function(Post, $cookies, $http, $location, $routeParams, $scope){
// "Authorization: JWT" '{"content":"some reply to another try"}' 'http://127.0.0.1:8000/api/comments/create/?slug=new-title&type=post&parent_id=13'
- addReply functionにコメント作成機能の実装していく。slugは $routeParams.slugで、持ってくる。
... var slug = $routeParams.slug Post.get({"slug": slug}, function(data){ ... $scope.addReply = function() { console.log($scope.reply) var req = { url: 'http://127.0.0.1:8000/api/comments/create/?slug=' + slug + '&type=post' data:{ content: $scope.reply.content } } $scope.comments.push($scope.reply) // $scope.post.comments.push("abc") resetReply() }
- tokenの有無でコメントできるかできないかを変更する。
$scope.addReply = function() { console.log($scope.reply) var token = $cookies.get("token") if (token){ var req = { method: "POST", url: 'http://127.0.0.1:8000/api/comments/create/?slug=' + slug + '&type=post', data:{ content: $scope.reply.content }, headers:{ authorization: "JWT " + token } } $scope.comments.push($scope.reply) // $scope.post.comments.push("abc") resetReply() } else { console.log("no token") } }
- $httpを利用して、tokenがある場合のreqの情報を送付する。
var requestAction = $http(req) requestAction.success(function(r_data, r_status, r_headers, r_config){ $scope.comments.push($scope.reply) }) requestAction.errors(function(e_data,e_status, e_headers, e_config){ console.log(e_data) })
resetReply()
functionは、requestAction.successの場合に組み込む。また、resetReply functionの内容も、text->contentに変更する。
function resetReply(){ $scope.reply = { "id": $scope.comments.length + 1, "content": "", } }
これでcommentを作成してみる。。。error!どうも、requestAction.errorsではなく、requestAction.errorが正解らしい。それを修正して試してみる。。。できた!
けど、tokenの期限が切れている場合はうまくいかないらしい。これは後で対応するとのこと。
comment delete機能も使ってみる。。。表面上は消えたように見えるが、ページを更新するとまた表示されてしまう。これを解消するには、結構難しい処理が必要らしい。
今回もなかなかヘビーだった。
AngularJS1 + Django REST Frameworkに再挑戦 その6 ngCookies for JWT Token
概要
AngularJS1 + Django REST Frameworkに再挑戦 その6 ngCookies for JWT Token
参考動画
Django + AngularJS | Coding For Entrepreneurs
ngCookies for JWT Token
前回の続きで、今回は、JWT によって発行されたtokenを使って、angularJSからのログインを実装する。
その為には、ngCookiesというライブラリ?を利用する。
公式サイト:
https://docs.angularjs.org/api/ngCookies
- これを利用するには、home.htmlで、読み込む必要あり!
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-cookies.js'></script>
- さらにapp.module.jsにいつものように追記する。
'ngCookies',
- login-detai.component.jsに追記する。
...
controller: function(
$cookies,
...
- いよいよtokenをcookieのputメソッドを利用して、入力してみる。
requestAction.success(function(r_data, r_status, r_headers, r_config){ console.log(r_data) //tokenをゲットできる。 $cookies.put("token", r_data.token) // r_data.tokenを"token"としてcookieに送付できる。 })
さらに、
var tokenExists = $cookies.get("token")
で、入力したtokenをtokenExistsに代入する。tokenExistsがある場合だけ、loginができるような下準備をする。
var tokenExists = $cookies.get("token") if (tokenExists) { // verify token $scope.loggedIn = true; }
- cookiesにusernameも渡すようにしておく。あとあと使うのかな?
requestAction.success(function(r_data, r_status, r_headers, r_config){ // console.log(r_data) //tokenをゲットできる。 $cookies.put("token", r_data.token) // r_data.tokenを"token"としてcookieに送付できる。 $cookies.put("username", user.username) // usernameもcookiesに入れておく。なぜだ? })
これでログインしてみる。。。特に何も起きない。。。なぜだ。
cookieにはちゃんと渡せているか、blog-list.component.jsを使って実験してみる。
controller: function(Post, $cookies, $location, $routeParams, $rootScope, $scope){ // console.log($location.search()) console.log($cookies.get("username")) console.log($cookies.get("token"))
OK!ちゃんと表示されている!
navbarにログインボタンを表示させたいので、try-nav.htmlに追記する。
<li><a ng-href="/login" ng-hide='userLoggedIn'>Login</a></li>
- さらに、try-nav.directive.jsにuserLoggedInの条件分岐を追記する。
scope.userLoggedIn = false var token = $cookies.get("token") console.log(token) if (token) { scope.userLoggedIn = true }
これで、tokenがあるときは、userLoggedInがtrueになり、結果的に、loginしているときは、loginボタンが表示されないようになる。
tokenは、最低限の仕事を終えたら、cookieから削除するため、以下の通り、tokenを削除するコードをlogin-detail.component.javascriptに追記する。usernameは、cokkiesからゲットして、scope.userの辞書に加えておく。
var tokenExists = $cookies.get("token") if (tokenExists) { // verify token $scope.loggedIn = true; $cookies.remove("token") $scope.user = { username: $cookies.get("username") } }
- ログインに成功したとき用のlocationも追記しておく。
requestAction.success(function(r_data, r_status, r_headers, r_config){ ... $location.path("/") ... })
おー、ログインに成功したらトップ画面が表示されるようになった〜!
ただ現状では、loginボタンがうまく消えない。ので、try-nav.directive.jsを修正する。$watchを使う。$watchは、対象の関数、変数などが変化したときに監視して、処理してくれる便利なメソドぽい。
scope.userLoggedIn = false scope.$watch(function(){ var token = $cookies.get("token") // console.log(token) if (token) { scope.userLoggedIn = true } else { scope.userLoggedIn = false } })
オーー、ログインしたら消えた〜!
ログアウトボタンも
<li><a ng-href="/logout" ng-show='userLoggedIn'>Logout</a></li>
で作成してみる。loginの時と逆で、userLoggedInが、trueの時に現れる。loginの時にtokenが削除されているので、logoutをクリックすると、userloggedinが、falseになる。さらに、app.config.jsにroutingを追記する。logoutにアクセスしたときは、自動で、loginにredirectするように設定する。
when("/logout", { // template: "<login-detail></login-detail>" redirectTo: '/login' }).
AngularJS1 + Django REST Frameworkに再挑戦 その5 Login with Angular & Django Rest Framework
概要
AngularJS1 + Django REST Frameworkに再挑戦 その5 Login with Angular & Django Rest Framework
参考動画
Django + AngularJS | Coding For Entrepreneurs
Login with Angular & Django Rest Framework
参照サイト:
Django で API のトークンベースの認証を JWT で行なう - Qiita
- まず、src/static/js/login-detai.module.jsを作成する。
'use strict'; angular.module('loginDetail', []);
- 次に, src/static/js/login-detail.component.jsを作成する。これは、他のangularのコードからコピペしてくる。
'use strict'; angular.module('loginDetail'). component('loginDetail', { templateUrl: '/api/templates/login-detail.html', controller: function(Post, $location, $routeParams, $rootScope, $scope){ } })
- さらに、login-detail.component.jsにコードを書いていく。
'use strict'; angular.module('loginDetail'). component('loginDetail', { templateUrl: '/api/templates/login-detail.html', controller: function(Post, $location, $routeParams, $rootScope, $scope){ var loginUrl = '/api/auth/token/' //http://127.0.0.1:8000/api/auth/token/にアクセスする。 $scope.user = { } // scope.userで、userを指定する。 $scope.doLogin = function(user){ console.log(user) } // これでloginを実行する関数を作成する。 // $http.post() } })
- ここで一旦テストをしたいので、home.htmlにlogin-detailのファイルを読み込ませる。
<script src='{% static "js/app/login-detail/login-detail.module.js" %}' ></script> <script src='{% static "js/app/login-detail/login-detail.component.js" %}' ></script>
- さらに、app.module.jsに追記する。
'use strict'; angular.module('try', [ // external 'angularUtils.directives.dirPagination', 'ngResource', 'ngRoute', 'ui.bootstrap', // internal 'blogDetail', 'blogList', 'confirmClick', 'loginDetail', 'tryNav', ]);
- app.config.jsにログイン画面のurlとtemplateを追記する。
when("/login", { template: "<login-detail></login-detail>" }).
- login-detail.component.jsで指定した、
templateUrl: '/api/templates/login-detail.html',
を作成する。
<div class='col-sm-6 col-sm-offset-3'> <form ng-submit='doLogin(user)'> <input type='text' ng-model='user.username' class='form-control'> <input type='password' ng-model='user.password' class='form-control'> <input type='submit' value='Login' class='btn btn-default' /> </form>
ここまで設定したら、
http://127.0.0.1:8000/login
にアクセスすると、ログインフォームが表示されるようになる!ng-model=userの値を、doLogin(user)のuserに代入して、doLoginのfunctionを実行することになると思われる。
userと、passwordを入れて送信ボタンを押すと、doLoginファンクションが起動して、console.logに、
password: "****"username: "****"
という形で、表示されることがわかる。doLogin(user)functionに追記する。
... $scope.doLogin = function(user){ console.log(user) var requestAction = $http.post(loginUrl, user) // $http.post は、httpモジュールのpostメソッドで、loginUrlに、userデータを送付するという意味だと思われる。 requestAction.success(function(r_data, r_status, r_headers, r_config){ console.log(r_data) //これは実はtokenをゲットできる! }) // successだった場合の requestAction.error(function(e_data, e_status, e_headers, e_config){ console.log(e_data) //error } ...
これで、ログイン画面に戻って操作してみる。すると、user認証ができた場合には、consoleに、
Object {token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkI…20ifQ.Quq-mALFfGd09JS9OuxcZqKukY6qniU1RGWoEgoYbmg"}
という形で、トークンが表示されて、errorの場合は、表示されない!なるほど、これで認証してるのか!doLogin functionにreqConfigという辞書型のリストを追記する。これはデータをusernameなどの情報をまとめて$http.postなどをするためだと思う。
$scope.doLogin = function(user){ console.log(user) var reqConfig = { method: "POST", url: loginUrl, data: { username: user.username, password: user.password }, headers: {} }
AngularJS1 + Django REST Frameworkに再挑戦 その4 Backend API Serializer Updates
概要
AngularJS1 + Django REST Frameworkに再挑戦 その4 Backend API Serializer Updates Actions
参考動画
Django + AngularJS | Coding For Entrepreneurs
Backend API Serializer Updates
- 前回は、BackendのDjango REST Frameworkからうまくslugのjsonデータが取得できてなかったので、django rest frameworkのserializerを修正する。slugとimageフィールドを追記する。
class PostListSerializer(ModelSerializer): url = post_detail_url user = UserDetailSerializer(read_only=True) image = SerializerMethodField() class Meta: model = Post fields = [ 'url', 'user', 'title', 'content', 'publish', 'slug', 'image', ] def get_image(self, obj): try: image = obj.image.url except: image = None return image
- 無事表示された〜!そしてviewにもちゃんと表示された〜!
AngularJS1 + Django REST Frameworkに再挑戦 その3 Post Objects API Integration
概要
AngularJS1 + Django REST Frameworkに再挑戦 その3 Post Objects API Integration
参考動画
Django + AngularJS | Coding For Entrepreneurs
Post Objects API Integration
http://127.0.0.1:8000/api/posts/
にアクセスすると、djangoのデータベースに保存されている、postのjsonデータが表示される。今の127.0.0.1:8000
に表示されるデータは、static/json/posts/posts.jsonデータが元になっている。そのため、これをデータベースと連動させたい。まず、js/core/post/post.service.jsを修正する。postのデータの元をdjango rest frameworkのpostsのurlに単純に変更してみる。
// var url = '/static/json/posts.json' var url = '/api/posts/'
http://127.0.0.1:8000/
にアクセスすると、リストが表示されない。。。Error: $resource:badcfg Response does not match configured parameter
が表示されている。以下のようにpost.service.jsを修正してみる。
angular. module('post'). factory('Post', function($resource){ var url = '/api/posts/' return $resource(url, {}, { query: { method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ console.log(data) return data } // interceptor },
それでも表示されない。。console.logにはなぜか、htmlが表示されている。なぜだ。。。
理由は、
var url = '/api/posts/'
を設定しており、/api/postsのページのhtmlデータを持ってきてたからだった。。。ということで、ここには、別のURLを設定する必要がある。app.config.jsを修正する。
angular.module('try'). config( function( $locationProvider, $resourceProvider, $routeProvider ){ $locationProvider.html5Mode({ enabled:true }) $resourceProvider.defaults.stripTrailingSlashes = false;
- resourceProviderについては、以下の公式サイトを参照。$resourceの働きを変更するものらしい。
https://docs.angularjs.org/api/ngResource/provider/$resourceProvider
$resourceProvider.defaults.stripTrailingSlashes = false;
は、Restfulなdataを持ってくるために必要なことらしい。
By default, trailing slashes will be stripped from the calculated URLs, which can pose problems with server backends that do not expect that behavior. This can be disabled by configuring the $resourceProvider
これでもまだerrorは出てしまう。。。ただ、logにjsonデータは表示されているので、あと一歩!
post.service.jsを修正する。dataをAngular.Jsonで、angular用のデータに修正する。
transformResponse: function(data, headerGetter, status){ console.log(data) var finalData = angular.fromJson(data) return finalData.results }
おおーーこれで、表示されるようにはなった。ただ、titleのURLのリンクが、/blogになっており、うまくdetailページが表示されない。
理由は、djangoREST Frameworkは、detailpageの表示URLがslugになっていたことみたいやな。
'use strict'; angular. module('post'). factory('Post', function($resource){ // : slug/を付け加える。 var url = '/api/posts/:slug/' return $resource(url, {}, { query: { method: "GET", params: {}, isArray: true, cache: false, transformResponse: function(data, headerGetter, status){ console.log(data) var finalData = angular.fromJson(data) return finalData.results } // interceptor }, get: { method: "GET", // paramsをslugに修正する。 params: {"slug": "@slug"}, // isArrayをfalseにする。 isArray: false, cache: false, } })
- app.config.jsのblog-detailの箇所のURLを、修正する。slugをつける。
when("/blog/:slug", { template: "<blog-detail></blog-detail>" }).
- blog-detail.component.js を修正する。
angular.module('blogDetail'). component('blogDetail', { templateUrl: '/api/templates/blog-detail.html', controller: function(Post, $http, $location, $routeParams, $scope){ Post.get({"slug": $routeParams.slug}, function(data){ $scope.post = data })
- blog-list.htmlを修正する。id->slugに修正する。
<a ng-href='/blog/{{ post.slug }}' ng-if='post.image'> <img ng-src='{{ post.image }}' class='img-responsive'> </a> <div class="caption"> <h3><a ng-href='/blog/{{ post.slug }}'>{{ post.title }}</a></h3> <p>{{ post.text }} {{ post.publishDate }}</p> <p><a ng-href='/blog/{{ post.slug }}' class='btn btn-primary'>View</a></p>
それでも出てこない。。。なんでだ。。。と思ったら、api/postsのデータにslugを表示させてなかったのが、原因だった〜。
なので、次章ではこれを修正するらしい〜〜!
AngularJS1 + Django REST Frameworkに再挑戦 その2 Angular Templates in Django Part 1
概要
AngularJS1 + Django REST Frameworkに再挑戦 その2 Angular Templates in Django Part 1
参考動画
Django + AngularJS | Coding For Entrepreneurs
Angular Templates in Django Part 1
src/ang/init.py と、src/ang/views.pyを作成する。
views.pyは以下のように設定する。
from django.shortcuts import render def get_angular_template(request, path=None): return render(request, "ang/app/blog-list.html", {})
templates/ang/app/blog-list.htmlファイルを作成する。内容は、try-angular1.5のblog-list.htmlをひとまずコピペする。
また、templates/ang/home.htmlには、try-angular1.5のindex.htmlをコピペする。
さらに、ang-srcの、img, js, jsonなどのstaticフォルダを, src/static/に丸ごとコピーして配置する。
python manage.py collectstatic
で、try-angularからコピーしてきたファイルをまとめる。この状態で、
python manage.py runserver
してみる。。。errorは出ないけどまだ空白のページのまま。検証してみると、Uncaught SyntaxError: Unexpected token <
という表示が出ている。多分ここのerrorは、djangoでrenderingするときのstaticfileの扱い方の問題だと思われるので、次章で解決する。
Static Files in Django & Angular
{% load staticfiles %}
を追記する。読み込みたいstaticfileを
'{% static "" %}'
で囲む。まとめると以下のようになる。
{% load staticfiles %} ... <script src='{% static "js/external/dirPagination.js" %}' ></script> <script src='{% static "js/app/app.module.js" %}' ></script> <script src='{% static "js/app/app.config.js" %}' ></script> <script src='{% static "js/app/core/post/post.module.js" %}' ></script> <script src='{% static "js/app/core/post/post.service.js" %}' ></script> <script src='{% static "js/app/blog-detail/blog-detail.module.js" %}' ></script> <script src='{% static "js/app/blog-detail/blog-detail.component.js" %}' ></script> <script src='{% static "js/app/blog-list/blog-list.module.js" %}' ></script> <script src='{% static "js/app/blog-list/blog-list.component.js" %}' ></script> <script src='{% static "js/app/utils/confirm-click/confirm-click.module.js" %}' ></script> <script src='{% static "js/app/utils/confirm-click/confirm-click.directive.js" %}' ></script> <script src='{% static "js/app/utils/try-nav/try-nav.module.js" %}' ></script> <script src='{% static "js/app/utils/try-nav/try-nav.directive.js" %}' ></script> ...
- これでhome.htmlのerrorは無くなった!でも相変わらず空白のページ。shellは以下のように延々と表示されている。
[21/Jan/2017 23:47:19] "GET /json/posts.json HTTP/1.1" 200 2316 [21/Jan/2017 23:47:20] "GET /json/posts.json HTTP/1.1" 200 2316 [21/Jan/2017 23:47:20] "GET /json/posts.json HTTP/1.1" 200 2316 ...
- どうもjsonファイルの読み込みに問題があるらしい。/json/posts.jsonが読み込めていないので、その部分の読み込みのルートを修正する。該当箇所は、static/js/app/post/post.service.js にある。以下のようにurlを修正する。
... var url = '/static/json/posts.json' ...
- でも、まだ空白ページは続く。。。
Angular Templates in Django Part 2
- src/blog/blog/urls.pyを設定する。
... # 普通にviewをimportするだけ from ang.views import get_angular_template ... # 正規表現でget_angular_templateのurlを設定する url(r'^api/templates/(?<item>[A-Za-z0-9\_\-\.\./]+)\.html$', get_angular_template), ...
- さらに、ang/views.pyに、以下の通り、itemを表示させるような設定にしてみる。
def get_angular_template(request, item=None): print(item) return render(request, "ang/app/blog-list.html", {})
上記を設定して、
http://127.0.0.1:8000/api/templates/blog-list.templates.html
に、アクセスする。そうすると、blog-list.templates とshellに表示される。つまり、127.0.0.1:8000/api/templates/blog-list.templates.htmlの、blog-list.templates
の部分がitemとして表示されている。これはurlからviewが取得している。この性質を利用して、urlを設定していく。ang/views.pyに設定する。
import os from django.conf import settings from django.http import HttpResponse, Http404 from django.views.generic import View from django.shortcuts import render class AngularTemplateView(View): def get(self, request, item=None, *args, **kwargs): template_dir_path = settings.TEMPLATES[0]["DIRS"][0] #settingsのTEMPLATESのDIRの1番目の要素。 final_path = os.path.join(template_dir_path, "ang", "app", item + ".html") #templates/ang/app/templateのhtml指定 try:#templateがある場合は、返す。 html = open(path) return HttpResponse(html) except:#ない場合は404 raise Http404
- urls.pyを修正する。
rl(r'^api/templates/(?P<item>[A-Za-z0-9\_\-\.\./]+)\.html$', AngularTemplateView.as_view()),
この状態で、
127.0.0.1:8000/api/templates/blog-list.html
にアクセスしてみる。。。404が表示されてしまう。Not Found: /api/templates/blog-list.html
と表示されてしまうので、templateの場所がうまく指定されてないのかもしれない。ang/views.pyの
html = open(path)
をhtml=open(final_path)
に修正する。。。表示された!pathが間違ってた〜〜!このままでは、angularからのcontextなどが引き継がれていないので、angularJS側のurlのroutingを修正する。blog-list/component.jsなどなど。
// blog-list.component.js ... templateUrl: '/api/templates/blog-list.html' ...
- さらに、app.config.jsも修正する。
when("/about", { templateUrl: "/api/templates/about.html" }).
表示された〜〜!
まとめると以下のようになるはず。
- staticファイルをdjangoのプロジェクトに持ってくる。js,img,cssなど。
- それぞれのテンプレートファイルに、
{% static %}
を設定する。 - さらに、javascriptのstaticファイルに関しては、絶対パスでurlを指定する。例えば、
var url = '/static/json/posts.json'
など。 - angularのtemplatesのhtmlファイルにアクセスするためのurlは、
url(r'^api/templates/(?P<item>[A-Za-z0-9\_\-\.\./]+)\.html$', AngularTemplateView.as_view()),
などで設定しておく。 - angularの各種moduleが、上記のtemplates urlにアクセスできるように、angular関連のjsファイルのtemplateURLのファイルパスを設定変更する。
AngularJS1 + Django REST Frameworkに再挑戦 その1Django & AngularJS Project
概要
AngularJS1 + Django REST Frameworkに再挑戦 その1Django & AngularJS Project
参考動画
Django + AngularJS | Coding For Entrepreneurs
Git & Django Project
- 以下の動画に従って、django projectをgit cloneする
Git & Django Project | Django + AngularJS | Coding For Entrepreneurs
Downloading Try AngularJS 1.5
- 以下の動画に従って、angular projectをダウンロードする
Downloading Try AngularJS 1.5 | Django + AngularJS | Coding For Entrepreneurs
Single Page App Template View
# url(r'^comments/', include("comments.urls", namespace='comments')), # # url(r'^register/', register_view, name='register'), # url(r'^login/', login_view, name='login'), # url(r'^logout/', logout_view, name='logout'), # url(r'^', include("posts.urls", namespace='posts')),
- Templateviewをimportして、angular用のhtmlを指定してみる。
... from django.views.generic.base import TemplateView ... urlpatterns = [ ... url(r'', TemplateView.as_view(template_name='ang/home.html')), ]
上記のtemplate view用の
src/templates/ang/home.html
を作成する。中身は空でOK。localhost:8000にアクセスすると空白のページが表示される。これでOK。
ただし、localhost:8000/admin のようなURLでも空白のページが表示されるようになってしまっている。urls.pyを少し修正する。templateviewは最後に持ってくる。
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/auth/token/', obtain_jwt_token), url(r'^api/users/', include("accounts.api.urls", namespace='users-api')), url(r'^api/comments/', include("comments.api.urls", namespace='comments-api')), url(r'^api/posts/', include("posts.api.urls", namespace='posts-api')), ] ... urlpatterns += [ url(r'', TemplateView.as_view(template_name='ang/home.html')), ]
- angularが扱うページは、djangoが扱うapiviewなどの後に持ってくる。