Angularjs validation - bootstrap datepicker input is not recognized - angularjs

If you read following Angularjs validations, you understand that:
Message will appear if user interacted and did not fill the date manually.
The problem is when date is filled using the datepicker the input is not recognized by Angularjs and still consider $invalid true, so the message remains there which is confusing/problem although date is already filled using datepicker!
<div class="form-group" ng-class="{ 'has-error' : AddForm.Birthdate.$invalid && !AddForm.Birthdate.$pristine }">
<input type="text" required data-provide="datepicker" class="form-control" name="Birthdate" ng-model="Birthdate" />
<span ng-show="AddForm.Birthdate.$invalid && !AddForm.Birthdate.$pristine" class="help-block" >
Birthdate is required.
</span>
</div>

You can either validate it prior to form submit, or else hook a listener on your datepicker to manually set the model property Birthdate value.
It seems bootstrap datepicker is built on top of JQuery datepicker, manually setting the value would be a bad practice you can refer to:
Update Angular model after setting input value with jQuery
a better approach would be to use some built-in angular component such as the ones from:
https://angular-ui.github.io/bootstrap/
http://dalelotts.github.io/angular-bootstrap-datetimepicker/
https://github.com/dalelotts/angular-bootstrap-datetimepicker

I discovered a new way for this problem-
First of all create an id for that input box and then create a function say $scope.assign(), which simply assign the id value to the model of that input.
Something Like this-
$scope.assign = function() {
$scope.modelValue = $('#idName').val();
}
Now use ng-bind="assign()" to your input box.
It worked for me :)

Was facing the issue, and its because of the picker you are using is built on top of Jquery which remains undetectable by the scope on update.
For my new project I have added another library and its pretty awesome.
See the documentation http://dalelotts.github.io/angular-bootstrap-datetimepicker
Providing the piece of code for which I have added a wrapper directive

My Previous Answer was based on work around and because at that time of answer I was pretty new to the angular and now instead of that I will recommend, not to use an library which is built on top of Jquery in Angular project. Instead prefer angular libraries.
Coming on the topic-
For date time picker I found one very good library
https://github.com/indrimuska/angular-moment-picker
You can find more libraries in built in angular, but I found it pretty useful for other validations too like min-date, max-date validation.
Using this library will solve the issue of validation for sure and its pure Angular way.

Related

Checkboxes in text angular not saving

I'm using text-angular to save html-based content into database and i want to save checkboxes with the checked attribute on them. I've tried to use the input field like below but text-angular doesnt render checkboxes with checked attribute. Is there any way to do this without doing pure css checkboxes?
<input type="checkbox" checked>
EDIT: The code I am using:
<text-angular data-ng-model="example_content" placeholder="Content..."
rows="5">
And inside the textarea of the text-angular directive, I am trying to insert the input from above but it renders without checked attribute
I looked up in text-angular's sanitizer library textAngular-sanitize.js to find out that the checked attribute isn't part of their htmlAttrs attribute map.
Hence, the only option we're left with is to override the sanitizer JS file with the edit. Moreover, you can add other attributes/tags if you want (Do consider vulnerabilities though!)
Here's the working example plunker forked from official text-Angular plunker. Notice the ta-sanitize.js included in plunker which is modified version of their textAngular-sanitize.js
Hope this helps!
You can achieve by using ng-click
<input type='checkbox' ng-click='onsaveValue()' ng-model="saveValue">

angular ui bootstrap typeahead - Before select callback

Is there anyway to get the value of the angular ui bootstrap typeahead selection before it changes the model?
Thanks alot!
Uri
There is no built in way to do this with with typeahead, but you can just store the old value in your ng-change
<input type="text" ng-model="foo" typeahead="bar in bars" ng-change="storeLast()">
$scope.storeLast() = function(){
//Before you reassign oldFoo, you can do some work with it here.
oldFoo=foo
}
However, this feels like a very strange thing to do. I think the better answer would be to redesign what you are trying to do so that you don't need weird code to do it.

How to use angularjs form validation in a non-angular SPA app

