86691


最近htmlなどの学習に取り組み始めたばかりの初学者です。  
元々Rubyの経験が少しあったため、Rubyでのメソッドの記述はできたのですが、htmlやJavaScriptの理解が難しく、うまくページに反映できず困っています。

htmlでファイルの読み込みを行った際、onchangeでメソッドを実行したいのですが、

  * そのままRubyのメソッドを呼び出したい
  * ページ遷移をしないまま実行したい

と考えています。

[code]

    <script type="text/javascript">
    function analyze(file){
    〜
    }
    </script>
    
    <input type="file" id="file" accept=".html" onchange="analyze(file)">
    
[/code]

のようなコードにおいて、「〜」部でRubyのメソッド(例:sample(file))を呼び出すためにはどうしたら良いでしょうか。

(追記)  
form_withを用いてメソッドに渡す方法ではできたのですが、そうするとファイルの選択フォームが「ファイル未選択」状態になってしまうので困っています。(ファイル名を表示しておきたいため)  
JavaScriptに不慣れなことと、Rubyでのコードは完成しているため、それを活用して実行したいと考えています。

○実行したいこと

  * htmlのチャットログを受け取り、発話者を抜粋したものを表示
  * 重要な発話者のみにチェックをつけ、その発話者のチャットのみを残したファイルを返す

ひとまずファイルの内容を一旦DBに保存することでこれは実現したのですが、セキュリティ的なことも考え、叶うならデータベースに入れることなく再編集や行いたいため、onchangeで出来ないかと検討しています。


CC BY-SA 4.0
2022-03-03T09:17:16.523
86670
2022-03-05T02:03:00.800
2022-03-03T16:18:42.233
3060
51668
question
javascript ruby html
scriptタグ内でRubyのメソッドを実行したい


**単純に実行することはできません。**

Webアプリの作成は初めてですか？もし、そうなら、最初に学ぶべき重要なことがあります。それは、サーバーサイドとクライアントサイドの隔絶です。

Webアプリではサーバーサイドとクライアントサイドという二つの環境が織り成すハーモニーです。この二つ環境は、実行する場所(実際のコンピューター)も違えば実行される言語も違います(言語は選択肢によっては同じ場合もあります)。この二つを混ぜて実行されることは原則できません。Webアプリを作る場合は、常に、サーバーサイドとクライアントサイドを意識し、それぞれの環境での制限は何か、そしてどちらで実行されるのかを意識する必要があります。

では、Railsを例にしながら実際に説明していきます。

`<script>`タグの中で実行されるコードはクライアントサイドのJavaScriptです。その中でサーバーサイドのRubyのコードを書くことはできません。Rubyのクラスやメソッドを書いても、JavaScriptはそんなものは知らないので実行出来ません。逆も同じです。Rubyのコード内でJavaScriptのコードを呼び出すことはできません。
**これは言語が異なるからと言う理由では無く、サーバーサイド側の言語をJavaScriptにしても、クライアントサイド側の言語をOpal(Rubyと互換性が高いAltJS)にしても、互いにそれぞれのコードを書くことはできません。**
まずは、この原則を覚えてください。

それぞれは別々という話でしたが、そこから、データのやり取りはどうするのか考える必要があります。大きく分けて三つあります。例として、`sample(file)`
でファイルを解析した結果を`<pre>`タグ内に出すというかたちで、説明していきます。

## POSTで投げて実行結果が含まれたHTMLを生成する

Railsが生成するHTMLはERBで書いているかとも思います。この中ではRubyを実行出来ます。もっと正確に言うと、Rubyが実行された結果のHTMLをクライアントサイドに渡しているのです。ですので、Rubyで必要なことはERBの中で実行して、出力するためのファイルに入れてしまいましょう。

formは同じページに対してPOSTするものです。コントローラーでは@fileにアップロードされたファイルが読み込まれるとします。

[code]

    <%= form_with(id: 'logform', ...) do |form| %>
    ...
    <input type="file" id="file" accept=".html" onchange="document.logform.submit()"
      value="<%= @file&.original_filename%>">
    ...
    <% end %>
    ...
    <pre><%= @file && sample(@file) %></pre>
    
[/code]

ERBを上のような感じにします。valueにファイルの名前を入れておくことでファイル名もそのまま表示されるようになります。

`<% ...
%>`の部分はサーバーサイドで実行されると言うことを常に意識してください。実行され、得られたHTMLがクライアントサイドに渡った後、サーバーサイドはその内容を変更することはできません。しかし、再度読み込まれるのであれば、また実行する事ができますので、Rubyの実行結果を反映したHTMLを渡すことができるというわけです。

## Ajaxを使用する

最初の方法は昔ながらの方法でした。今ほどJavaScriptが協力では無かった時代は(JavaAppletやFlash等の遺物を付かない限り)一般的な方法でした。しかし、各ブラウザがXMLHtttpRequestを実装したところで、Porotype.jsやjQueryの出現でAjaxという技術が急速に広まり、いまや一般的になっています。

Ajaxを使うためと技術はいくつかあります。古くはXMLHtttpRequestでしたが、余り使い勝手が良い物ではなかったため、jQuer.ajaxが広く使われていました。IEというレガシーなブラウザーはサポートしないモダンブラウザーのみをターゲットにした現代的なプログラムであれば[Fetch
API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)がいいでしょう。

Ajaxでのやり方は選択肢も多いですし、一から説明するには余りにも長くなるのでここでは書きません。本格的にWebアプリを作るには今や必須の技術なので、別途学ぶことをお勧めします。

## フレームワークのAjax機能を使って可能な限りシームレスにする

Ajaxは色々あると言いましたが、フレームワークにAjaxを使ってなるべくシームレスに実行環境を交互に行き交うようにする物があります。

Railsにも簡単にできる機能があります。それはrails-ujs.jsを使用したremoteです。form_withで`remote:
ture`を設定していることで使用できるようになります。

[code]

    <%= form_with(action: '/sample_file', remote: true, ...) do |form| %>
    ...
    <input type="file" id="file" accept=".html" onchange="document.logform.submit()">
    ...
    <% end %>
    ...
    <pre id="logpre></pre>
    
[/code]

最初の例と似ているように見えますが、inputのvalueやpreの中身をRubyのコードで入れておこうとはしていません。代わりにpreにはidが付いています。このリモートで、呼び出される側のsample.js.erbを次のようにします。

[code]

    document.getElementById('logpre').textContent = "<%= @file && sample(@file) %>"
    
[/code]

formでのPOSTはAjaxとしてページ遷移せずに行われます。それは、最終的に上のERBで出力した結果のJavaScriptが返されるので、それを実行するという仕組みです。詳しくは[Railsガイド](https://railsguides.jp/working_with_javascript_in_rails.html)を参考にしてください。

* * *

いずれの場合でも、ちょっとコピペしたら動作するという物ではありません。どんな方法を取るにしても、RailsガイドやRailsチュートリアル、その他の入門書を読んで、基礎知識を身につけた上で、自分が実現したいことに対するベストな方法を選んで実装する必要があります。がんばってください。


CC BY-SA 4.0
2022-03-05T02:03:00.800
86691
2022-03-05T02:03:00.800
7347
86670
answer