Posso acessar um formulário no controlador?

152

Atualmente, estou usando o seguinte.

$scope.$$childHead.customerForm[firstName], de modo a:

<form name="customerForm">
  <input type="text" name="firstName" 
         ng-model="data.customer.firstName" 
         tabindex="1"  
         ng-disabled="!data.editable" 
         validationcustomer />
</form>

Mas isso só funciona no Chrome. Agora eu tentei o seguinte:

$scope.editCustomerForm[firstName], de modo a:

<form name="customerForm" ng-model="editCustomerForm">
  <input type="text" name="firstName" 
         ng-model="data.customer.firstName" tabindex="1"  
         ng-disabled="!data.editable" 
         validationcustomer />
</form>

O que não funciona. Observe que meu formulário está dentro de uma guia Foundation. Como posso acessar firstName?

EDIT : Parece que formnão é adicionado ao scopequando está dentro de uma guia Foundation.

Alguém tem uma solução para isso?

Vincent
fonte

Respostas:

210

Embora aludido em outros comentários, pensei em explicar um pouco para aqueles que usam a sintaxe "Controller As":

<div ng-controller="MyController as ctrl">

<form name="ctrl.myForm">
    ...inputs
    Dirty? {{ctrl.myForm.$dirty}}

    <button ng-click="ctrl.saveChanges()">Save</button>
</form>

</div>

Então você pode acessar o FormController no seu código como:

