Por que o AngularJS inclui uma opção vazia no select?


652

Trabalho com o AngularJS há algumas semanas, e o que realmente me incomoda é que, mesmo depois de tentar todas as permutações ou a configuração definida na especificação em http://docs.angularjs.org/api/ng .directive: select , ainda recebo uma opção vazia como o primeiro filho do elemento select.

Aqui está o Jade:

select.span9(ng-model='form.type', required, ng-options='option.value as option.name for option in typeOptions');

Aqui o controlador:

$scope.typeOptions = [
    { name: 'Feature', value: 'feature' },
    { name: 'Bug', value: 'bug' },
    { name: 'Enhancement', value: 'enhancement' }
];

Finalmente, aqui está o HTML que é gerado:

<select ng-model="form.type" required="required" ng-options="option.value as option.name for option in typeOptions" class="span9 ng-pristine ng-invalid ng-invalid-required">
    <option value="?" selected="selected"></option>
    <option value="0">Feature</option>
    <option value="1">Bug</option>
    <option value="2">Enhancement</option>
</select>

O que preciso fazer para me livrar dele?

PS: As coisas funcionam sem isso também, mas parece estranho se você usar select2 sem seleção múltipla.

Respostas:


646

O vazio optioné gerado quando um valor referenciado por ng-modelnão existe em um conjunto de opções passadas para ng-options. Isso acontece para impedir a seleção acidental de modelos: o AngularJS pode ver que o modelo inicial está indefinido ou não está no conjunto de opções e não deseja decidir o valor do modelo por conta própria.

Se você quiser se livrar da opção vazia, basta selecionar um valor inicial no seu controlador, algo como:

$scope.form.type = $scope.typeOptions[0].value;

Aqui está o jsFiddle: http://jsfiddle.net/MTfRD/3/

Em resumo: a opção vazia significa que nenhum modelo válido está selecionado (por válido, quero dizer: no conjunto de opções). Você precisa selecionar um valor de modelo válido para se livrar dessa opção vazia.


7
Eu tentei com ambos $ scope.form.type = ''; e $ scope.form.type = $ scope.typeOptions [0], no entanto, ainda vejo isso - <option value = "?" selected = "selected"> </option>
Sudhanshu 29/09/12

5
Às vezes, realmente não faz sentido ter esses dados no JS. Se foi o servidor que tomou a decisão sobre o que estava nessa seleção, por que o JS deve duplicá-lo?
Heneryville

11
Infelizmente, isso não funciona se você estiver usando uma matriz e nullé um dos seus valores.
precisa saber é o seguinte

6
É possível adicionar um texto no interior do vazio <option>assim tat angular gerado algo como<option value="?">Select an option</option>
RPDeshaies

3
@ Tareck117 acabou de escrever <option value="">Select an option</option>o valor nulo da lista de opções. Não há necessidade de caracteres?
Sebastian

235

Se você deseja um valor inicial, consulte a resposta de @ pkozlowski.opensource, que também pode ser implementada na FYI na exibição (e não no controlador) usando ng-init:

<select ng-model="form.type" required="required" ng-init="form.type='bug'"
  ng-options="option.value as option.name for option in typeOptions" >
</select>

Se você não deseja um valor inicial, "um único elemento codificado, com o valor definido como uma sequência vazia, pode ser aninhado no elemento. Esse elemento representará a opção nula ou" não selecionada "":

<select ng-model="form.type" required="required"
  ng-options="option.value as option.name for option in typeOptions" >
    <option style="display:none" value="">select a type</option>
</select>

4
@ hugo, violino de trabalho: jsfiddle.net/4qKyx/1 Observe que "selecione um tipo" é mostrado, mas não há valor associado a ele. E depois de selecionar outra coisa, você não a verá novamente. (Teste no Chrome.)
Mark Rajcok 9/13/13

1
@MarkRajcok - excelente sugestão! Eu tropecei em outra questão usando o seu exemplo com a diretiva de transclusão. Você poderia dar uma olhada? plnkr.co/edit/efaZdEQlNfoDihk1R1zc?p=preview
Jan-Terje Sørensen,

2
Problema com isto é que você ainda pode selecionar com o teclado, consulte stackoverflow.com/a/8442831/57344 solução
HaveAGuess