I wonder if anyone can help. On the surface of it, my question title may sound kind of stupid! I'm trying to use angularjs form validation in a non-angularjs application! Let me try to explain ...
We have a 'traditional' web app. Its not an SPA. The backend is java, and the java app server manages the session and the data within. Each page is a full http request/response. (There are a small number of ajax request/responses, but these are to add some bling to the page, rather than it's core functionality). In this respect, the architecture of the app is very traditional/old-skool, in that the server-side java code is responsible for generating the markup and populating form field values from it's version of the model data held in it's session store. (I think this is the crux of the problem)
The app is predominantly a HTML form based application, and to enhance the UX we have written some javascript field validators based around jQuery. For a number of reasons these have started to get a bit out of control, and we are exploring alternative options.
A simple google search finds countless jQuery plug ins for form validation. We are looking at these, but random jQuery plug ins are not our favoured approach (we tend to steer clear of 'somebloke.com' plug ins because we can't guarantee how well they're written, browser compatibility, future maintenance, how well they work with other plug ins etc - we've had our fingers burnt with this kind of thing before)
So we are looking at other approaches, and are currently exploring the use of angularjs.
Being able to 'gently introduce' angularjs into our architecture has some advantages. It's from a stable best-of-breed organisation (ie. it's not 'somebloke.com') so is well supported and maintained. It encourages us to write our js in a very modular & testable manner (our current rats nest of jQuery plugins, callbacks etc is far from that!). angularjs form validation is based around html5 standards and is declarative and semantic. It gives us a way forward for migrating our other jQuery based code to something better (angularjs directives). And overall, if we can layer angularjs into our current app, it gives us a good foundation for converting the app to a modern SPA at some point in the future.
Rewriting the entire app as an angularjs (or any other mv* framework) SPA at this point in time is not an option, so as mentioned above, we are looking at introducing small bits of functionality at a time; and today's challenge is form validation.
So, that's the background.
I've stripped out our current js client side validation, and our server-side java code is generating markup like this:
<form method="POST" action="/renew">
<input name="firstname" type="text" value="alf" />
<input name="surname" type="text" value="garnet" />
<input name="age" type="number" value="88" />
<input type="submit" />
</form>
(where the values for the input fields have been populated server-side from the model held by the server)
I've added the angularjs library to the page, and have got form validation working as follows:
<form method="POST" action="/renew" novalidate name="renewForm"
ng-controller="yourDetails" ng-submit="submitForm(renewForm, $event)">
<input name="firstname" type="text" value="alf" required ng-model="firstname"/>
<input name="surname" type="text" value="garnet" required ng-model="surname"/>
<input name="age" type="number" value="88" required ng-model="age"/>
<input type="submit" />
</form>
app.controller('yourDetails', function($scope) {
$scope.submitForm = function(form, $event) {
if (!form.$valid) {
$event.preventDefault();
return false;
}
};
});
This is a reasonable starting point. Broadly speaking it works in that angularjs is handling the form validation and submission. The submitForm method is executed, and if the form is not valid then the if block is entered and the form submission is cancelled. From here I can see it would be easy to add in the field error messages etc using ng-show etc.
The problem however is the use of ng-model on each html field. As I understand it I need to use this so that angularjs binds the field to the form, and can therefore track each fields valid status.
However, ng-model also appears to setup the 2-way data binding and sets the value of the field to it's version of the model data ... which is empty. For example:
Our server-side template might contain this:
<input th:field="*{firstname}" type="text" required ng-model="firstname"/>
Which might generate this markup:
<input name="firstname" value="alf" type="text" required ng-model="firstname"/>
The markup that gets served to the client includes value="alf"
But then angularjs steps in and sets up 2-way binding for the field. Because we don't have a firstname property in the angularjs scope, it initialises one with a blank value, and sets that blank value in the DOM of the field.
This results in the page being rendered by the browser with blank values in the fields, even though server-side we have values in the model, and the server has correctly generated the markup etc.
So, I think I understand the core problem and why it's happening. My question is, can I do angularjs form validation without the ng-model attribute on each field, or is there a version of the ng-model directive that only does 1-way binding - specifically DOM -> model
Any help would be very much appreciated;
Thanks
Nathan
When generating your form at server side, you can initialize your model with ng-init:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
OK, #Alexandre's answer was almost right, and it was his answer that pointed me in the direction of my final solution (so he should get the credit for this really :) )
ng-init does work as #Alexandre has suggested. The reason I couldn't get it to work was that I was trying to use it on a number field.
The following works because the value being set on the model with ngInit and the html input type are both text/string:
<input ng-init="firstname='alf'" th:field="*{firstname}" type="text" required ng-model="firstname" />
I was trying it on the age field as follows:
<input ng-init="age='88'" th:field="*{age}" type="number" required ng-model="age" />
This does not work because the age property was being set on the model as a string, but the html input type is a number. The following does work:
<input ng-init="age=88" th:field="*{age}" type="number" required ng-model="age" />
This led me to realise that the value being set on the angular model needs to match the data type of the html input type (certainly true of chrome, not sure about other browsers) (ie. string properties in the model - even if they are parse-able as numbers - cannot be used in a html number field with ngModel)
With this in mind, I decided there were 2 options. I could either do it server-side in the code that generates the markup:
<input ng-init="age=(some-potentially-complex-logic-to-workout-whether-its-a-string-or-number)" th:field="*{age}" type="number" required ng-model="age" />
Or I could do it client-side with a custom directive. In the end I went with a custom directive because a) it meant I could have a go at writing a directive (all part of the learning :)) and b) I realised there might be other cases that need special consideration which might make doing it server-side even more complex (ie. select fields don't have a value, they have a selected index of which you need to get it's value; radio buttons all have a value but you only want to set the value of the checked radio)
Here's what I came up with:
angularApp.directive('lvInitializeValueOnScope', function() {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
var propertyName = attrs.name,
propertyValue = attrs.value,
elementName = element.get(0).tagName.toLowerCase(),
fieldType = ( elementName === 'input' ? attrs.type.toLowerCase() : elementName ),
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
// if the input field type is number and propertyValue is parse-able as a number
if (fieldType === 'number' && !isNaN(parseFloat(propertyValue))) {
// set expression without quotes surrounding propertyValue
expression = propertyName + '=' + propertyValue;
}
// if the field is a html select element
if (fieldType === 'select') {
// propertyValue will be blank because select elements don't have a value attribute
// instead, we need to use the value of the child option element that is selected
propertyValue = $(element.html()).filter(":selected").val();
// set expression to assume propertyValue is a string value
expression = propertyName + '=\'' + ( !!propertyValue ? propertyValue : '' ) + '\'';
}
// if the input field type is a radio button but its not checked (selected)
if (fieldType === 'radio' && !element.is(':checked')) {
// we need to reset the expression so a blank value is used
// doing this means that only the checked/selected radio button values get set on the model
expression = propertyName + '=\'\'';
}
// evaluate the expression, just as angular's ngInit does
scope.$eval(expression);
}
};
});
(it looks more complex than it actual is because I've left the comments in - strip those out and there's really nothing to it)
To use it, each field that I am using ng-model on, I also need to use the attribute data-lv-initialize-value-on-scope. You don't need to pass any value on the attribute, the directive gets everything it needs from the element and attr parameters
So far it caters with text, number, radio and select fields. The pages I'm working on don't have any checkboxes, but as and when I come across those I dare say I'll need to add some code for those.
It works, and I think I prefer this to using ng-init with a load of server-side to determine whether to set a value, what its data type should be, etc
The only downside I can see at the moment is that processing a radio button set might be inefficient. IE. if you have 10 radio buttons, each marked up with ng-model and data-lv-initialize-value-on-scope; all 10 will run the data-lv-initialize-value-on-scope directive and set a value on the scope. At least 9 of the iterations will set a blank value on the scope, and at most only 1 will set the actual value on the scope.
Hey-ho, it seems to work for now :). Hope this helps someone in the future
Nathan

