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

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

ただいまの
回答率

91.28%

  • JavaScript

    11717questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Monaca

    752questions

    「Monaca」はiOS、Android、Windows向けのアプリ開発に対応した、Cordovaベースのモバイルアプリ開発プラットフォームです。HTML5、JavaScriptといったWeb標準技術を用いてモバイルアプリ開発を行うことができます。

  • AngularJS

    495questions

    AngularJSはオープンソースのJavaScriptフレームワークです。ブラウザ上で動作するウェブアプリケーションの開発にMVCアーキテクチャを取り入れることを目的としています。

  • Onsen UI

    243questions

    HTML5で記述されたモバイルアプリの高速化、およびネイティブアプリライクなUIが作れるフレームワーク。 様々なJavaScriptフレームワークと併せて使用することができます。スマートフォン向けアプリ、Webサイトに必要なアニメーション、UI/UXを実装することが可能になります。

コントローラAで取得したデータを非同期的にコントローラBに表示したい

解決済

回答 1

投稿 編集

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

shori0128

score 10

前提・実現したいこと

いつもお世話になっています。
angularjs+onsenuiでアプリの開発をしております。

コントローラAでローカルから取得したデータをコントローラBでlazy-repeatで非同期的に表示させたいと考えております。
コントローラ間でデータの共有はできるようになったのですが、コントローラAでデータの取得が完了する前に、コントローラBで画面が表示されてしまうため、うまく表示されません。

なので、コントローラAで読み込みが終わったタイミングでコントローラBに終わったことを通知し、表示できればと思ったのですが、記述方法が分かりません。

以下に抜粋したソースを記載しますので、上記方法、もしくはその他に最適な方法をご存じの方がいらしたらご教示いただけますでしょうか。

該当のソースコード

'use strict';

const kijiFolder = 'kiji';
const gazoFolder = 'image';
const pageOption = {
  animation: 'slide'
};

let appDir, gazoDir, kijiDir, destination;

ons.bootstrap()
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  // 共通
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  .controller('commonCtrl', function ($scope, sharedScope) {
    $scope.item = [{
      src: 'icon/gif-load.gif',
      orgSrc: '',
      date: 'Loading...',
      size: 'Loading...'
    }];

    sharedScope.setScope('gazoCatalogCtrl', $scope.item);

    //■ データフォルダへアクセス
    resolveLocalFileSystemURLSync(cordova.file.dataDirectory)
      .then(function (fs) {
        appDir = fs;
        let process = [];
        process.push(getDirectorySync(appDir, kijiFolder));
        process.push(getDirectorySync(appDir, gazoFolder));
        return Promise.all(process);
      })
      .then(function (fsArray) {
        kijiDir = fsArray[0];
        gazoDir = fsArray[1];
        showGazo();
      })
      .catch(getErr);

    //■ 画像一覧の取得
    const showGazo = function () {
      const dirObj = gazoDir.createReader();

      readEntriesSync(dirObj)
        .then(function (fileArray) {
          //実画像(=jpg画像)のみ抽出してメタデータ取得
          return Promise.all(fileArray.filter(function (fileObj) {
              const ext = fileObj.name.split('.')[1];
              if (ext == 'jpg') return fileObj;
            })
            .map(function (fileObj) {
              return getMetadataSync(fileObj);
            }));
        })
        .then(function (fileArray) {
          //ファイル一覧をタイムスタンプ順(昇順)にソート
          fileArray.sort(function (a, b) {
              if (a.metadata.modificationTime > b.metadata.modificationTime) return -1;
              if (a.metadata.modificationTime < b.metadata.modificationTime) return 1;
              return 0;
            })
            .forEach(function (fileObj, index) {
              let localItem = [];
              const src = fileObj.toURL();

              localItem.push({
                src: src.replace('.jpg', '.thm'),
                orgSrc: src,
                date: fileObj.metadata.modificationTime,
                size: fileObj.metadata.size
              });

              $scope.item = localItem;
              sharedScope.setScope('gazoCatalogCtrl', $scope.item);
            });
        })
        .catch(getErr);
    };
  })
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  // 画像一覧
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  .controller('gazoCatalogCtrl', function ($scope, sharedScope) {
    $scope.delegate = {
      configureItemScope: function (index, itemScope) {
        const gazoCatalogCtrlScope = sharedScope.getScope('gazoCatalogCtrl');
        itemScope.item = {
          src: gazoCatalogCtrlScope[index].src,
          orgSrc: gazoCatalogCtrlScope[index].orgSrc,
          date: gazoCatalogCtrlScope[index].date,
          size: gazoCatalogCtrlScope[index].size
        };
      },
      calculateItemHeight: function (index) {
        return 91;
      },
      countItems: function () {
        return $scope.item.length;
      }
    };
  })
  .factory('sharedScope', function ($rootScope) {
    let sharedScope = {};

    return {
      setScope: function (key, value) {
        sharedScope[key] = value;
      },
      getScope: function (key) {
        return sharedScope[key];
      }
    };
  });
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/angular/angular.min.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>
  <script src="lib/onsenui/js/angular-onsenui.min.js"></script>
  <script src="ext/imageResizer/resize.js"></script>
  <script type="text/javascript" src="func.js"></script>
  <script type="text/javascript" src="promise.js"></script>
  <script type="text/javascript" src="index.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/bootstrap-4.0.0-beta-dist/css/bootstrap.min.css">
  <link rel="stylesheet" href="css/selectBox.css">
  <link rel="stylesheet" href="css/style.css">
</head>