2
Isso pode funcionar para agora, mas a documentação angular recomenda especificamente contra o uso ng-init para qualquer coisa além ng-repeat - ver docs.angularjs.org/api/ng/directive/ngInit
Ed Norris

3
Não funciona no IE10, o "selecione um tipo" está sempre visível e selecionável.
Robin

121

Angular <1.4

Para qualquer pessoa que trate "null" como valor válido para uma das opções (imagine que "null" seja um valor de um dos itens em typeOptions no exemplo abaixo), achei a maneira mais simples de garantir que a adição automática fosse feita. opção está oculta é usar ng-if.

<select ng-options="option.value as option.name for option in typeOptions">
    <option value="" ng-if="false"></option>
</select>

Por que ng-if e não ng-hide? Como você deseja que os seletores de css que segmentam a primeira opção acima, selecione a opção "real", não a que está oculta. Torna-se útil quando você usa o transferidor para o teste e2e e (por qualquer motivo) usa o by.css () para direcionar as opções selecionadas.

Angular> = 1.4

Devido à refatoração das diretivas de seleção e opções, o uso ng-ifnão é mais uma opção viável, portanto você precisa voltar ng-show="false"a fazê-lo funcionar novamente.


5
Deve ser a resposta aceita, porque esse é o comportamento visual correto para o select. Você coloca size="5"os atributos seleciona e pode ver que não há nada selecionado! Como em um elemento padrão <select>! Eu não consigo entender porque angular está fazendo tal coisa para seleciona sem multipleatributo ...
Sebastian

1
Sim, eu concordo, esta deve ser a resposta aceita. Este é o 'caminho angular'. O ng-if remove o elemento options do dom (quando false), portanto esta solução também funciona em todos os navegadores sem codificação 'hacky' (ao contrário do ng-hide ou ng-show).
precisa saber é o seguinte

2
@Sander_P, por definição, esta solução é uma codificação "hacky" na minha opinião (colocar algo no dom para enganar o angular e eliminá-la), mas você está certo de que ainda é a "melhor" solução. A solução "ainda melhor" seria permitir uma seleção friggin que não tem valor! Que diabos? Não é como se este fosse um cenário de ponta.
Dudewad

@dudewad: as coisas podem funcionar melhor com o Angular 1.4. No blog do AngularJS para a versão 1.4.0: "ngOptions foi completamente refatorado para permitir a correção de vários bugs irritantes"
#

1
Haha "bugs irritantes". OK. Bem, ainda estou um pouco enganador quando se trata de atualizar três dias antes da entrega, por isso continuarei com sua solução por enquanto, que parece estar funcionando perfeitamente. Notável para o futuro, no entanto :) Obrigado!
Dudewad

36

Talvez útil para alguém:

Se você quiser usar opções simples em vez de ng-options, faça o seguinte:

<select ng-model="sortorder" ng-init="sortorder='publish_date'">
  <option value="publish_date">Ascending</option>
  <option value="-publish_date">Descending</option>
</select>

Defina o modelo em linha. Use ng-init para se livrar da opção vazia


Não consegui encontrar um exemplo claro de configuração de opções de ng com OBJETOS, em vez de matrizes JSON, e quando tento "traduzir" entre os dois, ele não apenas não dá certo, mas falha "silenciosamente". Eu não tenho idéia do que estou fazendo de errado. Finalmente, optei pela repetição simples de ng em uma tag de opção. Eu sei que não é uma prática recomendada, mas pelo menos funciona. Se alguém puder me apontar para um exemplo de WORKING amigável, direto e facilmente intercambiável nos detalhes, usando ng-options com objetos de dados (NÃO JSON), eu ficaria feliz. Exaltado duas vezes se usar ng-init também.
Code-sushi

Estou usando opções simples, mas no controlador não consigo obter o valor do menu suspenso selecionado. Ex: tentei alertar ($ scope.sortorder.value); e alerta ($ scope.sortorder); ambos dá indefinido. Como obter o valor selecionado aqui?
Maverick Riz

Muito obrigado por isso. Eu estava prestes a escrever um objeto completamente inútil no controlador que não precisava, só precisava de uma seleção simples. Obrigado mais uma vez.
Muhammad bin Yusrat

22

