Alguém pode me explicar o collection_select em termos claros e simples?

146

Estou analisando os documentos da API do Rails collection_selecte eles são horríveis.

O cabeçalho é o seguinte:

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

E este é o único código de exemplo que eles fornecem:

collection_select(:post, :author_id, Author.all, :id, :name_with_initial, :prompt => true)

Alguém pode explicar, usando uma associação simples (digamos, um Userhas_many Planse um Planpertence a User), o que eu quero usar na sintaxe e por quê?

Edit 1: Além disso, seria incrível se você explicasse como funciona dentro de um form_helperformulário ou em um formulário regular. Imagine que você está explicando isso a um desenvolvedor web que entende de desenvolvimento web, mas é "relativamente novo" para o Rails. Como você explicaria isso?

marcamillion
fonte
50
Sim. Essa é a documentação mais horrível que eu já vi
Jaseem
1
Para ser justo, a documentação é bastante ok, não apenas no FormBuildermas no FormOptionsHelper: api.rubyonrails.org/classes/ActionView/Helpers/...
amiuhle
1
Minha parte favorita é quando você usa collection_select em um formulário e isso altera toda a assinatura para que o objeto não faça parte da lista de parâmetros, mas, em vez disso, collection_select é chamado como um método no objeto. Não pense que eles mencionam que nos docs ...
user3670743
1
Este é um projeto de código aberto: se a documentação for ruim, temos apenas a culpa de nós mesmos; poderia ser incrível se apenas fizéssemos a nossa parte para torná-lo melhor. PRs pequenos / simples percorrem um longo caminho.
BKSpurgeon 18/02

Respostas:

305
collection_select(
    :post, # field namespace 
    :author_id, # field name
    # result of these two params will be: <select name="post[author_id]">...

    # then you should specify some collection or array of rows.
    # It can be Author.where(..).order(..) or something like that. 
    # In your example it is:
    Author.all, 

    # then you should specify methods for generating options
    :id, # this is name of method that will be called for every row, result will be set as key
    :name_with_initial, # this is name of method that will be called for every row, result will be set as value

    # as a result, every option will be generated by the following rule: 
    # <option value=#{author.id}>#{author.name_with_initial}</option>
    # 'author' is an element in the collection or array

    :prompt => true # then you can specify some params. You can find them in the docs.
)

Ou seu exemplo pode ser representado como o seguinte código:

<select name="post[author_id]">
    <% Author.all.each do |author| %>
        <option value="<%= author.id %>"><%= author.name_with_initial %></option>
    <% end %>
</select>

Isso não está documentado no FormBuilder, mas noFormOptionsHelper

alexkv
fonte
32
Isso é facilmente, uma das melhores explicações de uma estrutura complexa do Rails que eu já vi. Você usou linguagem clara, juntamente com construções básicas do Rails para solidificá-la. Muito obrigado !!
Marcamillion
2
Por que você o chamaria de "post [author_id]"?
Jaseem
@alexkv obrigado - você seria capaz de explicar o significado dos parâmetros: post, # field namespace e: author_id, # field name parameters (os dois primeiros) - não entendo o objetivo deles no esquema das coisas - pode ' eles são omitidos?
BKSpurgeon
1
O primeiro parâmetro pode de fato ser nulo. Se você estiver criando um formulário de objeto, será esse objeto para que a seleção tenha o mesmo espaço de nome que o restante do formulário. Se isso não for necessário, deixe o 1º como nulo. O segundo é o nome do controle, portanto, se for para atualizar um campo em um objeto (1º parâmetro), o nome do campo será o segundo parâmetro. Se você estiver criando algum formulário para outros usos, o segundo parâmetro é o que você deseja que o controle de formulário seja nomeado e identificado. Por que usar isso com zero primeiro? Provavelmente porque você deseja collection_select para as opções: prompt.
elc
1
Devo também observar que você pode obter o mesmo efeito alinhando um parâmetro de prompt em uma tag select_ que usa options_for_select, o que provavelmente responderia o mesmo que uma collection_select com um objeto nulo.
elc
21

Passei bastante tempo nas permutações das tags de seleção.

collection_selectcria uma tag de seleção de uma coleção de objetos. Mantendo isso em mente,

object: Nome do objeto. Isso é usado para gerar o nome da tag e para gerar o valor selecionado. Pode ser um objeto real ou um símbolo - neste último caso, a variável de instância com esse nome é procurada na ligação doActionController (ou seja, :postprocura por uma instância var chamada @postno seu controlador).

method: Nome do método. Isso é usado para gerar o nome da tag. Em outras palavras, o atributo do objeto que você está tentando obter da seleção

collection : A coleção de objetos

value_method : Para cada objeto na coleção, esse método é usado para o valor

text_method : Para cada objeto da coleção, esse método é usado para exibir texto

Parâmetros opcionais:

options: Opções que você pode passar. Eles estão documentados aqui , sob o título Opções.

html_options: O que for passado aqui, é simplesmente adicionado à tag html gerada. Se você deseja fornecer uma classe, ID ou qualquer outro atributo, ele será exibido aqui.

Sua associação pode ser escrita como:

collection_select(:user, :plan_ids, Plan.all, :id, :name, {:prompt => true, :multiple=>true })

No que diz respeito ao uso form_for, novamente em termos muito simples, para todas as tags que vêm dentro do form_for, por exemplo. f.text_field, você não precisa fornecer o primeiro objectparâmetro ( ). Isso é retirado da form_forsintaxe.

zsquare
fonte
2
Obrigado por dedicar um tempo ... o único problema é, com toda a honestidade, sua explicação não ajuda a esclarecer as coisas na minha cabeça. Você usou muitos termos na definição real. No entanto, eu aprecio que você dedique algum tempo - por isso, votei.
Marcamillion
4
Pelas razões claramente indicadas por Marcamillion, votei contra.
Jamie