LoginSignup
4
4

More than 5 years have passed since last update.

Webサービスのページ遷移をモーダルで表現する[開発メモ]

Last updated at Posted at 2018-05-20

はじめに

トビデモを作った際に、機能のキモとして、
ページの画面遷移を作成する編集画面の実装方法をメモしています。

はじめに

5月-20-2018-14-35-39-compressor.gif

Webサービスのページ遷移を表現するために、スクリーンショットをモーダルで繋げて表示しています。
この画面は全てGUIから作れるようになっています。

5月-20-2018-14-45-06-compressor (1).gif

全体の流れ

  1. ポップアップを配置
  2. ポップアップアイコンの編集ボタンをクリック
  3. 新しいHTML要素が生成される。表示階層以外はdisplay:none;
  4. 保存ボタンを教えてAjaxで保存する。

当初は実現方法が思い浮かばずだいぶ悩みましたが、
後から見るとだいぶシンプルですね!以下主要なポイントを紹介しています。

考案時のメモ:

IMG_20180523_223631.jpg

編集画面の設計

ページ遷移を編集する方法として、必要に応じてHTMLの要素を追加し、
それ以外はdisplay:none;にすることで、サーバーサイドとのやりとりを最低限にストレスなく編集できるよう気を使いました。
以下が実際に生成される要素です。

スクリーンショット 2018-05-20 15.01.40.png

当初はその発想がなく、ページを増やすごとに今あるデータを非同期で保存する実装を考えていました。 
ページ遷移の度にリクエストが発生しますし、一つ前の階層に戻って編集し直すときも、
データベースから情報を取って再描画しなくてはならず、実装に手間がかかるので、今思っても前者の実装で正解だったと思います。