Algo semelhante estava acontecendo comigo também e foi causado por uma atualização para o angular 1.5. ng-initparece estar sendo analisado para o tipo nas versões mais recentes do Angular. No antigo, o Angular mapeava ng-init="myModelName=600"para uma opção com o valor "600", ou seja, <option value="600">First</option>mas no Angular 1.5, não o encontrará, pois parece estar esperando encontrar uma opção com o valor 600, ou seja <option value=600>First</option>. O Angular insere um primeiro item aleatório:

<option value="? number:600 ?"></option>

Angular <1.2.x

<select ng-model="myModelName" ng-init="myModelName=600">
  <option value="600">First</option>
  <option value="700">Second</option>
</select>

Angular> 1,2

<select ng-model="myModelName" ng-init="myModelName='600'">
  <option value="600">First</option>
  <option value="700">Second</option>
</select>

1
Obrigado! No meu caso, não posso usar o ng-init (sem valor padrão), portanto, converti meu modelo em uma string e funcionou!
Rodrigo Graça

Muito útil para meu problema de produção de aplicativos. Obrigado @Nabil Boag
Venki

3
Isso funciona, com certeza, mas as melhores opções são usar ng-value="600"no optionlugar de value. Ele vai analisá-lo para um número e você não tem que converter seus valores, como você faz agora :)
Max

@ Max Tentei muitas respostas de alto valor, mas nenhuma funcionou até encontrar a sua. Obrigado.
28419 Andrew

21

Entre as inúmeras respostas aqui, imaginei que repassaria a solução que funcionou para mim e atendeu a todas as seguintes condições:

  • forneceu um espaço reservado / prompt quando o modelo ng é falso (por exemplo, " --select region-- " w. value="")
  • quando o valor do modelo ng é falso e o usuário abre a lista suspensa de opções, o espaço reservado é selecionado (outras soluções mencionadas aqui fazem a primeira opção parecer selecionada, o que pode ser enganoso)
  • permite ao usuário desmarcar um valor válido, essencialmente selecionando o valor falso / padrão novamente

insira a descrição da imagem aqui

código

<select name="market_vertical" ng-model="vc.viewData.market_vertical"
    ng-options="opt as (opt | capitalizeFirst) for opt in vc.adminData.regions">

    <option ng-selected="true" value="">select a market vertical</option>
</select>

src

Perguntas e respostas originais - https://stackoverflow.com/a/32880941/1121919


Você quer dizer se o padrão é <option ng-selected="true" value="null">?
jusopi

Quer dizer, se o valor ng-modelo é atribuido como nulo, angular e cria opção vazio no seleccione
Flezcano

16

Uma solução rápida:

select option:empty { display:none }

Espero que ajude alguém. Idealmente, a resposta selecionada deve ser a abordagem, mas se isso não for possível, deve funcionar como um patch.


2
Nos meus testes, isso funcionará apenas no Chrome (novo Chrome, além disso) e no Firefox. IE e Safari quebram. Não saiba sobre ópera e outros navegadores de borda. De qualquer maneira , infelizmente , essa não é uma boa solução.
Dudewad

onde devemos colocar isso em html antes do fim do <select> ou então nos js
H Varma

1
@HVarma É uma regra de CSS. Coloque-o na folha de estilo ou dentro da tag <style>
KK


14

Sim ng-model criará um valor de opção vazio, quando a propriedade ng-model não estiver definida. Podemos evitar isso, se atribuirmos objeto ao ng-model

Exemplo

codificação angular

$scope.collections = [
    { name: 'Feature', value: 'feature' }, 
    { name: 'Bug', value: 'bug' }, 
    { name: 'Enhancement', value: 'enhancement'}
];

$scope.selectedOption = $scope.collections[0];


<select class='form-control' data-ng-model='selectedOption' data-ng-options='item as item.name for item in collections'></select>

Nota importante:

Atribua o objeto da matriz como $ scope.collections [0] ou $ scope.collections [1] ao ng-model, não use propriedades do objeto. se você estiver obtendo o valor da opção de seleção do servidor, usando a função de retorno de chamada, atribua o objeto ao ng-model

NOTA do documento Angular

Nota: ngModel é comparado por referência, não por valor. Isso é importante ao vincular a uma matriz de objetos. veja um exemplo http://jsfiddle.net/qWzTb/

