Angular form checkbox not initially included in the model - javascript

I am building a dynamic form that could include any number of field combinations and field types. A typical example of this might be a user registration with firstName, lastName, email and password. However also to be included on some forms would be a boolean checkbox for a field like active.
I have an empty object in my controller that will be populated by the form and then submitted. I attach ng-modelto all of the form elements as usual, these all work well until I get to the boolean field which does not get included in the model unless the user has interacted with it.
The boolean field is usually required at the server side so I want it to be checked by default, but this won't be added to the model unless you first uncheck it and check it again.
$scope.formData = {}
<div class="{{field.name}}">
<label for={{field.name}} class="field-label">{{field.prettyName}}</label>
<input
type="checkbox"
class="form-field boolean-checkbox"
id={{field.name}}
ng-model="formData[field.name]"
ng-checked="true"
/>
</div>
So the scenarios are:
Initial page load checkbox is checked but object reads as:
{}
The user then has to uncheck the checkbox, and then recheck it and the object will read as:
{
"active":true
}
I have also tried using ng-init="true" but no joy there.

I'm not sure why ngInit didn't work for you, but here's an example using it, as luk492 stated in its comment (you need to check your console logs to see it working):
angular
.module('myApp', [])
.controller('MainCtrl', ['$scope',
function($scope) {
$scope.formData = {};
$scope.$watch(function() {
console.log('formData', JSON.stringify($scope.formData, null, 4))
});
}
]);
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<input type="checkbox" ng-init="formData.truthyValue = true" ng-model="formData.truthyValue">
<input type="checkbox" ng-init="formData.falsyValue = false" ng-model="formData.falsyValue">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular.min.js"></script>
You'll see that both truthyValue and falsyValue are added to formData regardless of their boolean value.
An important thing to note from ngChecked docs is that you shouldn't use it together with ngModel, as this can lead to unexpected behavior.

If your form has the named Option ng-checked="true" at the start, then you could use a "constant" on the back-end. So if the form returned following data:
{ NULL }
then you could use your constant instead, because the form is on true.
And if the user changed the Option, you could use the form-value instead
{
"active":true
}
If you got any further question, don't hestitate to comment my answer. :)

Related

Why I can't access a form object inside a ngInclude on angularJS?

I have a screen built with a several stacks of ng-includes. The last one, in special, I build the screen based on user configuration.
Sometimes, I have to show a Form in one of this included templates. And when my user click on save button, I have to validate if all fields in the form are valid.
In the meantime, when a try to access form object, to check for $valid, my form is undefined.
After a day fighting against it, I've discovered that ng-include process is not accepting my form object to be created.
I've created this plunker to see if it's really happening on a simple project, making a working form and not working one:
http://plnkr.co/edit/4oMZYLgaYHJPoSZdSctI?p=preview
Basically, created a form, like this, with demanded angular attributes:
<form name="sampleForm">
<input type="text" name="aws" required ng-model="myValue">
<br/>myValue: "{{ myValue }}"
<br/>
<input type="text" name="aws" required ng-model="myValue">
<br/>myValue: "{{ myValue }}"
</form>
And trying to access form object like this:
$scope.sampleForm.aws.$valid
And the result is:
$scope.sampleForm === undefined
Someone know how to solve this problem?
Since ng-include creates a new scope, $scope.sampleForm will be undefined from within the included page.
The solution should be getting the ng-controller="formController" declaration inside of the included HTML page itself, which I think is also a better design, since I can't see a scenario where it's not "controlling" the form.
The other non-included form obviously works as you might expect.
Plunker

How to show error message until input is changed?

