質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.61%

  • Node.js

    1803questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

  • Vue.js

    655questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Express

    232questions

    ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

VuejsのComponent内のhrefの値をfetchで取得してDOMを表示したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 201

mitts

score 11

 前提・実現したいこと

VuejsのComponentを利用してfetchで取得してした配列の数分だけ並べて表示したい
またfetchで取得した配列内のURLに応じてhrefを変更したい

backeendはNode+expressで実装しようとして居ます
fetch で特定のURLでGETした場合 Backendで特定のディレクトリをreadしてファイル名を取得してfrontend に返そうと考えて居ます。

何も表示されない状況です 誤っている箇所を教えていただきたいです

 確認したこと

chromeの開発者ビューのデバッグ実行を実施
以下のjson.fileListに目的の文字列(URL)がArrayで格納されていることを確認した

this.imageList = json.fileList;

 発生しているエラー

vue.js:597 [Vue warn]: data functions should return an object:
https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function

vue.js:597 [Vue warn]: Property or method "list" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

 該当のソースコード

var express = require('express');
var app = express();
var config = require('./config');
var fs = require('fs');

app.get('/',function(req,res){
    res.sendfile(__dirname + '/index.html');
});

app.get('/:id',function(req,res){
    res.sendfile(__dirname + '/' + req.params.id);
});

app.get('/imageList/:id',function(req,res){
    var dirName = req.params.id;
    fs.readdir(__dirname + '/images/' + dirName,function(err,files){
        if(err) throw err;
        var fileList = files.filter(function(file){
            return /.*\.(jpg|png)$/.test(file); //絞り込み
        });
        var fileListExt = [];
        fileList.forEach(file => {
            fileListExt.push('/images/' + dirName+ '/' + file);
        });
        res.json({fileList : fileListExt});
    });
});

app.use('/css', express.static('css'));
app.use('/images', express.static('images'));
app.use('/js', express.static('js'));
app.use('/fonts', express.static('fonts'));

app.listen(config.port);
<!DOCTYPE HTML>
<!--
    Aesthetic by gettemplates.co
    Twitter: http://twitter.com/gettemplateco
    URL: http://gettemplates.co
-->
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <!-- Animate.css -->
    <link rel="stylesheet" href="css/animate.css">
    <!-- Icomoon Icon Fonts-->
    <link rel="stylesheet" href="css/icomoon.css">
    <!-- Bootstrap  -->
    <link rel="stylesheet" href="css/bootstrap.css">
    <!-- Theme style  -->
    <link rel="stylesheet" href="css/style.css">

    <!-- Magnific Popup -->
    <link rel="stylesheet" href="css/magnific-popup.css">

    <!-- Modernizr JS -->
    <script src="js/modernizr-2.6.2.min.js"></script>
    <!-- FOR IE9 below -->
    <!--[if lt IE 9]>
    <script src="js/respond.min.js"></script>
    <![endif]-->

</head>

<body>

        <div class="container-fluid" id="fh5co-image-grid">
            <div class="grid" id="image-area">
                <div class="grid-sizer"></div>
                <image-component 
                    v-for="elm in list" 
                    v-bind:elm = "elm"
                    v-bind:key="elm.key" 
                    v-bind:href="elm.value" 
                    v-bind:src="elm.value">
                </image-component>
            </div>
        </div>

    <!-- jQuery -->
    <script src="js/jquery.min.js"></script>

    <!-- Vue JS -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- MAIN JS -->
    <script src="js/main.js"></script>

    <script>
        var imageList;
        document.addEventListener('DOMContentLoaded', function () {
            fetch('/imageList/kyoto').then(function (response) {
                return response.json();
            }).then(function (json) {
                this.imageList = json.fileList;
            });
        });
        Vue.component('image-component', {
            props: ['elm'],
            // template : '<li>{{elm}}</li>'
            template: '<div class="grid-item item animate-box" data-animate-effect="fadeIn">\
                    <a href="elm.value" class="image-popup" title="Name of photo or title here">\
                        <div class="img-wrap">\
                            <img src="elm.value" alt="" class="img-responsive">\
                        </div>\
                        <div class="text-wrap">\
                            <div class="text-inner popup">\
                                <div>\
                                    <h2>Name of photo or title here</h2>\
                                </div>\
                            </div>\
                        </div>\
                    </a>\
                </div>'
        })

        const image = new Vue({
            el: '#image-area',
            data: imageList
            })
    </script>
</body>

</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • karamarimo

    2018/06/11 23:26

    まずエラーが出ていないかということと、fetch が成功しているか確認してください。

    キャンセル

回答 3

checkベストアンサー

+1

まず v-bind:key="elm.key" ですが elm は文字列なので key なんていうプロパティは存在しません。
同様に elm.value も存在しないので下記のように書き換え。

<image-component 
    v-for="(elm, index) in imageList" 
    v-bind:elm="elm"
    v-bind:key="index">
</image-component>