Eu tentei muitas vezes, finalmente encontrei.


8

Embora as respostas de @ pkozlowski.opensource e de @ Mark estejam corretas, eu gostaria de compartilhar minha versão ligeiramente modificada, onde sempre seleciono o primeiro item da lista, independentemente de seu valor:

<select ng-options="option.value as option.name for option in typeOptions" ng-init="form.type=typeOptions[0].value">
</select>

4

Estou usando o Angular 1.4x e encontrei este exemplo, então usei o ng-init para definir o valor inicial no select:

<select ng-init="foo = foo || items[0]" ng-model="foo" ng-options="item as item.id for item in items"></select>

3


Eu enfrentei o mesmo problema. Se você estiver postando um formulário angular com postagem normal, enfrentará esse problema, pois o angular não permite definir valores para as opções da maneira que você usou. Se você obtiver o valor de "form.type", encontrará o valor certo. Você precisa postar o objeto angular e não o post do formulário.


3

Uma solução simples é definir uma opção com um valor em branco ""que achei que elimina a opção indefinida extra.


5
Doenst trabalho @ 1.4.3, Ele só adiciona 2 opções vazias para a forma
Denny Mueller

3

Ok, na verdade a resposta é bem simples: quando existe uma opção não reconhecida pela Angular, ela inclui uma opaca. O que você está fazendo de errado é que, quando você usa ng-options, ele lê um objeto, digamos[{ id: 10, name: test }, { id: 11, name: test2 }] right?

É isso que o valor do seu modelo precisa para avaliá-lo como igual, digamos que você queira que o valor selecionado seja 10, você precisa definir seu modelo para um valor semelhante { id: 10, name: test }a selecionar 10, portanto, NÃO criará esse lixo.

Espero que ajude todos a entender, tive um tempo difícil tentando :)


2

Esta solução funciona para mim:

<select ng-model="mymodel">    
   <option ng-value="''" style="display:none;" selected>Country</option>
   <option value="US">USA</option>
</select>

O voto negativo pode ser porque a aplicação de CSS a elementos de opção não funciona em todos os navegadores.
Kafoso

Acabei de testar no Chrome, FireFox, Safari e Edge - tudo está funcionando.
MMM

@MMM "todos os navegadores" inclui o IE. Muitos usuários ainda estão presos ao IE 10 e 11, e é provavelmente por isso que o voto negativo aconteceu. Chrome, fF, Saf e Edge não são "todos os navegadores".
PKD

1

Isso funcionou para mim

<select ng-init="basicProfile.casteId" ng-model="basicProfile.casteId" class="form-control">
     <option value="0">Select Caste....</option>
     <option data-ng-repeat="option in formCastes" value="{{option.id}}">{{option.casteName}}</option>
 </select>

Apenas adiciona a opção à lista e deixa o espaço vazio, infelizmente.
AMontpetit

1

Isso funciona perfeitamente bem

<select ng-model="contact.Title" ng-options="co for co in['Mr.','Ms.','Mrs.','Dr.','Prof.']">
    <option style="display:none" value=""></option>
</select>

da maneira que funciona, isso fornece a primeira opção a ser exibida antes de selecionar algo e a display:noneremove do menu suspenso. Se você quiser, pode fazer

<select ng-model="contact.Title" ng-options="co for co in['Mr.','Ms.','Mrs.','Dr.','Prof.']">
    <option style="display:none" value="">select an option...</option>
</select>

e isso fornecerá o select and optionantes de selecionar, mas uma vez selecionado, ele desaparecerá e não será exibido no menu suspenso.


Na verdade, você não responde à pergunta em si, pior do que isso, está apenas ocultando o elemento.
Marco Marco

0

Tente este no seu controlador, na mesma ordem:

$scope.typeOptions = [
    { name: 'Feature', value: 'feature' }, 
    { name: 'Bug', value: 'bug' }, 
    { name: 'Enhancement', value: 'enhancement' }
];
$scope.form.type = $scope.typeOptions[0];

Eu tentei o seu método, mas infelizmente não consegui fazê-lo funcionar. Você pode dar uma olhada neste violino. jsfiddle.net/5PdaX
Aniket Sinha

0

Aqui está a correção:

para uma amostra de dados como:

