トピック表示ページが読み込まれたときにトピックIDを取得する

テーマにおいて、トピックの表示ページが読み込まれた際に、そのトピックの ID(可能であれば名前も)を取得する必要があります。

その後、この情報をテーマ内で使用して、forum-name/t/topic-name/id.json のようなエンドポイントへの呼び出しを通じて、トピックに関する情報を取得する API 呼び出しを行います。

トピック表示ページが読み込まれた際に、トピックの ID を取得するにはどうすればよいでしょうか?

明らかな方法では不十分です

明らかな解決策は、window.location.href や window.location.pathname を使用して URL から情報を取得することです。問題は、トピック表示ページの URL が異なる形式を取り得る点です。多くの場合、以下のような形式になります:
forum-name/t/topic-name/topic-id

もし常にこの形式であれば、window.location.pathname を使用すればうまくいきます(“t/topic-name/topic-id” が得られます)。

しかし、トピック表示ページの URL にトピックのカテゴリ順序が追加される場合があり、その場合の URL は以下のようになります:
forum-name/t/topic-name/topic-id/category-index

そのため、window.location.pathname を使用するだけでは不十分です。最後のパラメータがトピック ID なのかカテゴリインデックスなのかをプログラム的に判断できないからです。

正規表現を使用してこの問題を解決する方法もあるかもしれませんが、それには高度な正規表現のスキルが必要です。また、ページ上の要素を調べることで ID を取得する方法を jQuery で実現できる可能性もありますが、現時点ではうまくいっていません。

それはいつ/どこで発生しますか?見つけられませんでした。

とにかく、このトピックが読み込まれると、link 要素が含まれます。

<link rel="canonical" href="https://meta.discourse.org/t/get-topic-id-when-the-topic-show-page-loads/155620" />

そこから取得できるかもしれません。:slight_smile:

私のフォーラムでは、実際にはトピックをクリックしたときにこの現象のほとんどが発生します。カテゴリからトピックをクリックした場合、URL の末尾にはそのトピックが属するカテゴリ内の番号が含まれます。これは Discourse の設定によるものかもしれません。Meta ではこの現象は起きないように見えますが、私のフォーラムでこれが起きる特別な設定をしているつもりはありません。

ただし、この挙動に常に依存することはできません。なぜなら、カテゴリ内の最初のトピックの場合、カテゴリインデックス番号が URL に追加されないからです。現時点では、トピックがカテゴリインデックスに表示されるかどうかを判断する良い方法はないようです。

div から ID を取得する方法は可能ですが、残念ながら URL や他の直接的な手段から直接取得するよりもかなり遅くなります。他に解決策がない場合、その href の値を取得する jQuery の方法をご存知でしょうか?

お試しください

api.onPageChange((url, title) => {
    var res = url.match(/\/t\/(.*?)\/(\w+)/);
    if (res && res[2] > 0) {
        console.log(res[2]);
    }
});
「いいね!」 2

何を達成しようとしていますか?

これは機能するかもしれませんが、すべてのユーザー(匿名ユーザーを含む)がすべてのトピックページを表示するたびに追加の AJAX リクエストが発生するため、効率的ではありません。これにより、サーバーに不要な負荷が多数かかります。

実際にはそうではありません。トピックの URL は以下のようになります。

your.site.com/t/topic_title/topic_id/linked_post_number(optional)

デフォルトの Discourse インストールでは、URL にカテゴリー順序は含まれません。

解決しようとしている問題を説明していただければ、お手伝いできます。解決策の説明ではなく、問題そのものを説明してください。

「いいね!」 3

これで動作しました!ありがとうございます。ここで match メソッドを使用しているのは、URL からおそらく 3 番目の「/」の位置を取得するためだと推測します。なぜなら、ID は常に URL の 3 番目の「/」の後に現れるからです。URL の形式は「/t/name/id/otherstuff」となります。この処理を正規表現でどのように行っているのか、少し教えていただけますか?私の正規表現の学習にとって非常に役立つと思います。