I have a form with server-side validation. and if server returns validation errors I want to show them in the form, but only until user edited anything.
Ideally I want to have something like:
<form name='MyForm' ng-submit='doSomething()'>
<label for='my-field-id'>Some field</label>
<input type='text' id='my_field_id' ng-model='myField' name='my_field'>
<div ng-show-if-changed='MyForm.my_field.serverSideError'
ng-show-until='MyForm.my_field.changed'>Error!</div>
</form>
How to achieve this behavior? I really want to avoid to put this logic in a controller, and want to come up with some smart reusable directive.
UPDATE
Main thing here is how to implement ng-show-until-changed functionality. The logic of it must be "if an element is shown and target is changed - hide it". And the questions here:
if target will be just some scope variable, like MyForm.my_field.serverSideError - will I need to manually launch $digest when it may be changed? For example after AJAX request will be completed, and so probably some errors can arrive from the server? How to properly watch for these server-side errors changes ?
What is a proper way to hide/show some element in a custom directive in a same way as ng-show directive does that? is there some built-in utilites for that?
The simplest and crudest way is to add a text-box to the form, and make it read-only so the user can't do anything with it. But you can still use JavaScript to put text into the box, such as the error message you get from the server. And of course you can erase the text any time (triggered by, say, an onkeydown event-handler for the data-entry-field that the user edits).
A similar and more elegant way involves a <p> element in the HTML of your form, located near the place where you expect the user to do some editing:
<p id="errmsg"> </p>
In your Javascript you would do something like this, to prepare:
var P=document.getElementById("errmsg");
and then, when you need to display an error message from the server:
P.innerHTML="Text of error message";
and when the user begins editing (again, detect with an onkeydown event handler), you can replace the error-message text with another
If you really want to do hide/show of error messages, you might consider constructing several paragraphs, all with the same number of lines. Each would contain a pre-written error message, although one of them might be instructions regarding data that is supposed to be entered. That's the one to show by-default. All the paragraphs can be stacked up in the same place on the form, this way:
<div style="position:relative;">
<p id="p1" style="position:absolute;top='0px';left='0px';" hidden="">paragraph 1 text</p>
<p id="p2" style="position:absolute;top='0px';left='0px';" hidden="hidden">paragraph 2</p>
<p id="p3" style="position:absolute;top='0px';left='0px';" hidden="hidden">paragraph 3</p>
</div>
<br /> <!-- you will discover that some line-breaks are needed here -->
<br />
You would again use JavaScript to getElementById() of each paragraph, perhaps into an array of P[] variables. Then you can hide or display any of the paragraphs with
P[x].hidden="hidden";
P[x].hidden="";
You can hide the error message when the model changes and show it again when errors occure on submit.
See sample below or in this [plunker][1]
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
</head>
<body ng-app="changeExample">
<!-- snippet adapted from https://docs.angularjs.org/api/ng/directive/ngChange. -->
<script>
angular.module('changeExample', [])
.controller('ExampleController', [
'$scope',
function($scope) {
var model;
function simulateError(model) {
model.hasError = true;
model.errorMessage = "Some validation error!"
}
$scope.model = model = {
myField: 'my field value',
hasError: false,
errorMessage: '',
changed: false
};
$scope.change = function() {
model.hasError = false; // soon as the model changes unset the error.
};
$scope.doSomething = simulateError;
}
]);
</script>
<!--
snippet adapted from SO question
NOTES:
- using ng-change to listen to input field changes
- ng-trim="false" to listen to whitespace e.g spacebar
- hide error when user uses text input field
-->
<form name="myForm" ng-controller="ExampleController" ng-submit="doSomething(model)">
<label for="my-field-id">Fill in:</label>
<input type="text" id="my_field_id" ng-model="model.myField" ng-change="change()" ng-trim="false" />
<input type="submit" />
<div ng-show="model.hasError" ng-bind="model.errorMessage">
Error from server goes here!
</div>
</form>
</body>
</html>
[1]: http://plnkr.co/edit/BNBgiT?p=preview

Angularjs ng-model is undefined