angularjs multilingual text field with ngmodel

I'm trying to implement a multilingual text input field with a little dropdown button to the left for selecting the language. For instance, when the dropdown menu shows 'de' the text field should be bound to model.multilingualData['de'].someField and so on.
My first approach was to set ngModel to model.multilingualData[selectedLanguage].someField. But when the user selects a different language without filling in the field correctly, no error is set on the form, because the model now points to a different object.
My next idea was to create an entire element directive without ngModel, but then I wouldn't be able to do other validations like ngMaxLength.
I couldn't find anything helpful on the web either. Any idea on how to implement this properly?
EDIT
Here's a little fiddle that illustrates the problem: http://jsfiddle.net/FZ2kg/
Not only that the form appears valid when you switch languages, the previous field value is also deleted, because the model is set to null when the field becomes invalid.
would be nice if you use this awesome external directive for multilanguage!
https://angular-translate.github.io/
I hope it helps
If you need to have form validation for all language variations and you're loading all languages at once in your model, can't you just create an input per language in the form and hide all but the currently selected language?
Here's a fiddle: http://jsfiddle.net/jvl70/w3rstmwd/5/
<form name="myForm">
<div ng-repeat="(lang, value) in model.multilingualData"
ng-show="lang==stuff.currentLanguage">
<ng-form name="innerForm">
<div ng-class="{ 'has-error': innerForm.anything.$invalid }">
<input type="text" name="anything" ng-model="value.someField" ng-maxlength="6"/>
</div>
</ng-form>
</div>
</form>
(At first I tried to use dynamic names for each input but found that the individual field $invalid wasn't available for dynamically named inputs. See this post to get around it: Dynamic validation and name in a form with AngularJS.
As an alternative to ng-form, you could use dynamic names for each input and try one of the custom directives on the link.)
I guess if there were many possible languages, this approach might get slower but it's ok for a few languages.

