Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Angular2 The Tour of Heroes tutorial に挑戦してみる 3

MULTIPLE COMPONENTS

https://angular.io/docs/ts/latest/tutorial/toh-pt3.html

  • 複数のcomponentsを扱う場合の話か。

Make a hero detail component

  • Add a file named hero-detai.component.ts to the app/ folder. This file will hold the new HeroDetailComponent.
import { Component } from '@angular/core';

@Component({
  selector: 'hero-detail', //selectorは、css selectorで、このコンポーネントを呼び出すときに使う名前かな?
})
export class HeroDetailComponent{

}
  • export classは、どこからでも、importするために設定するのか。exportするとき(importするとき)の設定をここに書くのか。

Hero detail template

  • my-appのHero detailの部分を、カットしてペーストする。
@Component({
  selector: 'hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div><label>id: </label>{{hero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})

Add the hero property

  • hero: Hero; のclass定義をセットする。
export class HeroDetailComponent{
  hero: Hero;
}
  • このままでは、Heroが定義されていないので、Heroのclassの定義をする
  • 2つ以上のcomponentsで、Hero classが必要なので、別ファイルで、Hero classの定義をしてしまう。
  • src/app/hero.ts ファイルを作成する。
export class Hero{
  id: number;
  name: string;
}
  • AppComponent and HeroDetailComponent have to import it.
  • Add the following import statement near the top of both the app.component.ts and the hero-detail.component.ts files.
import { Hero } from './hero';
  • djangoとfrom と importが逆になっただけだな。

The hero property is an input property

  • どのheroを表示するかについては、親Componentである、AppComponentが子ComponentのHeroDetailComponent へと伝達する。selectedHeroをhero property of the HeroDetailComponent へとbindingすることで。

<hero-detail [hero]="selectedHero"></hero-detail>

  • []でheroを囲んでいるのは, bindingのtargetにするため。
  • target binding property が、input propertyであることをdeclareする必要がある
  • otherwise, Angular rejects the binding and throws an error
  • First, amend the @angular/core import statement to include the Input symbol.

src/app/hero-detail.component.ts (excerpt)

import { Component, Input } from '@angular/core';
  • Then declare that hero is an input property by preceding it with the @Input decorator that you imported earlier.
export class HeroDetailComponent{
  @Input() hero: Hero;
}
  • bindingのためには、Inputの定義が必要とのこと。ここはまだよくわかってない。

Declare HeroDetailComponent in the AppModule

  • app.module.tsにimportする。app.module.tsがdjangoでいうところのsettings.pyに近いのかな。

Add the HeroDetailComponent to the AppComponent

template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <hero-detail [hero]="selectedHero"></hero-detail>
`,

What changed?

  • You simplified the AppComponent by reducing its responsibilities.

  • You can evolve the HeroDetailComponent into a rich hero editor without touching the parent AppComponent.

  • You can evolve the AppComponent without touching the hero detail view.

  • You can re-use the HeroDetailComponent in the template of some future parent component.

  • オブジェクト指向に近いものと考えていいのかな?

 まとめ・気づき

  • componentは分割して再利用
  • componentをactiveにするには、app.module.tsに設定する(settings.pyみたいなもんか)
  • component.tsに記載する、export classは、他からimportするときに必要なclassの定義。
  • []は,bindingのtargetであることを示す(ことが多い?)。
  • inputされるpropertyの場合は、@Input デコレータを設定する。
  • classの定義が重複する場合は、それ用のtsファイルを用意してそっちに記載する。

Angular2 The Tour of Heroes tutorial に挑戦してみる 2

Angular2 The Tour of Heroes tutorial に挑戦してみる 2

  • MASTER/DETAIL

https://angular.io/docs/ts/latest/tutorial/toh-pt2.html

Keep the app transpiling and running

$ npm start

Displaying heroes

Create Heroes

Create an array of ten heroes.

const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];
  • const は、書き換えができない変数を設定するときに使う
  • なので、var HEROESでも代用はできるはず!
  • HEROES という変数に、Hero classのリストを代入するということかな?
  • AppComponent classに、heroes という変数として、HEROESを代入する
  • Hero class のlist>Hero[]>HEROES>heroes という順番かな。 
export class AppComponent {
  title = 'Tour of Heroes';
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
  heroes = HEROES;
}

Display hero names in a template

<h2>My Heroes</h2>
<ul class="heroes">
  <li>
    <!-- each hero goes here -->
  </li>
</ul>
  • class にそのままheroesって, class(Hero[])を代入できるのか!!すげーな。

List heroes with ngFor

  • Modify the
  • tag by adding the built-in directive *ngFor.
  • li tagを修正する。builtin directiveの*ngForを使う。
  • The (*) prefix to ngFor is a critical part of this syntax らしい。つまり*重要ってことでいいのかな。
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
  • python風にすると、for hero in heroes って感じだな(^^)
  • class=badge のbadgeはあとで定義するのかしら。

Style the heroes

styles: [`
  .selected {
    background-color: #CFD8DC !important;
    color: white;
  }
  .heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
  }
  .heroes li {
    cursor: pointer;
    position: relative;
    left: 0;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
  }
  .heroes li.selected:hover {
    background-color: #BBD8DC !important;
    color: white;
  }
  .heroes li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
  }
  .heroes .text {
    position: relative;
    top: -3px;
  }
  .heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color: #607D8B;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
  }