financeRef.pageCount = [{listCount:10,listName:modelStrings.COMMON_TEN_PAGE},    
{listCount:25,listName:modelStrings.COMMON_TWENTYFIVE_PAGE},
{listCount:50,listName:modelStrings.COMMON_FIFTY_PAGE}];

A opção de seleção deve ser assim: -

<select ng-model="financeRef.financeLimit" ng-change="financeRef.updateRecords(1)" 
class="perPageCount" ng-show="financeRef.showTable" ng-init="financeRef.financeLimit=10"
ng-options="value.listCount as value.listName for  value in financeRef.pageCount"
></select>

O ponto é que, quando escrevemos value.listCountcomo value.listName, ele preenche automaticamente o texto, value.listNamemas o valor da opção selecionada évalue.listCount embora os valores my show normal 0,1,2 .. e assim por diante !!!

No meu caso, o financeRef.financeLimitestá realmente pegando o value.listCounte posso fazer minha manipulação dinamicamente no controlador.


0

Gostaria de acrescentar que, se o valor inicial vier de uma ligação de algum elemento pai ou componente 1.5, verifique se o tipo correto é passado. Se estiver usando@ ligação, a variável passada será string e se as opções forem, por exemplo. inteiros, a opção vazia será exibida.

Analise adequadamente o valor em init ou vincule-o <e não @(menos recomendado para desempenho, a menos que seja necessário).


0

Solução simples

<select ng-model='form.type' required><options>
<option ng-repeat="tp in typeOptions" ng-selected="    
{{form.type==tp.value?true:false}}" value="{{tp.value}}">{{tp.name}}</option>


0

Uma solução de moagem com jQuery quando você não tem o controle das opções

html:

<select id="selector" ng-select="selector" data-ng-init=init() >
...
</select>

js:

$scope.init = function () {
    jQuery('#selector option:first').remove();
    $scope.selector=jQuery('#selector option:first').val();
}

Pascal, alguém downvoted você, porque esta é uma solução jQuery, não AngularJS
MAWG diz Reintegrar Monica

A pergunta é solicitada a desenterrar mais detalhes do Angular ...: - /
Sahu V Kumar

0

Se você usa ng-init, seu modelo para resolver este problema:

<select ng-model="foo" ng-app ng-init="foo='2'">

0

eu tive o mesmo problema, eu (removido "ng-model") mudei isso:

<select ng-model="mapayear" id="mapayear" name="mapayear" style="  display:inline-block !important;  max-width: 20%;" class="form-control">
  <option id="removable" hidden> Selecione u </option>
    <option selected ng-repeat="x in anos" value="{{ x.ano }}">{{ x.ano }}
</option>
</select>

para isso:

<select id="mapayear" name="mapayear" style="  display:inline-block !important;  max-width: 20%;" class="form-control">
  <option id="removable" hidden> Selecione u </option>
    <option selected ng-repeat="x in anos" value="{{ x.ano }}">{{ x.ano }}
</option>
</select>

agora está funcionando, mas no meu caso foi porque eu excluí esse escopo do ng.controller, verifique se você não fez o mesmo.



0

A única coisa que funcionou para mim está usando track byem ng-options, como este:

 <select class="dropdown" ng-model="selectedUserTable" ng-options="option.Id as option.Name for option in userTables track by option.Id">


se eu usar isso, ng-optionobtenha o último valor da matriz da qual desejo definir o valor optionde select. Não resolveu :(
rufatZZ

0

Consulte o exemplo da documentação do angularjs como superar esses problemas.

  1. Vá para este link da documentação aqui
  2. Find ' Binding selecione para um valor não-string via ngModel parsing / formatting '
  3. Lá você pode ver lá, a diretiva chamada ' convertToNumber ' resolve o problema.

Funciona para mim. Também pode ver como funciona aqui


0

Podemos usar CSS para ocultar a primeira opção, mas não funcionará no IE 10, 11. A melhor maneira é remover o elemento usando o Jquery. Esta solução funciona para os principais navegadores testados no chrome e IE10, 11

Além disso, se você estiver usando angular, em algum momento usando setTimeout funcionará

$scope.RemoveFirstOptionElement = function (element) {
    setTimeout(function () {
        $(element.children()[0]).remove();
    }, 0);
};
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.