Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

ViewComponentGemの紹介

Reactに触発されたViewComponentsは、ビューをレンダリングするためのマークアップを構築するために使用されるRubyオブジェクトです。 ViewComponentは、Railsで再利用可能、テスト可能、およびカプセル化されたビューコンポーネントを構築するためのフレームワークです。通常、再利用可能なビューは、パーシャルを使用してRailsで作成され、必要に応じてさまざまなビューでレンダリングされますが、ViewComponent gemの導入により、パーシャルをビューコンポーネントと交換できるため、より多くの利点が得られます。これらの利点について詳しく見ていきましょう。

いつ、なぜViewComponentsを使用する必要がありますか?

前に述べたように、ビューコンポーネントは再利用可能でテスト可能です。したがって、ビューを再利用する場合、または直接テストすることでメリットが得られる場合はいつでも適用できます。ドキュメントに記載されているように、ビューコンポーネントの利点には、次のようなものがあります。

  • パーシャルよりも約10倍高速です。
  • これらはRubyオブジェクトです。したがって、それらのinitializeメソッドは、ビューをレンダリングするために必要なものを明確に定義します。これは、他のいくつかのビューで理解して再利用しやすいことを意味します。さらに、Rubyコードの品質基準を適用できるため、バグのリスクが軽減されます。
  • これらは、ビューに加えてルーティングレイヤーとコントローラーレイヤーも実行する統合テストを必要とするRailsの従来のビューとは対照的に、単体テストを行うことができます。

ViewComponentの実装

ViewComponentsは、ViewComponent::Baseのサブクラスです。 app/componentsに住んでいます 。それらの名前は- Componentで終わります 、およびそれらは、受け入れるものではなく、レンダリングするものに基づいて名前を付ける必要があります。ビューコンポーネントは手動またはコンポーネントジェネレーターを介して生成できますが、gemファイルにgemを追加し、bundle installを実行する必要があります。 最初。

gem "view_component", require: "view_component/engine"

ジェネレータを使用するには、次のコマンドを実行します。

rails generate component <Component name> <arguments needed for initialization>

たとえば、学習プラットフォームで利用可能なクラス内コースのリストを表示するコンポーネントを生成する場合は、次のコマンドを使用できます。

rails g component Course course

courseを通過します このRubyオブジェクトを、表示する予定のコースで初期化し、Courseという名前を付けたため、コマンドの引数 コースをレンダリングするからです。なんて偶然だ!

ViewComponentGemの紹介

上記のように、コンポーネントとそれに対応するビューはapp/componentsに作成されます。 フォルダとテストファイル。

ViewComponentには、erbのテンプレートジェネレーターが含まれています 、haml 、およびslim テンプレートエンジンですが、デフォルトではconfig.generators.template_engineで指定されているテンプレートエンジンが使用されます。 。それでも、以下を使用して、好みのテンプレートエンジンを指定できます。

rails generate component Course course --template-engine <your template engine>

コースモデルと表示するいくつかのコースの作成に進みましょう。

rails g model Course title:string price:decimal location:string
rails db:migrate

コンソールでは、2つの新しいコースをすばやく作成できます。

Course.create(title: 'The Art of Learning', price: 125.00, location: 'Denmark')
Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')

course_component.rb 以下に示すように、ファイルはinitializeメソッドを使用して生成されます。

ViewComponentGemの紹介

コースのリストにルーティングするコースコントローラーを作成する必要があります。

rails g controller Courses index

routes.rbで ファイルの場合、以下を追加してルートルートを示します。

root 'courses#index'

これですべての設定が完了したので、次のステップはビューの作成です。これは、すでに生成されたcourse_component.html.erbで行われます。 。

<div>
  <h2><%= @course.title %></h2>
  <h4><%=  number_to_currency(@course.price, :unit => "€") %></h4>
  <h4><%= @course.location %></h4>
</div>

私たちのビューでは、@courseを使用して、コースのタイトル、価格、場所を表示します 変数。CourseComponentのinitializeメソッドですでに定義されています。これは、コントローラーメソッドで変数を作成し、ビューで使用できる場合と似ています。

コントローラがどのように機能するかを知っていると、対応するindex.html.erbにルーティングされます。 私たちのインデックスメソッドの。したがって、ここでコンポーネントをレンダリングします。

