Why Angular 1 form validation not works without ng-model - angularjs

I am trying to implement the basic angular form validation. I have implemented the same. But I am curious to know, why it does not works if I am not using ng-model.
Link for plunker to show the same behaviour

ngModel directive holds an instance of NgModelController containing the services for data-binding, validation, CSS updates, and value formatting and parsing. If ngModel itself is not there, validation will not work.
For form validation to work you need both form instance which can be published to scope using name attribute and the form input control (ngModel). The below description from Angular documentation explains that:
A form is an instance of FormController. The form instance can
optionally be published into the scope using the name attribute.
Similarly, an input control that has the ngModel directive holds an
instance of NgModelController. Such a control instance can be
published as a property of the form instance using the name attribute
on the input control. The name attribute specifies the name of the
property on the form instance.
This implies that the internal state of both the form and the control
is available for binding in the view using the standard binding
primitives.
This allows us to extend the above example with these features:
Custom error message displayed after the user interacted with a
control (i.e. when $touched is set) Custom error message displayed
upon submitting the form ($submitted is set), even if the user didn't
interact with a control

Because without the ng-model, the input elements aren't bound to anything within your application's scope.

Angular has own context model. (scope or something else) If you treat form outside Angular world, form can't get any information in Angular point of view.
If you doesn't want to use ng-model, use plain javascript validation method.

ng-model="value" defines that the value of that particular element is from angular's context. It checks for the value in the scope where the element is declared. For example, we have onclick="call()" and ng-click="call()" methods. The onclick event search for the function that is outside of angular context, ng-click event search for the function in the scope.
<div ng-app="app" ng-controller="Ctr" onclick="call()" ng-click="call()"></div>
<script>
function call(){
console.log('out of angular');
}
angualr.app('app', function($scope){
$scope.call = function(){console.log('insode angular')}
})
</script>
In the above code if you remove onclick, then ng-click prints 'inside angular', if you remove ng-click, then onclick prints out of angular. If both will be there, both will be called and prints.
In the same way, if you remove ng-model, you will loose the control over the value of input in angular context and $error, $invalid doesn't know what to validate.

Related

AngularJS using ngRequired without using ngModel

I'm currently maintaining an AngularJS application and need to have html inputs use the ngRequired directive. However these inputs do not use ngModel-- just straight up value to display the value dynamically and ngClick to popup a modal window that will eventually set the value in an object that is not bounded to a model. Seems like ngRequired only works when paired with ngModel in order for the submit button to reflect changes appropriately (like enable/disable).
I tried to work around not using ngRequired by manipulating the ngInvalid CSS which works great for showing the required fields-- however it does not bubble up to the submit button (disabling it if one or more required fields have no input). Is there a way to emulate ngRequired without using it explicitly in AngularJS?

AngularJS Custom form validator for custom directive form controls

I am working on an app with custom form controls which need to be validated before clicking a Next button. For standard HTML form controls, angular's built-in validation is quite nice, allowing me to bind my Next button with a ng-disabled on the form.$valid value. However, I am having trouble getting custom validation working in a rather difficult control. Here is my situation:
I have a custom directive for my form control (a component called mySlider)
mySlider wraps a 3rd-party component angular-slider -- yes, I know it's deprecated
We want to validate that the user has clicked the slider. The Next button should not be enabled if the user has not clicked the slider control.
I tried to create this custom validation by adding a has-changed attribute to the 3rd party directive element, and updating it on-click, however it does not seem to be working as expected.
I tried to examine how the built-in validations work (ex: try searching for var requiredDirective), as well as reading the Angular Forms Guide but it's still a little unclear
We have a requirement to not use jQuery, jQuery UI/Mobile etc, however it must also work for mobile touch events
See my Plunk and help
- please fork if you're going to edit. The custom validator and custom Slider directive is in script.js while the 3rd-party directive is in angular-slider.js.
You need to use the $setValidity function on your form model to toggle the validity state of the slider. This will give you the flexibility you desire to make complicated form rules which each component being different.
Here is working code to solve your problem. I converted your ng-change to an ng-click that compared begin and after values Working Plunkr
link: function (scope, element, attrs) {
// Slider init
scope.max = 100;
scope.min = 0;
scope.initial = 50;
scope.sliderValue = scope.initial;
//Set validity to false by default
scope.myForm.mySliderControl.$setValidity('hasChanged',false);
// ng-change is not coded with this example, converted to ng-click
scope.change = function(){
if(scope.initial != scope.sliderValue){
console.log("I have changed!");
scope.myForm.mySliderControl.$setValidity('hasChanged',true);
}
};
}
On a side note. Several issues that compounded to cause your problem:
angular-slider is not coded to enable the ng-change functionality you were attempting to use
Your mySlider directive should have an isolate scope and pass options via a config object. Currently, it is on the same scope as the controller which means the directive can't really be reused.
Compounding directive here only adds complications