`]

Selecting a hero

  • When users select a hero from the list, the selected hero should appear in the details view. This UI pattern is known as “master/detail.” そうなんや。

Add a click event

<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
...
</li>
  • click した時に、onSelect() methodをcallするという意味。引数には、heroを渡す。このheroは, ngForのheroから来ている。

Add a click handler to expose the selected hero

  • replace the hero property with this simple selectedHero property:
selectedHero: Hero;
  • これは、selected の型の定義は、Hero class ということか。
  • Add an onSelect method that sets the selectedHero property to the hero that the user clicks.
  • onSelect methodを追加しよう。
onSelect(hero: Hero): void{
  this.selectedHero = hero;
}
  • voidは、返り値がない、関数に使う。python風にいうと、def で、returnがないもの
  • hero のvalueを、this.selectedHeroに代入する。python風にいうと、self.selectedHeroってところかな。
  • hero: Heroってのは、よくわからんが、型の設定なのかな。hero は、Hero classで設定する。わかりやすくすると、(person: string)とかそんな感じ。
<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name"/>
</div>

Hide the empty detail with ngIf

  • ngIfを使う。Don't forget the asterisk (*) in front of ngIf.
<div *ngIf="selectedHero">
  <h2>{{selectedHero.name}} details!</h2>
  <div><label>id: </label>{{selectedHero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="selectedHero.name" placeholder="name"/>
  </div>
</div>

Style the selected hero

  • 選択されているheroは、styleを変える
<li *ngFor="let hero of heroes"
  [class.selected]="hero === selectedHero"
  (click)="onSelect(hero)">
  <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
  • これはtypescriptの構文なのか。=“条件式” は、条件式がtrueの場合に、内の処理を実行するとかそんな感じ?
  • classを選択する条件式なので、
  • タグ内に記載する。む〜ややこしい。
import { Component } from '@angular/core';
export class Hero {
  id: number;
  name: string;
}
const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes"
        [class.selected]="hero === selectedHero"
        (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span> {{hero.name}}
      </li>
    </ul>
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
  `,
  styles: [`
    .selected {
      background-color: #CFD8DC !important;
      color: white;
    }
    .heroes {
      margin: 0 0 2em 0;
      list-style-type: none;
      padding: 0;
      width: 15em;
    }
    .heroes li {
      cursor: pointer;
      position: relative;
      left: 0;
      background-color: #EEE;
      margin: .5em;
      padding: .3em 0;
      height: 1.6em;
      border-radius: 4px;
    }
    .heroes li.selected:hover {
      background-color: #BBD8DC !important;
      color: white;
    }
    .heroes li:hover {
      color: #607D8B;
      background-color: #DDD;
      left: .1em;
    }
    .heroes .text {
      position: relative;
      top: -3px;
    }
    .heroes .badge {
      display: inline-block;
      font-size: small;
      color: white;
      padding: 0.8em 0.7em 0 0.7em;
      background-color: #607D8B;
      line-height: 1em;
      position: relative;
      left: -1px;
      top: -4px;
      height: 1.8em;
      margin-right: .8em;
      border-radius: 4px 0 0 4px;
    }
  `]
})
export class AppComponent {
  title = 'Tour of Heroes';
  heroes = HEROES;
  selectedHero: Hero;
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

気づきなど