#app/views/courses/index.html.erb
<%= render(CourseComponent.new(course: Course.find(1))) %>

上記のように、CourseComponentの新しいインスタンスを、ビューでレンダリングする予定のコースで初期化することによってレンダリングします。このコースは@courseになります course_component.html.erbで利用できるようになった変数 ファイル。

このコンポーネントをコントローラーから直接レンダリングして、インデックスファイルビューをバイパスすることもできます。

class CoursesController < ApplicationController
  def index
    render(CourseComponent.new(course: Course.find(1)))
  end
end

選択した方法に関係なく、これはサーバーに表示されます:

ViewComponentGemの紹介

次のいずれかの方法で、追加のコンテンツをコンポーネントに渡すこともできます。

<%= render(CourseComponent.new(course: Course.find(1))) do %>
  container
<% end %>
<%= render(CourseComponent.new(course: Course.find(1)).with_content("container")) %>

ビューコンポーネントファイルでは、必要な場所にコンテンツを含めることができます。この場合、divを次のように編集して、クラスとして含めます。

<div class=<%= content %>>

追加コンテンツをレンダリングする上記のすべての方法により、以下の画像が生成されます。

ViewComponentGemの紹介

コレクションのレンダリング

コースのリスト全体をレンダリングしたい場合はどうなりますか? ViewComponentは、with_collectionを使用してこれを行う非常に簡単な方法を提供します 鬼ごっこ。 .newを使用してコンポーネントを初期化する代わりに 、.with_collectionを使用して初期化されます 、およびコレクションは、以下に示すように変数として渡されます。

CourseComponent.with_collection(Course.all)

これにより、次のようになります。

ViewComponentGemの紹介

with_collection_parameterもあります コレクションを別の名前でアドレス指定したい場合に利用できるタグ。

class CourseComponent < ViewComponent::Base
  with_collection_parameter :item

  def initialize(item:)
    @item = item
  end
end

上記の場合、コースパラメータはitemとしてアドレス指定されています 。したがって、対応するビューでは、@course @itemに置き換えられます 同じ結果が得られます。

追加のパラメーターをコレクションに追加することもできます。これらのパラメータは、コレクション内のアイテムごとに表示されます。 Buy Meを追加しましょう このメソッドを介して各アイテムにテキストを送信します。

#app/views/courses/index.html.erb
<%= render(CourseComponent.with_collection(Course.all, notice: "Buy Me")) %>
# app/components/course_component.rb
class CourseComponent < ViewComponent::Base
  with_collection_parameter :item
  def initialize(item:, notice:)
    @item = item
    @notice = notice
  end
end

app/components/course_component.html.erbに新しい段落を追加します 新しく追加された通知変数のテキストを示すファイル。

<p><a href='#'> <%= @notice %> </a></p>

これにより、次のようになります。

ViewComponentGemの紹介

最後に、コレクションの下に、ビュー内のアイテムに番号を付けるために有効にできるカウンター変数があります。 _counterを追加することで有効になります コレクションパラメータに追加し、initializeメソッドを介してビューで使用できるようにします。

#app/components/course_component.rb
def initialize(item:, notice:, item_counter:)
  @item = item
  @notice = notice
  @counter = item_counter
end

ビューでは、アイテムのタイトルの横にカウンターを追加します:

<h2><%= @counter %>. <%= @item.title %></h2>

カウンター現象をよりよく理解するために、コンソールから3番目のコースを生成しましょう。

Course.create(title: 'Understanding Databases', price: '100', location: 'Amsterdam')

これにより、次のようになります

ViewComponentGemの紹介

条件付きレンダリング

ViewComponentにはrender?があります フック。使用すると、ビューをレンダリングするかどうかを決定します。これを実装するために、100ユーロ以上の価格のコースに10%の割引を提供します。この目的のためのコンポーネントを作成しましょう。

rails generate component Discount item

ViewComponentGemの紹介

このコンポーネントは、以下に示すように、割引を表示する必要があるアイテムですでに自動的に初期化されています。

ViewComponentGemの紹介

したがって、discount_component.html.erb ファイルに、表示するテキストを追加します。

<p class="green"> A 10% discount is available on this course </p>

クラスgreenを追加することを躊躇しないでください cssファイルに追加し、好きな緑の色合いを割り当てます。さらに、discount_component.rbで ファイル、render?を追加します このコンポーネントをいつレンダリングするかを決定するメソッド。