<body>
  <ons-navigator id="appNavi" page="common.html"></ons-navigator>

  <ons-template id="tabNavi1.html">
    <ons-navigator id="tabNavi1" page="kijiCatalog.html"></ons-navigator>
  </ons-template>

  <ons-template id="tabNavi2.html">
    <ons-navigator id="tabNavi2" page="gazoCatalog.html"></ons-navigator>
  </ons-template>

  <ons-template id="tabNavi3.html">
    <ons-navigator id="tabNavi3" page="templateEdit.html"></ons-navigator>
  </ons-template>

  <ons-template id="gazoCatalog" ng-controller="gazoCatalogCtrl">
    <ons-page>
      <ons-list>
        <ons-list-item class="item" ons-lazy-repeat="delegate" modifier="chevron" tappable>
          <ons-row ng-click="openGazoEdit($index)">
            <ons-col width="80px">
              <img class="thumb" ng-src="{{item.src}}" original-src="{{item.orgSrc}}"></img>
            </ons-col>
            <ons-col>
              <header>
                <span class="title">スポニチ越中島にて記..</span>
                <span class="label">{{item.date}}</span>
              </header>
              <p class="etc">W: 1000 x H: 1000</p>
              <span class="etc">{{item.size}}</span>
              <span align="right">
                <input type="checkbox" class="check" name="select">
              </span>
            </ons-col>
          </ons-row>
        </ons-list-item>
      </ons-list>
    </ons-page>
  </ons-template>
</body>

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

すみません、実際に試してはいないのですが以下のようなコードでいかがでしょう?
スコープへの反映をループ内で行うのをやめました。

        .then(function (fileArray) {
          var localItem = [];
          //ファイル一覧をタイムスタンプ順(昇順)にソート
          fileArray.sort(function (a, b) {
              if (a.metadata.modificationTime > b.metadata.modificationTime) return -1;
              if (a.metadata.modificationTime < b.metadata.modificationTime) return 1;
              return 0;
            })
            .forEach(function (fileObj, index) {
              const src = fileObj.toURL();
              localItem .push({
                src: src.replace('.jpg', '.thm'),
                orgSrc: src,
                date: fileObj.metadata.modificationTime,
                size: fileObj.metadata.size
              });
            });
          $scope.item = localItem ;
          sharedScope.setScope('gazoCatalogCtrl', $scope.item);
        })

もしくは、例えば

1.コントローラAを生成し、データを取得し格納しておく要素をXで宣言する。
2.コントローラBを生成し、データを取得し格納しておく要素をYで宣言する。
3.コントローラBの要素Yについて、コントローラ外、例えばServiceCに格納されているオブジェクトZを参照するように代入する。※オブジェクトZは必ずオブジェクトか配列で宣言しておく
4.コントローラAでデータを取得し、要素Xにデータを代入していく。
5.データの取得が終わったら、要素XをServiceCのオブジェクトZに代入する。

上記手順でもなんとかなるかと思います。
コントローラBの要素YはオブジェクトZのアドレスを保持しているため、アドレス内の要素が書き換わったときにデータがコントローラBに反映されます。
アドレス内の要素の書き換えはコントローラAで行います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/12/11 18:31

    >akabee様
    いつもお世話になっております。
    いただいた回答をもとに早速改良を加えたのですが、うまく動作しません。。
    動作はしているのですが、取得したデータが動的に画面に表示されない状態です。
    なぜか、pushPage、popPage等で画面遷移を行うと取得データの一覧が表示されてくる状態です。
    データ取得後に、コントローラBに対して何かキックしてあげる必要があるのかと思ったのですが、原因はなんでしょうか。

    改良後のソースとHTMLを追記しました。
    ご確認いただけないでしょうか。

    キャンセル

  • 2017/12/11 19:08

    改良後のソースについて詳細な確認をしたわけではありませんが、そのような「値は反映できているようだが即座に画面表示されない」症状ですとまず$scope.$applyを試してみたくなりますね。
    参考→http://yutawatanabe.hatenablog.com/entry/angularjs-scope-apply

    AngularJSでは値が変更されると動的に画面表示が切り替わりますが、これは「AngularJSの中の何か(私も詳しくは理解していません)」が頑張ってコード内を監視していて、何か値が切り替わったら、それを画面表示にリアルタイムに反映すべく、頑張って見張ってくれているためです。
    ですが、「AngularJSの監視外」で値が切り替わった場合、即時に画面表示に反映されないということがあります。その場合、「画面表示に反映」ということを手動で行う必要があるのですが、それが「$scope.$apply」という処理です。

    実は私は「$scope」は好みに合わず利用していませんので自信がありませんが、

    sharedScope.setScope('gazoCatalogCtrl', $scope.item);

    の次に

    $scope.$apply

    でどうでしょう。改善されなければ申し訳ありません。

    キャンセル

  • 2017/12/12 14:37

    >akabee様
    お世話になっております。
    いただいた改善案を実施したところ。。。うまく動作するようになりました!
    大変勉強になりました。
    また別で回答いただける機会がありましたら宜しくお願いいたします。

    キャンセル

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

ただいまの回答率

91.28%

関連した質問

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

  • JavaScript

    11717questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Monaca

    752questions

    「Monaca」はiOS、Android、Windows向けのアプリ開発に対応した、Cordovaベースのモバイルアプリ開発プラットフォームです。HTML5、JavaScriptといったWeb標準技術を用いてモバイルアプリ開発を行うことができます。

  • AngularJS

    495questions

    AngularJSはオープンソースのJavaScriptフレームワークです。ブラウザ上で動作するウェブアプリケーションの開発にMVCアーキテクチャを取り入れることを目的としています。

  • Onsen UI

    243questions

    HTML5で記述されたモバイルアプリの高速化、およびネイティブアプリライクなUIが作れるフレームワーク。 様々なJavaScriptフレームワークと併せて使用することができます。スマートフォン向けアプリ、Webサイトに必要なアニメーション、UI/UXを実装することが可能になります。