2014-05-15 5 views
25

Il seguente codice genera l'errore "TypeError: Impossibile leggere la proprietà '$ pristine' of undefined" quando faccio clic sul pulsante "check".

app.controller('MainCtrl', function($scope) { 
    // other stuff 
}) 

.controller('Ctrl2', function($scope) { 
    $scope.product = {description:'pump'}; 
    $scope.output = 'unknown'; 
    // uncomment to avoid undefined error, still can't see $pristine 
    // $scope.formHolder = {}; 
    $scope.checkForm = function() { 
    $scope.descriptionTest = $scope.product.description; 
    if ($scope.formHolder.productForm.$pristine) { 
     $scope.output = 'yes'; 
    } 
    if ($scope.formHolder.productForm.$dirty) { 
     $scope.output = 'no' 
    } 
    } 
}); 

html

<body ng-controller="MainCtrl"> 
    <div > 
     <ng-include ng-controller="Ctrl2" src="'myForm.html'"></ng-include> 
    </div> 
    </body> 

myForm.html

<form name="productForm" novalidate> 
    <h2>myForm</h2> 
    description: <input type="text" name="description" ng-model="product.description"/> 
    <br> 
    <button ng-click="checkForm()">Check Form</button> 
    <br> 
    Form Pristine: {{output}} 
    <br><br> 
    I can see the description: {{descriptionTest}} 
</form> 

plunkr

Il problema è che il mio Ctrl2 non può vedere il ProductForm. In un primo momento ho pensato che questo avesse a che fare con il prototipo ereditare che ng-includere fa quando si fa un ambito figlio, così ho provato ad aggiungere una variabile in Ctrl2:

$scope.productForm = {}; 

Questa è sbarazzato dell'errore, ma il mio controller ancora non vedevo correttamente $ pristine o $ dirty.

ho finalmente capito di lavoro con l'aggiunta di un oggetto scope.formHolder $ al di sopra del ProductForm:

plunkr

.controller('Ctrl2', function($scope) { 
    $scope.product = {description:'pump'}; 
    $scope.output = 'unknown'; 
    // uncomment to avoid undefined error, still can't see $pristine 
    $scope.formHolder = {}; 
    $scope.checkForm = function() { 
    $scope.descriptionTest = $scope.product.description; 
    if ($scope.formHolder.productForm.$pristine) { 
     $scope.output = 'yes'; 
    } 
    if ($scope.formHolder.productForm.$dirty) { 
     $scope.output = 'no' 
    } 
    } 
}); 

html

<form name="formHolder.productForm" novalidate> 

Perché fa questo lavoro? E c'è un modo migliore per farlo?

Ho finito in questo modo perché ho avuto una forma & regolatore/modello di lavoro che ho voluto riutilizzare qualche altra parte. Probabilmente dovrei fare una direttiva, ma tutto ha funzionato bene tranne le caratteristiche $ pristine e $ dirty del modulo - tutte le vars ng-model sono state passate correttamente.

How can I set a form contained inside a ng-include to be prestine? ha una risposta che "infrange tutte le regole" ma sembra più complicato.

Quando scrivo quando il modulo Controller aggiunge $ pristine all'ambito e a quale scopo?

Modifica/Risposta:

La mia domanda iniziale può essere riassunta in confusione su come la direttiva modulo scrive al campo di applicazione. Ho avuto l'impressione che ci vorrebbe la cosa in

<form name="productForm">... 

e aggiungere proprietà ad esso, come

$scope.productForm.$pristine = function() {...} 

tuttavia, scrive direttamente sulla parte superiore del ProductForm:

$scope.productForm = formObject; 

Così , l'oggetto modulo è memorizzato nel bambino e non il genitore come spiegato nella risposta selezionata.

L'insieme di chiavi nell'eredità di ambito figlio che mi ha aiutato è che la catena viene consultata in lettura, ma non in scrittura. Quindi, se imposti qualcosa come childScope.myThing.property = '123', mentre sembra una scrittura, prima deve fare una lettura per scoprire cosa è myThing. Considerando che l'impostazione childScope.myThing = '567' è una scrittura diretta e non implica affatto l'osservazione della catena principale. Tutto ciò è spiegato meglio in: What are the nuances of scope prototypal/prototypical inheritance in AngularJS?

risposta

14