Explain the working of ng-model and ng-show directives behind the scenes

The following code displays/hides the content based on whether the check box is checked or not. Pl. tell me in detail how ng-model and ng-show directives are working together behind the scenes to produce the desired result.
<input ng-model="toggleDisplayHide" type="checkbox"/>
<div ng-show="toggleDisplayHide">Some text goes here ...</div>
The ng-show directive shows or hides the given HTML element based on the expression specified in the ng-show attribute. Is toggleDisplayHide an expression?
The ng-model directive binds the value of HTML controls to app data. Is toggleDisplayHide referring to app data here?
Is this something like this:
When the checkbox is checked, the ng-model sets the value of toggleDisplayHide to true. And ng-show comes to know that the value of the expression toggleDisplayHide is set to true, it displays the content.
Angular docs is a great place to know about those things and It's well documented as well. Just added some excerpt from the docs here:
ng-show
The ngShow directive shows or hides the given HTML element based on the expression provided to the ngShow attribute. The element is shown or hidden by removing or adding the .ng-hide CSS class onto the element. The .ng-hide CSS class is predefined in AngularJS and sets the display style to none (using an !important flag).
Refer DOC
ng-model
The ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope using NgModelController, which is created and exposed by this directive.
ngModel is responsible for:
Binding the view into the model, which other directives such as
input, textarea or select require.
Providing validation behavior (i.e. required, number, email, url).
Keeping the state of the control (valid/invalid, dirty/pristine,
touched/untouched, validation errors).
Setting related css classes on the element (ng-valid, ng-invalid,
ng-dirty, ng-pristine, ng-touched, ng-untouched) including
animations.
Registering the control with its parent form.
Note: ngModel will try to bind to the property given by evaluating the
expression on the current scope. If the property doesn't already exist
on this scope, it will be created implicitly and added to the scope.
Refer DOCS
Based on that, Now to your question:
ng-show will evaluate the expression toggleDisplayHide in scope and if its truthy will display the DOM else will hide.
ng-model gives real taste of two way binding of the angularjs. The attribute provided to ngModel will bind to the respective scope property. If the property does not exist in the scope, angularjs will create automatically. So attribute should be a scope property at the end
ng-show simply hide/show some html content depending on the expression provided to the ngShow attribute. The expression can be any valid javascript expression. If the expression is Truthy, html content will be shown, otherwise it will be hidden. ngShow simply uses css property display:hide/show

Check the validity of dynamically added forms on angularjs controller's $scope

To check the validity of a form in my page, I test this property in my controller's scope:
$scope.formName.$valid
The problem is that when I add forms dynamically to the page (based on a model property), the $scope.newFormName property is not added to the scope.
This plnkr illustrate the problem
Click the 'Add form' button to add forms to the page
Click the 'Search forms' to update the list with the forms found in the $scope
Note that the added forms are not found in the scope
Is there any way to make this work? How can I check the validity of this dynamically added forms?
So your code adds a list of identical forms. And you want to see whether this list is valid or not.
The solution is to use ngForms within a parent form. See this Plunkr (my modified version of yours).
Form input values are bound to objects in the $scope.dynamicData array, which also is used by ngRepeat to create the list of forms.
Invalid fields are shown with a solid red border, and invalid forms have a dashed red border.
When forms are nested like this, the parent form is invalid when any of its child forms are invalid.
I'd use angular.element(). I would also personally get it via ID rather than name, but that is just me. View this plunker to see what I did: http://plnkr.co/edit/b87HJt
I'm using angular.element() to get the element by the name, getElementsByName and then using the $attr directive to get at the name.

is ng-model allowed inside <td> element of a table?

Is ng-model allowed inside element of a table? Will angular automatically update the model if I change a particular column(i.e. view)?
If you are making the table cells directly editable using the HTML contenteditable attribute, ng-model won't work automatically as by default it's only for form elements.
It is possible to make it work with contenteditable though. There is an working example of how to do it on the angular website at http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
ng-model is allowed wherever typical form elements exist that can use the directive (input, select and textarea)
One thing I will say about ng-model that can make it a bit tricky is that you will want to bind ng-model to a property of an object rather than just a simple scope variable. I have run into several instances where I bind $scope.foo to ng-model and use it in an input control. Then, if you clear the input field, the binding is lost and it stops updating the variable. Use something like $scope.fooObj.modelProp where fooObj is an object and it will work fine.

Resources