In HTML I have this line of code:
<input type="text" class="training-comment" placeholder="Enter comment" ng-model="content" data-ui-keypress="{13:'keypressCallback($event)'}">
And in controller this:
$scope.keypressCallback = function ($event) {
console.log("comment is", $scope.content);
$event.preventDefault();
};
And when I enter some text in input and press enter in console I see that $scope.content is undefined.
Why is this ?
I put together a Plunker example here using the Angular UI and basically copying the code from the question. I then took this example and added an ng-repeat to demonstrate one of the most common issues I have seen: scope issues:
<div ng-repeat="x in collections">
<input type="text" class="training-comment" placeholder="Enter comment"
ng-model="content" data-ui-keypress="{13:'keypressCallback($event)'}" />
<br /><br />
</div>
You can find this updated plunker example here. Basically whenever you use an ng-repeat or any other directive that creates a new scope your model exists in that scope - not the parent or root scope. This means that your code might be working, but it is printing out the value from the wrong scope! For more information on scopes see the angular documentation here.
To use the plunker demo, type into the first input and press the enter key, the model will be updated. But, if you type into either of the other two boxes, though, the model will either not be updated or it will be undefined (if you have never typed into the first box).
Even if this isn't the exact issue, hopefully it will still help. Best of luck!

Obtaining form data from within controller

I am trying to obtain form data from the Angular controller without success.
HTML:
<form>
<input type="text" id="entityName" ng-model="ent.Name">
<button class="btn" type="button" onclick="this.blur()" ng-click="$event.preventDefault(); saveData()">Save</button>
</form>`
JS Controller:
$scope.saveData = function() {
console.log($scope.ent.Name);
}
I receive error: Error: $scope.ent is undefined
always give form a name if you want angular validation to work. Shouldn't use onclick in angular app (asking for headaches and creating testing problems) ng-clcik will automatically prevent default so you don't need to add that yourself.
You probably haven't set up an object in scope for ent. If your ng-model values didn't have a dot in them you wouldn't need to register anything in scope for ng-model to work automatically creating a scope property
$scope.ent={};
This will be object that your ng-model properties will bind to. Will need to see more code if this isn't the issue

Why do two forms appear?

I am using jQuery and bootstrap to give drop-down search suggestions.Following is the html code.But when I type something in the search form and then clear the form.Two forms apears as in the picture.Why? I am new to jQuery. Thanks for any help.
<div class="container">
<div class="row">
<div class="span6 offset3">
<form class="form-search">
<input type="text" id="month" name="month" class="input-medium search-query">
<button type="submit" class="btn">Search</button>
<div id="suggestions">
</div>
</form>
</div>
</div>
</div>
<script>
jQuery("#month").keyup(function(){
ajax('search', ['month'], 'suggestions')});
</script>
EDIT:
I am using web2py framwork.This is the search function's code:
def search():
if not request.vars.month: return dict()
month_start = request.vars.month
selected=complete('piracyfinder',month_start) #this line get the search results
return DIV(*[DIV(k['title'],
_onclick="jQuery('#month').val('%s')" % k['title'],
_onmouseover="this.style.backgroundColor='lightblue'",
_onmouseout="this.style.backgroundColor='white'"
) for k in selected])
It appears you are using the same function (i.e., search()) to fill in the suggestions as well as to create the form (though that function doesn't process the form when submitted). According to the logic, when request.vars.month is either empty or does not exist, the function returns an empty dict. This will result in the associated view (i.e., /views/[controller name]/search.html) being executed and returned. Presumably the search.html view contains the HTML code shown above. So, when you clear the input box, the keyup handler is triggered and sends an empty month variable, which results in a new copy of the form being sent back and inserted in the "suggestions" div. You can avoid this problem by checking whether request.vars.month exists:
if not request.vars.month:
return '' if 'month' in request.vars else dict()
A better approach might simply be to use different functions for the search form and the suggestions given that they do completely different things and don't share any code.
if not request.vars.month also applies to the month var existing but being empty. Therefore, it's returning the form.
You need to do one of these:
Have your "suggestions" code be in a different page/file
Add a isAJAX variable to the request (or some other way to identify AJAX requests)
Check if the variable exists, rather than checking if it is falsy.

Categories