I cannot seem to avoid the need to generate dynamic sub forms in the application I am working on. The sub form is working as expected and the sub form shows $invalid=true when one or more of it's inputs are invalid. The parent form however has $invalid=false.
I have seen people achieve nested forms where invalid sub forms invalidate the parent form, but I can't seem to do it dynamically without wrapping the dynamic compiling of the sub form in a $timeout.
In the above link I have recreated the scenario. I have three forms. The parent form, a sub form created at the same time as the parent form, and a dynamically created sub form.
If you clear the bottom existing sub form's input, it will invalidate the parent form (parent form turns red).
If you clear the top dynamic form's input, it will not invalidate the parent form (parent form remains green).
It will begin to work if you stick the addForm method in a $timeout:
// WORKS! : When you delete the dynamic added sub form input
// the parent form also becomes invalid
//timeout(addForm,0);
// FAILS! : When you delete the dynamic added sub form input text
// the parent form does NOT become invalid
addForm();
It's great that I have a workaround, but I would like to understand why I need the $timeout and if there is a solution that avoids the use of a $timeout.
DOM manipulations should be done in the
link
phase and not in thecontroller
. See $compileDetailed explanation:
The problem lies in the the angular
FormController
. At intialization it will look for a parentform
controller instance. As the sub form was created in the controller phase - the parent form initialization has not been finished. The sub form won't find it's parent controller and can't register itself as a sub control element.FromController.js
Plunker
As Michael said, the correct place to do any DOM manipulations is the
link
function and not thecontroller
function of the directive. Some extra information on why what you already have does / does not work depending on the use of$timeout
:According to the Angular documentation of the
$compile
service for directive definitions the controllerwhile the
link
functionYou can observe this yourself if you include a
link
function in your directive and write twoconsole.log
statements, one in thecontroller
function and one in thelink
function. Thelink
function is always executed after thecontroller
. Now, when you includeaddForm();
in your controller this will be executed at the time thecontroller
is instantiated, ie. before the linking phase, at which time, as it is mentioned in the documentation it isOn the other hand, if you call the
addForm()
function in the $timeout, this will actually be executed after the linking phase, since a$timeout
call with a zero timeout value causes the code in the timeout to be called in the next digest cycle, at which point the linking has been performed and the DOM transformation is performed correctly (once again you can see the timing of all these calls by addingconsole.logs
in appropriate places).Usually altering dom elements inside of a controller is typically not ideal. You should be able achieve what you are looking for without the need for '$compile' and make things a bit easier to handle if you introduce a second directive an array of items to use 'ng-repeat'. My guess is that
$timeout()
is working to signal angular about the new elements and causes a digest cycle to handle the correct validation.Updated plunkr