  • :で区別する場合は、変数の型セットのことが多いのか? valiable : class(type) とか。
  • void は、def のreturnが無いバージョン
  • thisはself
  • const は、変更不可の変数の定義

The Tour of Heroes tutorial に挑戦してみる 1

The Tour of Heroes tutorial に挑戦してみる 1

angular.io

Setup

Setup for local development - ts - GUIDE

  • quick startに従って、setupをしてみる。プロジェクトフォルダは適当に名前をつけておく。
$ git clone https://github.com/angular/quickstart.git quickstart
$ cd quickstart
$ npm install # これでパッケージをinstallできる
$ npm start # サンプルプログラム始動
  • Hello Angularというページがlocalhost:3000に表示されればOK!

Heroes Editor

  • プロジェクトフォルダ名をangular-tour-of-heroesに変えておく。(チュートリアルと揃えるため)

  • src/app/app.component.tsを修正する.

# もともとこんな感じ
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>`,
})
export class AppComponent  { name = 'Angular'; }

#以下のように修正

import { Component } from '@angular/core';

@Component({
  selector: 'my-app', # css selectorのようなもので、たぶん、tagで指定できるんだろうな。
  template: `<h1>{{title}}</h1><h2>{{hero}} details!</h2>` # これはtemplateになるhtmlファイル
})
# 以下で、変数などの設定ができる。
export class AppComponent  {
  title = 'Angular';
  hero = 'Windstorm';
}

Hero object

  • hero object classを作成する。Create a Hero class with id and name properties. Add these properties near the top of the app.component.ts file, just below the import statement. とのこと 。hero obj with id and name properties. add these properties new
import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>`
})
export class AppComponent  {
  title = 'Angular';
  # hero: Hero で、Hero classのinitial valueを設定する
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}

export class Hero {
  id: number;
  name: string;
}
  • それに合わせてtemplateも以下のように修正しておく。このあたりは、普通のclassと同じように使えるんやな〜〜(^^)
template: `<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>`

Edit the hero name

  • two-way binding を利用して、input form に入力されたvalueをhero.nameに反映させる。

Two-way binding

<div>
    <label>name: </label>
    <input [(ngModel)]="hero.name" placeholder="name" >
  </div>
  • この時点では、errorが出てしまう。
"ngModel ... isn't a known property of input."
  • Although NgModel is a valid Angular directive, it isn't available by default. It belongs to the optional FormsModule. You must opt-in to using that module.ということらしい。

Import the FormsModule

  • open the app.module.ts file and import the FormsModule symbol from the @angular/forms library.
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms'; // <-- NgModel lives here

import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule,
                  FormsModule // <-- import the FormsModule before binding with [(ngModel)]
                ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
  • 最終的には以下のようになる模様。