def render?
  @item.price >= 100
end

これで、先に進んで、各コースをレンダリングするビュー内に割引コンポーネントをレンダリングできます。

# app/components/course_component.html.erb
<%= render(DiscountComponent.new(item: @item)) %>

これにより、次のようになります。

ViewComponentGemの紹介

それは素晴らしいことではありませんか?

ヘルパー

従来のRailsビューでは、ビューでメソッド名を呼び出すことでヘルパーを簡単にプラグインできますが、ビューコンポーネントでは動作が異なります。ビューコンポーネントでは、ヘルパーメソッドをビューで直接呼び出すことはできませんが、コンポーネントに含めることができます。すでにcourses_helper.rbがあります CoursesControllerの作成時に自動的に生成されたファイルなので、活用してみましょう。まず、これまでに何人の人がコースに登録したかを示すヘルパーメソッドを作成しましょう。値を価格の4分の1にしましょう:)

module CoursesHelper
  def count_enrollees(course)
    count = (course.price / 4).round()
    tag.p "#{count} enrollees so far"
  end
end

次に、ヘルパーを呼び出すコンポーネントを作成します。これは、ビューにレンダリングされるコンポーネントです。その中に、includeを追加します ヘルパーを含むステートメント。これにより、このコンポーネント内のヘルパー内の任意のメソッドを呼び出すことができます。

# app/components/enrollee_component.rb
class EnrolleeComponent < ViewComponent::Base
include CoursesHelper

  def total_enrollees(course)
    count_enrollees(course)
  end
end

最後のステップは、コースを表示するビューにEnrolleeComponentを追加することです。

# app/components/course_component.html.erb
<%= EnrolleeComponent.new.total_enrollees(@item) %>

EnrolleeComponentにはビューがないため、レンダリングワードを使用していないことに注意してください。その出力は、呼び出されたヘルパーメソッドの出力になります。これにより、次のようになります。

ViewComponentGemの紹介

ヘルパーは、アイコン、Gravitar、または選択したものに使用できます。 ViewComponentはヘルパーの使用を変更しません。コンポーネントでの呼び出し方法を変更するだけです。

before_renderメソッド

ViewComponentはbefore_renderを提供します コンポーネントがレンダリングされる前に呼び出すことができるメソッド。割引通知の横に星を追加しましょう。まず、スターをフェッチするヘルパーメソッドを追加します。 star.png 画像もダウンロードされ、app/assets/imagesに配置されています。 フォルダ。

#app/helpers/courses_helper.rb
def star_icon
  image_tag("/assets/star.png", width: "1%")
end

before_renderを追加しましょう このヘルパーメソッドを呼び出すDiscountコンポーネントへのメソッド。

# app/components/discount_component.rb
def before_render
  @star_icon = helpers.star_icon
end

上記のように、ヘルパーを呼び出す別の方法が導入されています。ヘルパーは、helpers.methodを使用して呼び出すこともできます component.rbファイル内。次に、DiscountComponentのrenderメソッドで、スターアイコンを入力します。これは、@star_iconから利用できるようになりました。 変数。

# app/components/discount_component.html.erb
<p class="green"> <%= @star_icon %> A 10% discount is available on this course </p>

これにより、次のようになります。

ViewComponentGemの紹介

before_renderに必ずしもヘルパーを使用する必要はありません 働く方法。ヘルパーを使用して、ヘルパーメソッドを呼び出すための別のアプローチを紹介しました。

プレビュー

アクションメーラーと同様に、ViewComponentを使用するとコンポーネントをプレビューできます。これは、最初にconfig/application.rbで有効にする必要があります ファイル。

config.view_component.preview_paths << "#{Rails.root}/lib/component_previews"

プレビューコンポーネントはtest/components/previewsにあります いくつかの異なる入力が渡されたコンポーネントをプレビューするために使用できます。DiscountComponentをプレビューします。

# test/components/previews/discount_component_preview.rb
class DiscountComponentPreview < ViewComponent::Preview
  def with_first_course
    render(DiscountComponent.new(item: Course.find(1)))
  end

  def with_second_course
    render(DiscountComponent.new(item: Course.find(2)))
  end
end