情報をありがとうございます。どうやら「linked_post_number」が時々表示され、API 呼び出しを混乱させているようです。ここでは「オプション」と書かれていますが、これが表示されないように保証する方法はありますか?

ユーザーがトピック表示ページを訪れた際に、以下の処理を行いたいと考えています:

  1. そのトピックに関連付けられているすべてのタグをプログラムで取得する。ただし、一部のタグはユーザーの表示から隠されている点に注意してください。
  2. トピックページにボタンを配置し、クリックすると特定の隠しタグをトピックに追加する(まだタグが存在しない場合)、またはクリックすると隠しタグを削除する(タグが既に存在する場合)。

これはすべて、適切なトピック URL を API 呼び出しで使用できることを前提として、Admin API と JavaScript/jQuery を使用すれば straightforward に実現できます。

この種の処理を行う他の方法としては、プラグインを作成して、1. Ember、2. Rails、3. Discourse のコードベースを深く理解する必要があると考えられます。これを行う方法に関する主要な Discourse の投稿やドキュメントを確認しましたが、これら 3 つの要素を本当に理解する必要があるため、進捗はゆっくりです。そのため、現在は API アプローチに焦点を当てています。

サーバー負荷を軽減できる別の方法があれば、ぜひ聞きたいです。

もし単一のステップで解決できる問題であれば、喜んでコードのサポートをお伝えできたのですが、この場合は複数の手順が必要です。作業の進め方や、テーマコンポーネントの自作方法についてコンサルティングのご支援が必要な場合は、マーケットプレイスに投稿してください。こちらからご連絡いたします。

ありがとうございます。ただし、どの段階でもガイダンスを得られると助かります。例えば、API を使用する以外に、トピックに関連するすべてのタグをどのようにして把握すればよいでしょうか?

「いいね!」 1

トピックページを読み込むと、クライアント側で完全なトピックオブジェクトが読み込まれます。これを使用したい場合は、テンプレートに独自のコードを追加してそのデータを使用するか、クラスを reopen して読み込まれたデータを利用する必要があります(つまり、独自の計算プロパティを作成し、テンプレートでそれを利用することです)。追加の API 呼び出しなしに、必要なデータのほとんど(おそらく必要以上に)がクライアント側で読み込まれています。既存のテンプレートに独自のマークアップを追加するには、plugin-outlet が必要です。

上記で使った用語については、コードベースやメタ情報を確認すると役立つでしょう。時間的な制約の中でできる限りのサポートをさせていただきますので、つまずいた場合はお気軽にお尋ねください。では。

ありがとうございます。おっしゃる基本的な事項の一部は理解していますが、一つだけつまずいている点があります(きっと多くの方が同じところでつまずくでしょう)。

トピックの表示ページでは、/templates/components/topic-category.hbs というテンプレートが読み込まれます。これがカテゴリとトピックタイトルの下に表示されるタグを表示しています。

topic-category 内では topic.tags がリストアップされています。これがこの処理を進めるために必要な重要な情報です。

ここでつまずいています:その topic.tags の情報を JavaScript で取得するにはどうすればよいでしょうか?

例えば、単に topic.tags の内容を console.log で出力したい場合、どのようにすればよいでしょうか?


テンプレートのオーバーライド方法については理解しています。例えば、テーマ内で discourse/templates/components/topic-category.hbs というファイルを作成し、そこにテンプレートを再定義して、表示に対して必要な変更を加えることができます(こちら で説明されている、ファイルを分割する構造を使用しています)。

また、テーマ内では theme/initializers/initializer-file.js.es6 のような場所に JavaScript を配置する方法も理解しています。

そして、jQuery を使ってこれら 2 つを連携させることも可能です。例えば、テンプレート内で topic.tags を div 要素に埋め込み、その div の内容を jQuery で取得してイニシャライザーからアクセスするといった方法です。

しかし、それは回り道です。topic.tags の情報を直接取得し、解析・操作するにはどうすればよいでしょうか?

「いいね!」 1

まずは topic 全体を console.log して、必要なキーが何かを確認することをお勧めします。jQuery での操作は、コンポーネント内で行うべきであり、実際に行うべきでもあります。その際に役立つコンポーネントのフックとして didInsertElement があります。