import { Component } from '@angular/core';
export class Hero {
  id: number;
  name: string;
}
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div>
      <label>name: </label>
      <input [(ngModel)]="hero.name" placeholder="name">
    </div>
    `
})
export class AppComponent {
  title = 'Tour of Heroes';
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}

まとめ

f:id:riikku:20170411104336p:plain

CSSとかのコツ

CSSって、ちゃんと勉強してないので、いまいち理解できてない。他の人が作ったhtmlファイルとか、cssファイルは読解が難しい気がする。

  • CSSの場合、classが重要なので、classをまず確認する。
  • そのclassが、どういう仕様になっているのか、コードの記載があるcssファイル(main.css)とかで確認する。
  • djangoに限って言えば、以下のようにstyleで囲んで新しいcss classを作成する。そのclassを追記すれば無理やり上書きできる。
<style>
{% block style %}
.btn-large {
    background-color: #00aeda;
}

.btn-blue {
    color: white;
    background-color:#00aeda;
}

.appIconBig{
 display: block;
 margin-left: auto;
 margin-right: auto;
 height: 75px;
 width: 75px;
}

.appIconS{
 padding: 0px;
 margin: 0px;
 height: 30px;
 width: 30px;
 vertical-align: middle;
}

.navbar-brand-small{
    padding:0px;
    margin:0px;
    vertical-align: middle;
}

@import 'https://fonts.googleapis.com/css?family=Dosis|PT+Sans';
.backimage-text{
    font-family:'PT Sans','Dosis', Avenir, "Open Sans", "游ゴシック", "Yu Gothic", Helvetica, sans-serif;
    /*font-weight: bolder;*/
}
{% endblock %}
</style>
  • cssを上書きするときに、ハイフォンでつないで、別のclassを追加するのはわかりやすいかもしれない。navbar-brandの場合は、navbar-brand-small とかいうふうに。

  • padding とmarginは大事。

  • padding 5 5 とかで、縦、横の設定が一気にできるので、楽。

  • padding-top, padding-bottom, padding-left, padding-right とかでも設定できる。

AngularJS1 + Django REST Frameworkに再挑戦 その12 Comment Reply Directive

概要

AngularJS1 + Django REST Frameworkに再挑戦 その12 Comment Reply Directive

参考動画

Django + AngularJS | Coding For Entrepreneurs

Reply to Comments part1

  • js/app/comment/comment.directive.js を作成する。
'use strict';

angular.module('core.comment').
    // Commentも読み込む
    directive('commentThread', function(Comment){
        return {
            restrict: "E",
            scope: {

            },
            template: "<ul><li>Reply</li></ul>",
            link: function(scope, element, attr){

            }
        }
    })
  • home.htmlに、<script src='{% static "js/app/core/comment/comment.directive.js" %}' ></script>に読み込ませる。

  • ここで1点ポイントは、comment.directive.jsの後に読み込ませる。なぜなら、comment.directive.js内の、Commentモジュールを継承したいため。

  • blog-detail.htmlに、<comment-thread></commentThread>を追記することで、commentThreadを利用できる。

  • 最終的に以下のようにcomment.directive.jsを修正する。

angular.module('core.comment').
    directive('commentReplyThread', function(Comment){
        return {
            restrict: "E",
            scope: {
                comment: '=comment',

            },
            template: "<ul ng-show='replies'><li ng-repeat='reply in replies'>{{ reply.content }}</li></ul>" + "<div class='text-center' ng-show='!replies'><img style='margin: 0 auto;' ng-src='/static/img/ring.gif' class='img-responsive'/><div>",
            // ng-src='/static/img/ring.gif'ここで、アニメーションがちょっと使える!
            // loading.ioという所のgifアニメを使うといいぽいな。
            link: function(scope, element, attr){
                if (scope.comment){
                    var commentId = scope.comment.id
                    if (commentId){
                        Comment.get({id: commentId}, function(data){
                            scope.replies = data.replies
                    })
                    }
                }
            }
        }
    })

Reply to Comments part2

  • blog-detailの一部を、comment.directive.jsのtemplateに埋め込む
'use strict';

angular.module('core.comment').
    directive('commentReplyThread', function(Comment){
        return {
            restrict: "E",
            scope: {
                comment: '=comment',

            },
            template: "<ul ng-show='replies'><li ng-repeat='reply in replies'>{{ reply.content }}</li></ul>" + "<div class='text-center' ng-show='!replies'><img style='margin: 0 auto;' ng-src='/static/img/ring.gif' class='img-responsive'/>" +
            "</div>" +
            "<p style='color:red;' ng-if='reply.content'>Preview: {{ reply.content }}</p>" +
            "<form ng-submit='addCommentReply(reply, comment)'>" +
            "<textarea class='form-control'  ng-model='reply.content'></textarea>" +
            "<input class='btn btn-default btn-sm' type='submit' value='Reply'/>" +
            "</form>",
            // ng-src='/static/img/ring.gif'ここで、アニメーションがちょっと使える!
            // loading.ioという所のgifアニメを使うといいぽいな。
            link: function(scope, element, attr){
                if (scope.comment){
                    var commentId = scope.comment.id
                    if (commentId){
                        Comment.get({id: commentId}, function(data){
                            scope.replies = data.replies
                    })
                    }
                }
            }
        }
    })
  • 一旦indexpageなどに直接コードをかく->angularのtemplateに一部埋め込む->angularのtemplateのhtmlを作成する という流れが一番わかりやすくてミスが少ないかもしれない。

AngularJS1 + Django REST Frameworkに再挑戦 その11 Reply to Comments

概要

AngularJS1 + Django REST Frameworkに再挑戦 その11 Reply to Comments

参考動画

Django + AngularJS | Coding For Entrepreneurs

Reply to Comments

  • コメントのreplyとかを表示したい。まずはcommentのcontextの内容を確認する。
<ul ng-show='comments.length > 0'>
    <li ng-repeat='comment in comments | filter: query'>
            {{ comment.content }} | Replies: {{ comment.reply_count }} | <a href='#' class='btn btn-sm btn-default' confirm-click='Do you want to delete this?' confirmed-click='deleteComment(comment)'>X</a>
    </li>
</ul>
  • さらにcommentの送信ボックスなどを作る。
<ul ng-show='comments.length > 0'>
    <li ng-repeat='comment in comments | filter: query'>
            {{ comment.content }}<br/>| <small>Replies: {{ comment.reply_count }} |<a href='#' class='' confirm-click='Do you want to delete this?' confirmed-click='deleteComment(comment)'>Remove</a></small>
            <br/>
            <div class='row'>
                <div class='col-sm-6'>
                    <p style='color:red;' ng-if='reply.content'>Preview: {{ reply.content }}</p>
                    <form ng-submit='addReply()'>
                        <textarea class='form-control'  ng-model='reply.content'></textarea>
                        <input class='btn btn-default btn-sm' type='submit' value='Reply'/>
                    </form>
                </div>
            </div>
        <div class='clearfix'></div>
        <br/>
        <br/>
    </li>
</ul>
  • commentのreplyのformに、parent_idが自動で入力されるようにする。
<form ng-submit='addReply()'>
    <input type='hidden' ng-model='reply.parent_id' value='{{ comment.id }}'>
    <textarea class='form-control'  ng-model='reply.content'></textarea>
    <input class='btn btn-default btn-sm' type='submit' value='Reply'/>
</form>
  • blog-detail.component.jsのaddReplyに追記してみる。
$scope.addReply = function() {
          console.log($scope.reply)
  • このままではうまく行かないので、addCommentReplyというfunctionをblog-detail.component.jsに追記する。
$scope.addCommentReply = function(reply, parentComment){
          console.log(reply)
          console.log(parentComment.id)
      }
      //これはとりあえず、replyの内容と、parentComment.idの内容をconsolelogに表示するだけ。
  • blog-detail.htmlに追記する。addCom
<form ng-submit='addCommentReply(reply, comment)'>
    <textarea class='form-control'  ng-model='reply.content'></textarea>
    <input class='btn btn-default btn-sm' type='submit' value='Reply'/>
</form>
  • blog-detail.htmlに追記する。addNewCommentというfunctionに変更する。
<div class='row'>
        <div class='col-sm-6'>
            <p style='color:red;' ng-if='newComment.content'>Preview: {{ newComment.content }}</p>
            <form ng-submit='addNewComment()'>
                <textarea class='form-control'  ng-model='newComment.content'></textarea>
                <input class='btn btn-default' type='submit'/>
            </form>
        </div>
    </div>
  • blog-detail.component.jsにaddNewCommentを作成する。
$scope.addNewComment = function() {
          // console.log($scope.reply)
          Comment.create({
              content: $scope.newComment.content,
              slug: slug,
              type: "post"
              },
              function(data){
                  //console.log(data)
                  $scope.comments.push(data)
                  resetReply()
              }, function(e_data){
                  // error
                  console.log(e_data)
              })
      }

AngularJS1 + Django REST Frameworkに再挑戦 その10 ngResource Create & Delete

概要

AngularJS1 + Django REST Frameworkに再挑戦 その10 ngResource Create & Delete

参考動画

Django + AngularJS | Coding For Entrepreneurs

ngResource Create & Delete

  • ngResource を使って、commentをsave, deleteする機能を実装する。

  • blog-detail.component.jsを編集する。Commentのsave機能をつける。

...
Comment.save({
  //saveに必要な項目を代入する。
                    content: $scope.reply.content,
                    slug: slug,
                    type: "post"
                    },
  //ここからは、dataと、e_dataがある場合は、error_dataを表示するようなfunctionをセットしてある。
                    function(data){
                        // success
                        console.log(data)
                    }, function(e_data){
                        // error
                        console.log(e_data)
                    })
  • これで、commentをsaveする機能がついた。あとは、今までのcomment createのコードをコメントアウトする。
...
$scope.addReply = function() {
          console.log($scope.reply)
          var token = $cookies.get("token")
          if (token){
              Comment.save({
                          content: $scope.reply.content,
                          slug: slug,
                          type: "post"
                          },
                          function(data){
                              // success
                              console.log(data)
                          }, function(e_data){
                              // error
                              console.log(e_data)
                          })

              // var req = {
              //     method: "POST",
              //     url: 'http://127.0.0.1:8000/api/comments/create/',
              //     data:{
              //         content: $scope.reply.content,
              //         slug: slug,
              //         type: "post",
              //     },
              //     headers:{
              //         authorization: "JWT " + token
              //     }
              // }
              //
              // var requestAction = $http(req)
              //
              // requestAction.success(function(r_data, r_status, r_headers, r_config){
              //     $scope.comments.push($scope.reply)
              //     resetReply()
              // })
              // requestAction.error(function(e_data,e_status, e_headers, e_config){
              //     console.log(e_data)
              // })
              //
              // $scope.comments.push($scope.reply)
              // // $scope.post.comments.push("abc")
          } else {
...
  • Failed to load resource: the server responded with a status of 405 (Method Not Allowed)このままだとこのerrorが出てしまうので、ちょっと修正が必要。GETではなく、POSTでデータを送付する必要あり。

  • 全体を修正する。comment.service.jsに、commentCreateメソッドを追加する。

'use strict';

angular.
    module('core.comment').
        factory('Comment', function($resource){
            // こちらのurlは、commentのurlにセットする。
            var url = '/api/comments/:id/'
            var commentQuery = {
                url: url,
                method: "GET",
                params: {},
                isArray: true,
                cache: false,
                transformResponse: function(data, headerGetter, status){
                    // console.log(data)
                    var finalData = angular.fromJson(data)
                    return finalData.results
                }

            }

            var commentGet = {
                method: "GET",
                params: {"id": "@id"},
                isArray: false,
                cache: false,
            }

            var commentCreate = {
                //こちらのurlは、djangoで設定したcommentsをcreateするurlを指定する。
                url: '/api/comments/create/',
                method: "POST",
                // params: {"id": "@id"},
                // isArray: false,
                // cache: false,
            }

            return $resource(url, {}, {
                query: commentQuery,
                get: commentGet,
                create: commentCreate,
            })

        });
  • headerのstatusText:"Unauthorized"というerrorが出て保存ができない!comment.service.js`で、コメントアウトしたところに、headerにauthorizationを入れるコードがあったと思うので、それを利用して、認証をする。