さまざまなシナリオでDiscountComponentをプレビューするために、2つのメソッドが追加されました。これを表示するには、https://localhost:3000/rails/view_componentsにアクセスしてください 、作成されたすべてのプレビューコンポーネントとそのメソッドが表示されます。以下に示すように、それらのいずれかをクリックして、それらがどのように見えるかを表示できます。

ViewComponentGemの紹介

上のビデオでわかるように、最初のコースでは割引コンポーネントがレンダリングされますが、2番目のコースでは何もレンダリングされません。なぜこれが起こったのか知っていますか? render? メソッドチェックが発生しました。最初のシナリオは、原価が100ユーロ(第1コース)を超えているが、第2コースのコストが100ユーロ未満の場合です。メソッド名は、ここで強調表示される前に原因を特定できるようにするために、よりわかりやすいものではありませんでした:)

プレビューは、show_previewsを使用して、どの環境でも有効または無効にできます オプションですが、開発およびテスト環境では、デフォルトで有効になっています。

# config/environments/test.rb
config.view_component.show_previews = false

JSとCSSの包含

「サイドカー」アセットまたはファイルと呼ばれることもあるコンポーネントと一緒にJavaScriptとCSSを含めることができます。これはまだ実験的な機能です。したがって、この記事ではその内部の仕組みについては詳しく説明しませんが、このViewComponent機能の詳細についてはこちらをご覧ください。

テンプレート

ビューコンポーネントのテンプレートは、いくつかの方法で定義できます。より簡単なオプションは、以下に示すように、ビューとコンポーネントを同じフォルダーに挿入することです。

ViewComponentGemの紹介

ここに表示されているように、すべてのコンポーネントとビューがapp/componentsにあります。 フォルダ。

別のオプションは、ビューと他のアセットをコンポーネントと同じ名前のサブディレクトリに配置することです。したがって、app/components フォルダには、component.rbがあります コンポーネントを格納するファイルと、別のcourse_component ビューcourse_component.html.erbを格納するフォルダー およびcourse_componentに関連する他のすべてのアセット。

ViewComponentGemの紹介

コマンドラインからこの方法でコンポーネントファイルを生成するには、--sidecar フラグが必要です:

rails g component Example name  --sidecar

これにより、cssファイルとjsファイルをコンポーネントフォルダーに追加できます。ViewComponentsは、callを定義することにより、テンプレートファイルなしでレンダリングすることもできます。 方法。この例は、次のセクションで提供されます。ここでは、スロットについて説明します。 。

スロット

slot を使用して、コンテンツの複数のブロックを1つのViewComponentに渡すことができます。 。 has_oneに似ています およびhas_many Railsモデルの属性、スロットはrenders_oneで定義されます およびrenders_many

  • renders_oneは、コンポーネントごとに最大1回レンダリングされるスロットを定義します:renders_one:header。
  • renders_manyは、コンポーネントごとに複数回レンダリングできるスロットを定義します:renders_many:titles。

利用可能なすべてのコースのヘッダーとタイトルをレンダリングするページが必要だと想像してみてください。これは、スロットを使用して実現できます 。 ListComponentを作成しましょう 、これには1回だけレンダリングされるヘッダーと、 TitleComponent が含まれます 、多くのタイトルをレンダリングします。

#app/components/list_component.rb
class ListComponent < ViewComponent::Base
  renders_one :header, "HeaderComponent"
  # `HeaderComponent` is defined within this component, so we refer to it using a string.
  renders_many :titles, TitleComponent
  # `titleComponent` will be defined in another file, so we can refer to it by class name.
  class HeaderComponent < ViewComponent::Base
    def call
      content_tag :h1, content
    end
  end
end

上記のコンポーネントでは、ヘッダーは1回レンダリングされると述べましたが、このページには多くのタイトルが含まれるため、タイトルは何度もレンダリングされます。 ListComponent内にHeaderComponentも定義しました。はい、これはViewComponentで可能です。クラスは別のクラス内で定義できます。また、「テンプレート」セクションで前述した呼び出しメソッドと、h1タグをレンダリングするためにHeaderComponentでどのように使用されているかにも注意してください。これにより、対応するビュー(html.erbファイル)が不要になります。 ListComponentに対応するHTMLファイルには、次のものが含まれます。