状況から推測すると、topic.tags に依存する計算プロパティ(computed property)が必要で、それによって対象のコントローラーやコンポーネントで操作結果を返す形になるでしょう。これはテンプレート内で直接使用できます。また、この分野での学習はあきらめないでください。時間がかかりますが、予想以上に良い成果をもたらすはずです。

「コンポーネント内」とはどういう意味でしょうか?jQuery のコードはどのファイルに記述すればよいのでしょうか?(私のテーマでは、theme/initializers/initializer-file.js.es6 ファイルで jQuery を使用できますが、それは「コンポーネント内」とは言えないようです)

didInsertElement のコードはどのファイルに記述すればよいのでしょうか?

「いいね!」 1

この時点で、Ember のガイドを必ず確認してください。コンポーネントは、展開して他のものに接続できる機能のパッケージです。

「いいね!」 2

ありがとうございます、皆さん。とても役立つ情報です。ガイドと一般ドキュメントを確認しています。(i) jQueryコードと(ii) didInsertElementコードを記述すべきファイル名がわかると、さらに大変助かります。そのような具体的な情報が得られれば、今後のレビューを導く上で大きな助けになります。

「いいね!」 1

コンポーネントとはEmberコンポーネントのことです。

Emberのクラスを reopen して、コードを注入することができます。これを行っているプラグインやテーマを調べて、より具体的なイメージをつかんでみてください。

@JQ331 ご存知かと思いますが、私は Google で正規表現を見つけ、ここで試してみました!最適な結果を得るために、バリエーションを入力することができます。

「いいね!」 1

これはプラグインの領域に該当すると思われます。

もしタグが通常のユーザーに対して隠されている場合、追加の呼び出しがどのように役立ちますか?シリアライザーは、通常のユーザーに対してこれらのタグを隠すように設定されているはずです。もしそうでなければ、コンテンツセキュリティ上の穴を露呈していることになります。

フロントエンドのみの変更の制限を回避するためにこれほど手間をかけているのであれば、思い切ってプラグインとして実装することをお勧めします。そうすれば、追加の呼び出しを伴う煩雑な設定ではなく、一度にシリアライズすることが可能になります。

「いいね!」 1

なるほど、理解できました。シリアライザーの使い方をさらに学びたいと考えています。Discourse プラグインにおいてシリアライザーは最も重要なステップの一つですが、基本的な使用例をまとめたドキュメントはまだ見つけていません。そのようなドキュメントをご存知でしょうか?

「シリアライザーはそれらのタグを隠すべきである」とおっしゃいましたが、その意味を掴むために擬似コードをご提供いただけますか?(完全に動作しなくても構いません。概念を理解しようとしているだけです)

こちらです:

これはジャズ音楽と同じです。ベストプラクティスは、既存のオープンソースプラグインの世界に、すべての録音曲と同様にすでに存在しています。すべてを完璧に教えるガイドを期待しないでください。多くの場合、過去の事例から学び、そのリズム(ええと)を掴む必要があります。

#plugin から機能的に類似した機能、または目的の一部を実行するプラグインを見つけ、コードを検査して、どのように機能を実現しているかを確認してください。Discourse のソースコードも同様で、特に Discourse に関する究極の「ベストプラクティス」の源泉として利用できます。

既存のプラグインをローカルにクローンし、いくつかの変更を加えて実験してみてください。

シリアライザーは、コントローラーやモデルから送信されるデータをフィルタリングするだけであり、基本的な変換も行うことができます。

「いいね!」 1

ソースからの一例です:discourse/app/serializers/concerns/topic_tags_mixin.rb at 888e68a1637ca784a7bf51a6bbb524dcf7413b13 · discourse/discourse · GitHub

  def tags
    # `pluck` メソッドを `includes` と併用すると N+1 クエリが発生します
    tags = topic.tags.map(&:name)

    if scope.is_staff?
      tags
    else
      tags - scope.hidden_tag_names
    end
  end

ここから、非スタッフからは隠しタグが除外されていることがわかります。

「いいね!」 2