Per capire perché la soluzione con formHolder funziona devi prima capire JavaScript prototypes chain. Illustriamo il primo caso, senza formHolder nel seguente codice pseudo:

$parentScope = { 
    //I'm a parent scope inside Ctrl2 
    productForm:{} //to avoid undefined reference error 
} 

$childScope = { 
    //I'm a child scope created by by ng-include 
    __protototype__: $parentScope 
} 

Quando la direttiva form viene analizzato crea FormController che si trova sulla proprietà $scope sotto chiave indicato nella name valore di attributo. Questo è più o meno equivalente a:

$childScope.productForm = $formCtrl; 

Dopo che i 2 ambiti di simile a questa:

$parentScope = { 
    //I'm a parent scope inside Ctrl2 
    productForm:{} //to avoid undefined reference error 
} 

$childScope = { 
    //I'm a child scope created by by ng-include 
    productForm: $formCtrl 

    __protototype__: $parentScope 
} 

Così è effettivamente conclusa con 2 immobili in ambiti diversi porta oggetti diversi. Ora, nel secondo caso si ha la seguente situazione:

$parentScope = { 
    //I'm a parent scope inside Ctrl2 
    formHolder:{} //to avoid undefined reference error 
} 

$childScope = { 
    //I'm a child scope created by by ng-include 
    __protototype__: $parentScope 
} 

Quando la direttiva form sta mettendo FormController esempio sul $scope questa volta utilizza catena diversa proprietà:

$childScope.formHolder.productForm = $formCtrl; 

Che è equivalente scrivere:

var formHolder = $childScope.formHolder; //since formHolder isn't defined on $childScope 
//the JS runtime will look for it in the prototypes chain and find it inside $parentScope 
//so here formHolder is the very same object you created and set on $parentScope 
formHolder.productForm = $formCtrl; 

speranza aiuta a capire perché la seconda opzione funziona. Per quanto riguarda la seconda parte di te in discussione - la soluzione è semplice e perfettamente valida - ma ci sono un paio di altri modi per gestire la cosa che è meglio dipende dal contesto effettivo utilizzo:

  • direttiva using senza scopo bambino di estrarre markup e parti di funzionalità
  • utilizzando direttiva con portata bambino che comunicare i cambiamenti di stato sia via controllante diretta accesso alle proprietà campo di applicazione o tramite eventi emessi
  • usando un custom include directive che non creerebbe portata figlio comune
+0

In angularJS è chiamata notazione a proposito. – Walfrat

0

So che questa è una vecchia domanda, ma ho avuto un problema simile, e ho cambiato l'html e incluso il mio ng-controller all'interno del file html.

Così, invece di

<ng-include ng-controller="Ctrl2" src="'myForm.html'"></ng-include> 

Change troppo

<ng-include="'myForm.html'"></ng-include> 

E poi nel file myForm.html, avvolgere il codice in un div, e aggiungere l'attributo ng-controllore, in modo che il myForm.html diventerebbe

<div ng-controller="Ctrl2"> 
    <form name="productForm" novalidate> 
     <h2>myForm</h2> 
     description: <input type="text" name="description" ng-model="product.description"/> 
     <br> 
     <button ng-click="checkForm()">Check Form</button> 
     <br> 
     Form Pristine: {{output}} 
     <br><br> 
     I can see the description: {{descriptionTest}} 
    </form> 
</div> 

Ora il controller bambino tocca il ng-comprendere la portata

1

Basta definire la variabile (oggetto vuoto) nel controller e usarlo mentre la definizione di modulo. Poiché JS angolare utilizza prototipi di ambito sotto il cofano, quando la forma proverà ad accedere all'ambito interno (per avviare la variabile), passerà prima attraverso la catena di portata e cercherà di trovare la stessa variabile nell'ambito del genitore.

<!—- The vars should live in the controller. I placed them here for the example. -—> 
<div ng-controller=“controllerName” ng-init="form={}; model={}" > 
    <div ng-include=“ ‘path-to-the-template’ ”></div> 
</div> 

<!—- Inside path-to-the-template -—> 
<form name="form.createUser"> 
    <input name="name" ng-model="model.name" /> 
    <input name="email" ng-model="model.email" /> 
</form> 

link di riferimento http://blog.152.org/2014/07/angular-form-element-not-attaching-to.html