次に image-component のテンプレートですが、href="elm.href" などが記述されてますが v-bind 付け忘れてますので、付けます。src="elm.src"も同様。

Vue.component('image-component', {
    props: ['elm'],
    template: '<div class="grid-item item animate-box" data-animate-effect="fadeIn">\
            <a v-bind:href="elm" class="image-popup" title="Name of photo or title here">\
                <div class="img-wrap">\
                    <img v-bind:src="elm" alt="" class="img-responsive">\
                </div>\
                <div class="text-wrap">\
                    <div class="text-inner popup">\
                        <div>\
                            <h2>Name of photo or title here</h2>\
                        </div>\
                    </div>\
                </div>\
            </a>\
        </div>'
})

最後にデータのフェッチとコンポーネントのマウントですが合体させましょう。
あと、エラーが出てるので分かると思いますが data プロパティは関数にして、戻り値をオブジェクトにしてやりましょう。

const image = new Vue({
    el: '#image-area',
    data: function () {
      return {
        imageList: []
      };
    },
    created: function () {
      fetch('/imageList/kyoto').then(function (response) {
          return response.json();
      }).then((function (json) {
          this.imageList = json.fileList;
      }).bind(this));
    }
})

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/12 00:50

    data をオブジェクトでなく関数でというのは、コンポーネントの場合必要なのは理解していますが、メインのインスタンスでも必要でしょうか?

    キャンセル

  • 2018/06/12 00:56

    fetch('/imageList/kyoto').then(function (response) {
    return response.json();
    }).then(function (json) {
    this.imageList = json.fileList;
    });
    ですが、 this.imageList は dataプロパティの値を示さないのでアロー関数で
    fetch('/imageList/kyoto').then(function (response) {
    return response.json();
    }).then( (json) => {
    this.imageList = json.fileList;
    });
    としたほうが良いと思います。

    キャンセル

  • 2018/06/12 00:57

    コンポーネントを使い回さないので要らないといえば要らないんですが、WARN 出て鬱陶しいですね。理由があってあえてオブジェクトにするとかでなければ、無難にベストプラクティスに従ったほうが良いのではないでしょうか。

    キャンセル

  • 2018/06/12 00:58

    あ、ほんとですね。ご指摘ありがとうございます。コンパイルしないみたいなので、`Function.prototype.bind` で束縛したほうが良さそうですね

    キャンセル

  • 2018/06/12 00:59

    データ取得は createdで行うのは賛成です。
    > mittsさん
    以下の箇所に created,mountedなどの説明書いてあります
    https://jp.vuejs.org/v2/guide/instance.html#インスタンスライフサイクルフック

    キャンセル

  • 2018/06/12 01:01

    >yhgさん
    束縛の件そうですね、そうでした scriptタグの中でしたね。

    キャンセル

  • 2018/06/12 01:04

    >yhgさん
    dataのwarningの件、jsfiddleで確認してたらワーニングでなかったので気にしてませんでした。
    ベストな方向に従うのは同意です。

    キャンセル

  • 2018/06/12 09:37

    yhgさん euledgeさん
    お二方ともコメントいただきありがとうございます!
    教えていただいた修正でステップ実行すると狙ったImageが表示されました
    ただ、ブレークポイントを置かないでリロードするとまた表示されなくなるのはfetchが非同期で行われているからでしょうか

    キャンセル

  • 2018/06/12 12:13

    確認したところDOMはきちんと生成されておりクリックすると画像が表示されたので要素はあるけれど描画がうまくされていないようです

    キャンセル

  • 2018/06/12 12:27

    Vueでの表示自体はうまくいったのでこのコメントをベストアンサーとさせていただきます
    お二方本当にありがとうございます

    キャンセル

+1

v-forで表示ができないという要点のみに注目して回答します。

<div class="grid" id="image-area">
  <image-component v-for="elm in list"></image-component>
</div>


というHTMLに対して vueインスタンスを下記のようにしているのですが、
v-forで使用している list という変数が存在しないので表示されていないのだと思います。

const image = new Vue({
                el: '#image-area',
                data: imageList
              })

vueインスタンスのところを下記のようにすればよいと思いますよ。

const image = new Vue({
                el: '#image-area',
                data: {
                  list: imageList
                }
              })

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/11 23:48

    アドバイスありがとうございます
    発生したエラーは教えて頂いた修正でなくなりました
    ただ依然、componentは表示するされないママです汗

    キャンセル

+1

thisのはまりパターンですね。

   var imageList;
   document.addEventListener('DOMContentLoaded', function () {
       fetch('/imageList/kyoto').then(function (response) {
            return response.json();
       }).then(function (json) {
           // ここで this.imagelist は 上記の var imagelistを指していません。
           this.imageList = json.fileList;
           // このようにして確認してみましょう
           console.log(imageList); // undefined つまりグローバルのimageList
           console.log(this.imageList); // この関数内で新たに定義された imageList
       });
   });

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Node.js

    1803questions

    Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

  • Vue.js

    655questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Express

    232questions

    ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。