Dynamic data-binding in AngularJS

I'm building an AngularJS app and I have ran into an issue. I have been playing with the framework for a while and I have yet to see documentation for something like this or any examples. I'm not sure which path to go down, Directive, Module, or something that I haven't heard of yet...
Problem:
Basically my app allows the user to add objects, we will say spans for this example, that have certain attribute's that are editable: height and an associated label. Rather than every span have its own dedicated input fields for height and label manipulation I would like to use one set of input fields that are able to control all iterations of our span object.
So my approx. working code is something like this:
<span ng-repeat="widget in chart.object">
<label>{{widget.label}}</label>
<span id="obj-js" class="obj" style="height:{{widget.amt}}px"></span>
</span>
<button ng-click="addObject()" class="add">ADD</button>
<input type="text" class="builder-input" ng-model="chart.object[0]['label']"/>
<input type="range" class="slider" ng-model="chart.object[0]['amt']"/>
The above code will let users add new objects, but the UI is obviously hardcoded to the first object in the array.
Desired Functionality:
When a user clicks on an object it updates the value of the input's ng-model to bind to the object clicked. So if "object_2" is clicked the input's ng-model updates to sync with the object_2's value. If the user clicks on "object_4" it updates the input's ng-model, you get the idea. Smart UI, essentially.
I've thought about writing a directive attribute called "sync" that could push the ng-model status to the bound UI. I've though about completely creating a new tag called <object> and construct these in the controller. And I've thought about using ng-click="someFn()" that updates the input fields. All of these are 'possibilities' that have their own pros and cons, but I thought before I either spin out on something or go down the wrong road I would ask the community.
Has anyone done this before (if so, examples)? If not, what would be the cleanest, AngularJS way to perform this? Cheers.
I don't think you need to use a custom directive specifically for this situation - although that may be helpful in your app once your controls are more involved.
Take as look at this possible solution, with a bit of formatting added:
http://jsfiddle.net/tLfYt/
I think the simplest way to solve this requires:
- Store 'selected' index in scope
- Bind ng-click to each repeated span, and use this to update the index.
From there, you can do exactly as you proposed: update the model on your inputs. This way of declarative thinking is something I love about Angular - your application can flow the way you would logically think about the problem.
In your controller:
$scope.selectedObjectIndex = null;
$scope.selectObject = function($index) {
$scope.selectedObjectIndex = $index;
}
In your ng-repeat:
<span ng-repeat="widget in chart.object" ng-click="selectObject($index)">
Your inputs:
<input type="text" class="builder-input" ng-model="chart.object[selectedObjectIndex]['label']"/>
<input type="range" class="slider" ng-model="chart.object[selectedObjectIndex]['amt']"/>

Resources