function MyController () {
    var vm = this;
    vm.saveChanges = saveChanges;

    function saveChanges() {

       if(vm.myForm.$valid) { 
            // Save to db or whatever.
            vm.myForm.$setPristine();
       }
}
slopapa
fonte
Tanto quanto posso ver, o modelo não pode chamar o método "saveChanges", pois não está exposto ao modelo
Spock
2
O método "saveChanges" é exposto na linha 3 do javascript ou estou entendendo mal?
slopapa
3
isso é bom porque significa que você pode evitar a injeção de toda a US $ escopo, que é mais limpo na minha opinião
72GM
2
Como você testa isso em jasmim? Na minha spec, vm.myForm é indefinido
bahrieinn
1
Isso deve ser observado nos documentos oficiais da 1.5.X, que é a maneira de executar componentes e es6. obrigado senhor
MatanCo
91

Você pode anexar o formulário a algum objeto definido em um controlador pai. Em seguida, você pode acessar seu formulário mesmo no escopo filho.

Controlador pai

$scope.forms = {};

Algum modelo em um escopo filho

<form name="forms.form1">
</form>

O problema é que o formulário não precisa ser definido no momento em que o código no controlador é executado. Então você tem que fazer algo assim

$scope.$watch('forms.form1', function(form) {
  if(form) {
    // your code...
  }
});
ondrs
fonte
10
Sugiro que você use var watcher = $scope.$watchere dentro da instrução if você chamaria watcher () para desvincular o relógio. Isto o torna um relógio 1 hora para que você não está observando cada digerir depois de ser definida
willJk
91

Se você deseja passar o formulário para o controlador para fins de validação, basta passá-lo como argumento ao método que manipula o envio. Use o nome do formulário, portanto, para a pergunta original, seria algo como:

<button ng-click="submit(customerForm)">Save</button>
Anthony Shull
fonte
13
Para esclarecer para futuros leitores, se dizer que a sua forma é nomeado / definida semelhante a este <form name="myform"></form>, ou mesmo <div ng-form name="myform"></div>, então o seu clique evento seria o seguinte: ng-click="submit(myform)". Então você pode acessar o objeto de formulário Angular na sua função de clique, como: $scope.submit = function (form) { if (form.$valid) {etc.
Matty J
Encontro um problema aqui - digamos que exista uma lista suspensa no formulário. Usar o método acima apenas me fornece o valor da visualização e não o valor exato que eu preciso. Ou estou fazendo algo errado, irá adicionar um violino.
19416 swateek
82

Um pouco atrasado para uma resposta, mas veio com a seguinte opção. Está funcionando para mim, mas não tenho certeza se é a maneira correta ou não.

Na minha opinião, estou fazendo o seguinte:

<form name="formName">
    <div ng-init="setForm(formName);"></div>
</form>

E no controlador:

$scope.setForm = function (form) {
    $scope.myForm = form;
}

Agora, depois de fazer isso, tenho meu formulário na minha variável de controlador, que é $scope.myForm

Atul Chaudhary
fonte
1
A única coisa que eu acrescentaria a isso é ter certeza de que isso está na parte inferior do formulário.
SMB
A posição de <div ng-init = "setForm (formName);"> </div> não importa. Apenas tome cuidado para que esteja em forma.
waqas 19/09/14
1
bom, mas eu preferiria uma solução mais simples: ng-init = "$ parent.myForm = formName" Sem a necessidade de alterar o controlador Nota: ele está funcionando apenas com o controlador direto, ao contrário da solução acima
mastilver
Depois de tentar os outros métodos, decidi por este, porque permite que o nameatributo seja exatamente o que eu quero. O problema com as outras soluções de objeto fictício é que, se esse componente for usado em outro componente com um formato ng, esse outro formato usará esse nome literalmente. Portanto, ele terá um campo com um nome literal de string (NÃO propriedades aninhadas) de "dummy.myForm"; achei isso inaceitável.
219 Basil Basil
Tentei e falhei muitas vezes para usar a sintaxe controllerAs (estou trabalhando com $ mdDialog). Finalmente resolvi isso e ele fez um ótimo trabalho. Única nota é que qualquer inicializações controlador precisa ser executado em um tempo limite $ como a forma não está disponível quando o controlador primeiras corridas
Peter Nixey
22

Para poder acessar o formulário no seu controlador, você deve adicioná-lo a um objeto de escopo fictício.

Algo como $scope.dummy = {}

Para sua situação, isso significaria algo como:

<form name="dummy.customerForm">

No seu controlador, você poderá acessar o formulário:

$scope.dummy.customerForm

e você poderá fazer coisas como

$scope.dummy.customerForm.$setPristine()

WIKI LINK

Tendo uma '.' nos seus modelos garantirá que a herança prototípica esteja em jogo. Então, use <input type="text" ng-model="someObj.prop1">ao invés de<input type="text" ng-model="prop1">

Se você realmente deseja / precisa usar uma primitiva, existem duas soluções alternativas:

1. Use $ parent.parentScopeProperty no escopo filho. Isso impedirá que o escopo filho crie sua própria propriedade. 2. Defina uma função no escopo pai e chame-a do filho, passando o valor primitivo para o pai (nem sempre possível)

Carsten
fonte
Onde está a área efetiva para definir a ligação do formulário?
Gus Crawford
vale a pena mencionar que dummy.customerFormserá indefinido até que as condições de ng-ifsão cumpridos no caso do elemento de formulário tem uma ng-ifcondicional em cima dele
haxxxton
22

Esta resposta está um pouco atrasada, mas me deparei com uma solução que facilita muito as coisas.

Você pode realmente atribuir o nome do formulário diretamente ao seu controlador se estiver usando a sintaxe controllerAs e, em seguida, referenciá-lo da sua variável "this". Aqui está como eu fiz isso no meu código:

Eu configurei o controlador via ui-router (mas você pode fazê-lo da maneira que quiser, mesmo no HTML diretamente com algo assim <div ng-controller="someController as myCtrl">) É assim que pode parecer em uma configuração de ui-router:

views: {
            "": {
                templateUrl: "someTemplate.html",
                controller: "someController",
                controllerAs: "myCtrl"
            }
       }

e, em seguida, no HTML, basta definir o nome do formulário como "controllerAs". "name" da seguinte forma:

<ng-form name="myCtrl.someForm">
    <!-- example form code here -->
    <input name="firstName" ng-model="myCtrl.user.firstName" required>
</ng-form>

Agora, dentro do seu controlador, você pode simplesmente fazer isso:

angular
.module("something")
.controller("someController",
    [
       "$scope",
        function ($scope) {
            var vm = this;
            if(vm.someForm.$valid){
              // do something
            }
    }]);
FrankieAvocado
fonte
2
Embora essa seja basicamente a mesma técnica sugerida por várias outras respostas, é a melhor variação e deve ser a resposta aceita, especialmente porque todo mundo usa controllerAs de qualquer maneira agora.
Ponto
6

Sim, você pode acessar um formulário no controlador (conforme indicado nos documentos ).

Exceto quando seu formulário não estiver definido no escopo do controlador e é definido em um escopo filho.

Basicamente, algumas diretivas angulares, como ng-if, ng-repeatou ng-include, criarão um escopo filho isolado. Quaisquer diretivas personalizadas com umscope: {} propriedade definida. Provavelmente, os componentes da sua fundação também estão no seu caminho.

Eu tive o mesmo problema ao introduzir um simples em ng-iftorno do<form> tag.

Veja estes para mais informações:

Nota: sugiro que você reescreva sua pergunta. A resposta para sua pergunta é sim, mas seu problema é um pouco diferente:

Posso acessar um formulário em um escopo filho do controlador?

Para o qual a resposta seria simplesmente: não .

Andre Torgal
fonte
... a menos que você configurar suas formas e controlador como descrito na resposta @ondrs (usando $scope.forms = {}e name="forms.form1")
marapet
Por favor, veja a resposta imediatamente acima da sua por KhalilRavanna. Você pode acessar o formulário em $ scope.formName. Ele fornece um exemplo de trabalho
micahblu 17/07/2015
3

adicione ng-model="$ctrl.formName"atributo ao seu formulário e, no controlador, você pode acessar o formulário como um objeto dentro do seu controlador,this.formName

Dhurim Kelmendi
fonte
0

Definitivamente você não pode acessar o formulário no escopo bec. não é criado. O DOM do modelo html é carregado lentamente como o construtor do controlador. a solução é assistir até que o DOM seja carregado e todo o escopo definido!

no controlador:

$timeout(function(){
    console.log('customerForm:', $scope.customerForm);
    // everything else what you need
});
Victor Orletchi
fonte