// ポップアップ先の要素を作成
    $('.edit_popup_target')
            .on(
                    'click',
                    function() {

                        var _this = $('.flag_now_edit');

                        var page_level_num = TRANSITION_MANAGER[TRANSITION_MANAGER.length - 1];
                        var page_level = page_level_num.slice(0, 1);
                        var next_page_level = Number(page_level) + 1;
                        var page_num = page_level_num.slice(2, 3);
                        var next_page_num = $('[data-dropzone^="'
                                + next_page_level + '"]').length;

                        if ($(_this).attr("data-popuptarget") == undefined) {

                            if (next_page_num != 0) {
                                next_page_num += 1;
                            } else {
                                next_page_num = 1;

                            }
                            var this_dropzone_id = page_level + "." + page_num;
                            var next_dropzone_id = next_page_level + "."
                                    + next_page_num;

                            $('.mapEreaWrapper')
                                    .append(
                                            '<div class="maperea" data-dropzone="'
                                                    + next_dropzone_id
                                                    + '"><img src ="./../../img/default-none.png"></div>');
                            $(_this).attr("data-popuptarget", next_dropzone_id);

階層管理の設計

スクリーンショット 2018-05-20 15.17.16.png

それぞれのページは以下情報を保持します。
- ページ番号
- 遷移ターゲットページ番号
- アイテムの情報(メモなど画面を説明する)
- 背景画像

ページ番号は1.1から始まり同じ階層では1.2,1.3と増えます。
新しい階層では2.1のように冒頭の数字を繰り上げ、2.1,2,2とと続きます。
ページのアイテムは、2.1.1,2.1.2,のようにページ番号の後に連番が続きます。

この管理番号を元にして、保存されたデータを再描画することになります。

戻るボタンの実装

編集画面ですので前の画面に戻れないとなりません!
スクリーンショット 2018-05-20 15.32.17.png

頭をひねった結果、ページを移動する際に配列にページ番号を書き込むことで、
ページの遷移履歴を管理しています。


// ------------グローバル変数--------------
    TRANSITION_MANAGER = [ '1.1' ]; // 現在地TRANSITION_MANAGER[TRANSITION_MANAGER.length
    // - 1]
    // ---------------------------------------

例えばページを一つ戻るときは、以下のようにページ管理用配列(TRANSITION_MANAGER)からデータを取得しています。

// ページを一つ戻る
    $('.backToBeforePage')
            .on(
                    'click',
                    function() {

                        if (TRANSITION_MANAGER.length != 1) {
                            $(
                                    '[data-dropzone="'
                                            + TRANSITION_MANAGER[TRANSITION_MANAGER.length - 1]
                                            + '"]').hide();
                            $(
                                    '[data-dropzone="'
                                            + TRANSITION_MANAGER[TRANSITION_MANAGER.length - 2]
                                            + '"]').show();
                            // ACTIVE_PAGE_NUMBER =
                            // TRANSITION_MANAGER[TRANSITION_MANAGER.length-2];
                            TRANSITION_MANAGER.pop();
                            changePageTransitionView();
                        }
                        ;
                        return false;
                    });

データの保存

  • ページ番号
  • 遷移ターゲットページ番号
  • アイテムの情報(メモなど画面を説明する)
  • 背景画像

などをAjaxで一気に保存します。
ネストすると綺麗に送付できたと思うのですが、

Jsonデータに対応したBeanを用意しておくと、Spring Bootがよしなに受け取ってくれるのですが、
ネスト構造の表現方法がわからなかったので、以下のように2,3項目だけ送付したいのに、
Beanで用意した全ての項目を用意しています。後ろ髪を引かれますが今回は回避策があったので諦めました。

また、保存したデータをアップデートする処理も書くつもりでしたが、
Spring boot では データベースのテーブル側のキーに指定している情報が一致していれば
同じデータとして更新してくれるので、意図せず実装できてしまいました。


  item_stat_array.push({
                                id : linkid + "_" + dropzone,
                                type : "drop-zone", // common
                                itemid : "0", // common
                                top : "0", // common
                                left : "0", // common
                                color : "0", // common
                                size : "0", // popup,tooltip
                                title : "0", // tooltip
                                target : "0", // popup
                                width : "0", // sticky
                                height : "0", // sticky
                                text : "0", // sticky
                                dropzone : dropzone, // dropzpne
                                img : img
                            // dropzone

ちょっと長いですが以下全文。


$(document)
            .on(
                    'submit',
                    ".save_to_server",
                    function(e) {
                        e.preventDefault();

                        $(".maperea").show(); // disply:noneだとtop,left が取れない

                        var demoname = $('.demo-name-val').val();
                        if (demoname === undefined || demoname === null | demoname.replace(/[\t\s ]/g, '').length < 1){
                            demoname = "-";
                        }
                        var item_stat_array = [];

                        var maperea_array = $('.maperea');
                        var maperea_leng = $('.maperea').length;
                        $(maperea_array[i]).attr('data-dropzone');
                        var linkid = $('#linkid').text();
                        var xsrf = $.cookie('XSRF-TOKEN');

                        for (var i = 0; i < maperea_leng; i++) {

                            var dropzone = $(maperea_array[i]).attr(
                                    'data-dropzone');
                            var img = $(maperea_array[i]).attr('data-img')
                            if (img === undefined || img === null) {
                                img = "0";
                            }
                            // var id = linkid + "_" +dropzone; //データベース保管時の主キー

                            item_stat_array.push({
                                id : linkid + "_" + dropzone,
                                type : "drop-zone", // common
                                itemid : "0", // common
                                top : "0", // common
                                left : "0", // common
                                color : "0", // common
                                size : "0", // popup,tooltip
                                title : "0", // tooltip
                                target : "0", // popup
                                width : "0", // sticky
                                height : "0", // sticky
                                text : "0", // sticky
                                dropzone : dropzone, // dropzpne
                                img : img
                            // dropzone

                            });
                        }

                        var targetArray = $('.dropped');
                        var item_num = targetArray.length;
                        item_num - 1;

                        for (var i = 0; i < item_num; i++) {
                            var target = targetArray[i];

                            if (target.className.indexOf('_tooltip') != -1) {

                                var itemid = $(target).attr("data-item");
                                var top = $(target).position().top;
                                var left = $(target).position().left;
                                var color = $(target).attr(
                                        "data-tooltipIconColor");
                                var size = $(target).attr(
                                        "data-tooltipIconSize");
                                var title = $(target).attr("title");
                                // var id = linkid + "_" + itemid;
                                // //データベース保管時の主キー

                                item_stat_array.push({
                                    id : linkid + "_" + itemid,
                                    type : "_tooltip", // common
                                    itemid : itemid, // common
                                    top : top, // common
                                    left : left, // common
                                    color : color, // common
                                    size : size, // popup,tooltip
                                    title : title, // tooltip
                                    target : "0", // popup
                                    width : "0", // sticky
                                    height : "0", // sticky
                                    text : "0", // sticky
                                    dropzone : "0", // dropzpne
                                    img : img
                                // dropzone
                                });

                            } else if (target.className.indexOf('_popup') != -1) {

                                var top = $(target).position().top;
                                var left = $(target).position().left;
                                var itemid = $(target).attr("data-item");
                                var color = $(target).attr("data-popupColor");
                                var size = $(target).attr("data-popupSize");
                                var target = $(target).attr("data-popuptarget");

                                item_stat_array.push({
                                    id : linkid + "_" + itemid,
                                    type : "_popup", // common
                                    itemid : itemid, // common
                                    top : top, // common
                                    left : left, // common
                                    color : color, // common
                                    size : size, // popup,tooltip
                                    title : "0", // tooltip
                                    target : target, // popup
                                    width : "0", // sticky
                                    height : "0", // sticky
                                    text : "0", // sticky
                                    dropzone : "0", // dropzpne
                                    img : img
                                // dropzone
                                });

                            } else if (target.className.indexOf('_sticky') != -1) {

                                var top = $(target).position().top;
                                var left = $(target).position().left;
                                var width = $(target).width();
                                var height = $(target).height();
                                var itemid = $(target).attr("data-item");
                                var color = $(target).attr("data-stickycolor");
                                var text = $(target).children(".textsticky")
                                        .val();

                                item_stat_array.push({
                                    id : linkid + "_" + itemid,
                                    type : "_sticky", // common
                                    itemid : itemid, // common
                                    top : top, // common
                                    left : left, // common
                                    color : color, // common
                                    size : "0", // popup,tooltip
                                    title : "0", // tooltip
                                    target : "0", // popup
                                    width : width, // sticky
                                    height : height, // sticky
                                    text : text, // sticky
                                    dropzone : "0", // dropzpne
                                    img : img
                                // dropzone
                                });

                            }

                        }

                        $(".maperea").hide();
                        $(
                                '[data-dropzone="'
                                        + TRANSITION_MANAGER[TRANSITION_MANAGER.length - 1]
                                        + '"]').show();

                        if (window.FormData) {

                            var ajaxUrl = "/edit/uploaditemstat" + "?name=" + demoname ;

                            $
                                    .ajax(
                                            {
                                                type : "POST", // HTTP通信の種類
                                                url : ajaxUrl, // リクエストを送信する先のURL
                                                dataType : "json", // サーバーから返されるデータの型
                                                data : JSON
                                                        .stringify(item_stat_array),
                                                contentType : 'application/json; charset=utf-8',
                                                "beforeSend": function(xhr, setting) {
                                                    $(".btn").attr('disabled', true);
                                                    $(".item-uploading-animation").removeClass("nodispy");
                                                },
                                                headers : {
                                                    'X-XSRF-TOKEN' : xsrf
                                                },
                                            })

                                    .done(
                                            function(data) { // Ajax通信が成功した時の処理

                                                var yourLink = "/show?id=";
                                                var yourLinkID = $("#linkid")
                                                        .text();

                                                var demoname = $(".demo-name-val").val();
                                                if(demoname==null ||demoname== "") {demoname = "無題のトビデモ"}

                                                $(".yourLink").attr("href",
                                                        yourLink + yourLinkID);
                                                $(".yourLink").text(
                                                        demoname);
                                                $(".yourLink").attr('target',
                                                        '_blank');
                                                $('.upload-item-done').slideDown(1500);

                                                $(".btn").attr('disabled', false);
                                                $(".item-uploading-animation").addClass("nodispy");


                                            }).fail(
                                            function(XMLHttpRequest,
                                                    textStatus, errorThrown) { // Ajax通信が失敗した時の処理
                                                $(".btn").attr('disabled', false);
                                                $(".item-uploading-animation").addClass("nodispy");
                                                $('.upload-item-failed').slideDown(1500);
                                                return false;

                                            });
                        } else {
                            alert("アップロードに対応できていないブラウザです。");
                        }

                    });

まとめ

以上です!

ここに書いた以外にもアイテムの座標や画面描画など細かいところも合わせて、
数週間かかってしまいました。
しかし、当初は無理かも...と思っていたのですが、やればできるものですね!

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4