#app/components/list_component.html.erb
<div>
  <%= header %> <!-- renders the header component -->
  <% titles.each do |title| %>
    <div>
      <%= title %> <!-- renders an individual course title -->
    </div>
  <% end %>
</div>

htmlファイルには、ヘッダーが含まれており、コンポーネントに渡されたすべてのタイトルを繰り返し処理してレンダリングしました。ご覧のとおり、リストビューファイルでレンダリングするコンポーネントの名前を指定する必要はありません。私たちのスロットはそれを処理しました。したがって、それらをheaderとして識別します。 およびtitle

次のステップは、TitleComponentとそれに対応するHTMLファイルを作成することです。これは、渡されるすべてのタイトルに対してレンダリングされるものです。

# app/components/title_component.rb
class TitleComponent < ViewComponent::Base
  def initialize(title:)
    @title = title
  end
end
# app/components/title_component.html.erb
<div>
  <h3> <%= @title %> </h3>
</div>

最後に、index.htmlで ファイル、持っているものを一時的に消去して、ListComponentレンダリングに置き換えましょう。

#app/views/courses/index.html.erb
<%= render ListComponent.new do |c| %>
  <% c.header do %>
  <%= link_to "List of Available Courses", root_path %>
  <% end %>
  <%= c.title(title: "First title") %>
  <%= c.title(title: "Second title!") %>
<% end %>

ViewComponentGemの紹介

それでは、コースをコレクションとしてこのビューに渡しましょう。コレクションをスロットに渡すには、初期化に必要な変数を含むオブジェクトの配列としてコレクションを渡す必要があります。ご覧のとおり、渡されるすべてのコースは、title引数で初期化する必要があります。データベース内のすべてのコースのタイトルをハッシュの配列に抽出してレンダリングできます。

ViewComponentGemの紹介

これで、複数のタイトルのリストを1つのc.titlesに置き換えることができます。 コレクションの場合は、変数course_titlesを使用して定義したタイトルのハッシュに渡します。 。

# app/views/courses/index.html.erb
<% course_titles = Course.all.pluck(:title).map { |title| {title: title}} %>

<% c.titles(course_titles) %>

これにより、次のようになります。

ViewComponentGemの紹介

これはまさにスロットの仕組みです。単一のViewComponent内で複数のコンポーネントをレンダリングします。スロットは他のいくつかの方法でレンダリングできます。詳細については、こちらをご覧ください。

テスト

ビューコンポーネントのテストは、テストファイルに「view_component / test_case」を要求し、render_inlineを使用して実行されます。 レンダリングされた出力に対してアサーションが作成されるようにヘルパーをテストします。まず、DiscountComponentのテストから始めましょう。

require "test_helper"
require "view_component/test_case"

class DiscountComponentTest < ViewComponent::TestCase
  def test_render_component
    render_inline(DiscountComponent.new(item: "my item"))
  end
end

コマンドrails test test/components/discount_component_test.rbを使用してこのテストを実行すると 、次のエラーが発生します:

ViewComponentGemの紹介

これは、テストが適切なコンポーネントに到達していることを証明していますが、合格したアイテムは文字列ではなく価格プロパティを持つコースでなければならないため、適切な小道具が不足しています。また、render?があることも示しています。 このコンポーネントがレンダリングされる前にチェックされるメソッド。今すぐ正しい変数を渡しましょう。

def test_render_component
  course = Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')
  render_inline(DiscountComponent.new(item: course))
end

これは正常に実行されます。このテストにアサーションを追加してみましょう。

def test_render_component
  course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
  render_inline(DiscountComponent.new(item: course))
  assert_selector 'p[class="green"]'
  assert_text "10% discount"
end

このテストも合格です。このコンポーネントにはレンダリング条件があることを思い出してください。ただし、ViewComponentには、refute_component_renderedを使用して、コンポーネントがレンダリングされていないことをテストする方法も用意されているため、心配する必要はありません。 。 100ユーロ未満の価格のコースを使用してこれをテストできます。

def test_component_not_rendered
  course = Course.create(title: 'Organizing your Time', price: 55.00, location: 'London')
  render_inline(DiscountComponent.new(item: course))
  refute_component_rendered
end

このテストも合格です。

CourseComponentの別のテストを作成して、その中にネストされたすべてのコンポーネントをレンダリングすることをテストしましょう。