...
factory('Comment', function($cookies, $resource){
...
// cookiesからtokenをgetする。
var token = $cookies.get("token")
      if(token){
// headersにJWT tokenを代入する。
          commentCreate["headers"] = {"Authorization": "JWT " + token}
      }
  • これで一応は、登録ができるようになったけど、一度更新しないと、comment一覧に反映されないのがいまいち!なので、blog-detailページの処理を担当している、blog-detail.compose.jsを修正する。
if (token){
            Comment.create({
                        content: $scope.reply.content,
                        slug: slug,
                        type: "post"
                        },
                        function(data){
                            // success
                            //console.log(data)
                            //ここを追加する
                            $scope.comments.push(data)
                        }, function(e_data){
                            // error
                            console.log(e_data)
                        })
  • これでリアルタイムに反映されるようになった!

  • 次はdelete機能をつけていく。試しにblog-detail.component.jsに、シンプルなdelete機能を追記してみる。

$scope.deleteComment = function(comment) {
          comment.$delete()
          // $scope.$apply(
          //     $scope.comments.splice(comment, 1)
          // )
          // someResource.$delete()
      }
  • これで、deleteを試すと、405 (Method Not Allowed)が表示されてしまう。なので、createの時と同じように、comment.compose.jsに、commentDeleteメソッドを作成する。
...
var commentDelete = {
          // urlは、commentsのidを選択する。
          url: '/api/comments/:id/',
          // methodにDELETEを指定する。
          method: "DELETE",
          // params: {"id": "@id"},
          // isArray: false,
          // cache: false,
      }
...
    var token = $cookies.get("token")
        if(token){
          commentCreate["headers"] = {"Authorization": "JWT " + token}
          //ここを追記する。
          commentDelete["headers"] = {"Authorization": "JWT " + token}
        }

      return $resource(url, {}, {
          query: commentQuery,
          get: commentGet,
          create: commentCreate,
          delete: commentDelete
      })
  • これで試してみると、errorが表示されてしまう。どうも、comment_idが不明らしいということなので、deleteの際に、idを追記する。blog-detail.component.jsを修正する。
...
$scope.deleteComment = function(comment) {
          comment.$delete({"id": comment.id})
          // $scope.$apply(
          //     $scope.comments.splice(comment, 1)
          // )
          // someResource.$delete()
      }
...
  • これで一旦削除はできるようになった!次は、リアルタイムで画面から削除するコードを追記する。javascriptの、spliceメソッドを使う。多分これは、リストから指定した数の要素を削除するとかそういう意味だと思う。
$scope.deleteComment = function(comment) {
          comment.$delete({"id": comment.id},
          function(data){
              // success
              $scope.comments.splice(comment,1)
          }, function(e_data){
              // error
              console.log(e_data)
          })
  • 同じようにupdateReplyもaddReplyと似たような形で、実装できる。まずは、blog-detail.component.jsを修正する。
$scope.updateReply = function(comment) {
          Comment.update({
              "id": comment.id,
              content: $scope.reply.content,
              slug: slug,
              type: "post"
              },
              function(data){
                  //console.log(data)
                  //$scope.comments.push(data)
                  // resetReply()
              }, function(e_data){
                  // error
                  console.log(e_data)
              })
      }
  • 次に、comment.service.jsを修正する。
...
// まず、commentUpdateを作成する。
var commentUpdate = {
          url: '/api/comments/:id/',
          method: "PUT",
          // params: {"id": "@id"},
          // isArray: false,
          // cache: false,
      }
...
// 次に認証を作成する。
var token = $cookies.get("token")
      if(token){
          commentCreate["headers"] = {"Authorization": "JWT " + token}
          commentDelete["headers"] = {"Authorization": "JWT " + token}
          commentUpdate["headers"] = {"Authorization": "JWT " + token}
      }
...
// 最後にプロパティとして、登録する。
return $resource(url, {}, {
          query: commentQuery,
          get: commentGet,
          create: commentCreate,
          delete: commentDelete,
          update: commentUpdate,
      })
...
  • 結構ややこしいな。重要なのは、viewを修正することと、モデル(データ)を修正することは別々の機能として考えた方がわかりやすいということだね。