require "test_helper"
require "view_component/test_case"

class CourseComponentTest < ViewComponent::TestCase
  def test_component_renders_all_children
    course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
    render_inline(CourseComponent.new(item: course, notice: 'A new test', item_counter: 1))
    assert_selector("h2", text: "Organizing your Time")
    assert_selector("h4", text: "€155.00")
    assert_selector("h4", text: "London")
    assert_text("enrollees")
    assert_text("discount")
  end
end

このテストも合格です。 EnrolleeコンポーネントとDiscountコンポーネントも適切にレンダリングされていることをテストします。

スロットコンポーネントがあることを思い出してください。下の画像に示すように、1つのヘッダーと多くのタイトルがレンダリングされます。

ViewComponentGemの紹介

これをテストするために、レンダリングするヘッダーとタイトルを含むコードのブロックをコンポーネントに渡し、レンダリングされたコンポーネントに対してアサートできます。

require "test_helper"
require "view_component/test_case"

class ListComponentTest < ViewComponent::TestCase
  def test_renders_slots_with_content
  render_inline(ListComponent.new) do |component|
  component.header { "A Test List" }
  component.titles [{title: 'Test title 1'}, {title: 'Test title 2'}]
  end

  assert_selector("h1", text: "A Test List")

  assert_text("Test title 1")
  assert_text("Test title 2")
  end
end

このテストも合格します:)

Rspecテスト

テストについて上記で述べたすべてに加えて、推奨されるテストフレームワークがRSpecである場合、ViewComponentsのRSpecを有効にするためにいくつかの追加の構成を実行する必要があります。

# spec/rails_helper.rb
require "view_component/test_helpers"
require "capybara/rspec"

RSpec.configure do |config|
  config.include ViewComponent::TestHelpers, type: :component
  config.include Capybara::RSpecMatchers, type: :component
end

以下に示すように、DiscountComponentテストは、Rspecを使用して書き直し、再テストできます。

require "rails_helper"

RSpec.describe DiscountComponent, type: :component do

  it "renders the component correctly" do
    course = Course.create(title: 'Organizing your Time', price: 155.00, location: 'London')
    render_inline(DiscountComponent.new(item: course))
    expect(rendered_component).to have_css "p[class='green']", text: "10% discount"
    expect(rendered_component).to have_css "img[src*='/assets/star.png']"
  end
end

このテストはエレガントに合格します。ええ、私たちは星のアイコンを見ることができます。

結論

Railsアプリ用にいくつかのビューコンポーネントを作成すると、コードが読みやすくなり、不要な複雑さによるエラーが発生しにくくなるだけでなく、HTTPリクエスト中だけでなく、ビューを個別にテストできるようになります。ビューコンポーネントは既存のRailsアプリに簡単に組み込むことができ、ほとんどが再利用されるビューから始めるのが最善です。これまでにすべてを学んだので、これは簡単な作業になるはずです。それでも、ViewComponentの詳細については、ドキュメントまたはRubyDoc情報を参照してください。


  1. SOLID 原則の紹介

    Kriptofolio アプリ シリーズ - パート 1 ソフトウェアは常に変化の状態にあります。各変更は、プロジェクト全体に悪影響を与える可能性があります。したがって、重要なことは、すべての新しい変更を実装する際に発生する可能性のある損害を防ぐことです. 「Kriptofolio」(以前の「My Crypto Coins」)アプリを使用して、新しいコードを段階的に作成していきますが、それを良い方法で開始したいと考えています。私は自分のプロジェクトが確かな品質であることを望んでいます。まず、最新のソフトウェアを作成するための基本原則を理解する必要があります。それらはSOLID原則と呼ばれま

  2. Google の Project Fi:通話の未来の紹介

    ワイヤレス ネットワークの市場は過密状態であり、まだ多くの情報を見つけることができません。実際、携帯電話会社の評判は悪く、必ずしも間違った理由があるわけではありません。これは、それほど無制限ではないデータプラン、ますます上昇するデータ料金、およびデータ上限の減少に関連する論争によるものです.データプランに大金を費やすこととは別に、最大のカバレッジを提供するキャリアを把握する必要もあります.すでにたくさんのオプションが利用可能で、リストに追加されています。もう 1 つ、Google の Project Fi があります。 Google の Project Fi とは Project Fi は、