I have been searching for an easy way to make a month/year picker for an ASP.NET Core application I'm making. I found this fantastic one on jsfiddle. It uses the following resources:
bootstrap-datepicker.min.js
datepicker.min.css
bootstrap.min.js
bootstrap.min.css
Here is the code:
var startDate = new Date();
var fechaFin = new Date();
var FromEndDate = new Date();
var ToEndDate = new Date();
$('.from').datepicker({
autoclose: true,
minViewMode: 1,
format: 'mm/yyyy'
}).on('changeDate', function(selected) {
startDate = new Date(selected.date.valueOf());
startDate.setDate(startDate.getDate(new Date(selected.date.valueOf())));
$('.to').datepicker('setStartDate', startDate);
});
$('.to').datepicker({
autoclose: true,
minViewMode: 1,
format: 'mm/yyyy'
}).on('changeDate', function(selected) {
FromEndDate = new Date(selected.date.valueOf());
FromEndDate.setDate(FromEndDate.getDate(new Date(selected.date.valueOf())));
$('.from').datepicker('setEndDate', FromEndDate);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/css/datepicker.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.3.0/js/bootstrap-datepicker.min.js"></script>
<div class="form-group">
<label>First check in:</label>
<input type="text" class="form-control form-control-1 input-sm from" placeholder="CheckIn">
</div>
<div class="form-group">
<label>First check out:</label>
<input type="text" class="form-control form-control-2 input-sm to" placeholder="CheckOut">
</div>
<br/>
<div class="form-group">
<label>Second check in:</label>
<input type="text" class="form-control form-control-1 input-sm from" placeholder="CheckIn">
</div>
<div class="form-group">
<label>Second check out:</label>
<input type="text" class="form-control form-control-2 input-sm to" placeholder="CheckOut">
</div>
I have made this work in my project in Visual Studio, however, it doesn't seem to be able to find the classes "form-control-1" (and 2) and "from" and so it gives me a little squiggly line indicating so.
I started looking through all the css files included and I cannot find a reference to those classes in any file. If I remove them, the month selector does not work. Can someone help me understand where that functionality is coming from? Does anyone know where those classes are defined?
The from and to are not being used for styling, they are being used in the code that you posted. $('.from').datepicker() $('.to').datepicker().
You can safely delete the form-control-1 and form-control-2 classes.
Those classes can be fuctional classes, not attached to any styling but used to select or manipulate the elements.
Thats probably why you wont find anything. Search through the js files and you will find something.
Since removing the class breaks the functionality, it's probably being referenced from JS. It may be that the JS is calculating the name of the class, something like "form-control " + i for example.
Your best bet to see where this is defined is to use the browser inspector. For example, in Chrome, right-click the page and choose "inspect" to get this UI, which shows you each css class and where they are declared or re-declared in the css hierarchy.
When using Javascript it can be very useful to use CSS classes which are not attached to any styling, but are instead used as semantic references for jQuery selectors to use later.
This can be very useful when trying to attach multiple semantic meanings to an element. You can have one element can have many semantic tags. I use this in complex components to be able to dynamically tag elements that I can later manipulate or check the class name. This is especially useful if you are generating elements from a server-side language and trying to tag them semantically so that they can be manipulated later from Javascript/jQuery. It think it is much easier to add a dynamic class string from the server side than to put in data attributes.
One example of this that I use is for validation information to be attached to an element. I generate the elements in a server-side class (including what type of validation it should have) which outputs a CSS class string, then when I submit the form in Javascript, I get all elements with specific classnames and check if their contents match the validation type.
The code sample you posted does a similar thing, where it wants to use a jQuery plugin on all elements with a certain class. Instead of direct styling, it uses them as semantic markers that those elements should be transformed in a certain way to be datepickers (which are documented here: https://jqueryui.com/datepicker/). Code for jQueryUI uses this a lot to be able to simplify which elements should be used-- and it can be easier to add new elements that are datepickers, since you simply add a class to the new one instead of having to add a new jQuery reference to it (especially if you don't know exactly how many of them there will be on the page when you write the Javscipt code).
If you are ever in a similar instance and looking for a different way to store data on elements without inadvertent effects and it is pure Javascript without server side rendering, data-* attributes in HTML5 are a way to do it (and a simple library could be developed to add them to elements from a server side language). Just use jQuery selectors with jQuery("[data-mytagname=someValue]") to select.
A word of caution that if you use classes like this as semantic tags, I would be careful to avoid commonly used class names, since you could inadvertently get extra styling when a co-worker (or yourself) decides to use that in an element. ;)
Related
We have to be ADA compliant on our site. One of the things they look for is every form must have a label tag. The code has a label tag in the right place, but then when the javascript loads on the page, a span tag gets between the tag and the search field making it no longer compliant. I don't see a way to add a label. I was curious if anyone else had a suggestion for this or is there an alternative to typeahead that will work? In order to be compliant it must look like
<label for="search">Search: </label>
<input type="text" name="search" id="search"/>
For example the way it works now looks like...
<label for="search">Search: </label>
<span class="twitter-typeahead">
<input type="text" name="search" id="search"/>
</span>
There is no option to change the span tag that wraps your input. You can see where it is hardcoded in the source code here. Unfortunately, typeahead is no longer maintained either, so there will not be a future option to customize this.
However, you can always modify the code yourself. Either in the www.js file that I linked to (if you compile yourself) or in the bundle, find the buildHtml() function and change that line to an empty string.
function buildHtml(c) {
return {
wrapper: '',
menu: '<div class="' + c.menu + '"></div>'
};
}
I don't know if this will have unknown repercussions elsewhere in typeahead, but I just tried it on a page and everything seemed to be working fine.
I have an app with many forms. Each field has several HTML elements, so I thought I could extract some directives (one per type of field) to keep my forms tidy.
I've created a sample app to demonstrate the problem, but I'm getting inconsistent behavior. In the sample app, a <link /> element replaces the <input />. In my real app, <input /> just gets removed from the DOM completely. I feel like this should be easy; why doesn't it work?
To answer your stated question, it's because you told it to, with ng-transclude. That replaces the contents of the tag with the original element, which I don't think you wanted; you probably wanted the original contents to be transcluded as the label instead.
This is probably what you're looking for:
<div class="form-group" >
<label for="{{htmlId}}" ng-transclude></label>
<input id="{{htmlId}}" class="form-control" type="text" ng-model="model" />
<span ng-repeat="error in errors">{{error}}</span>
</div>
I've moved the tranclusion into the label. While this works, I would also recommend the style of actually passing a label attribute, rather than transclude it, just for the sake of having a consistent API and simpler code; it's functionally equivalent, though, so don't let me bully you.
Also, you've got a few errors in your .js as well. First, you want to use = in your scope instead of &
scope: {
model: '=',
errors: '='
},
& is used to pass methods, while = is used for objects (this is a simplification). Since your model and errors are objects, you'll want to use = instead.
Finally, in your example, your html template and your directive's template don't have the same name... you've got an extra 's' in your .js, but that's probably just in the plunker and not your real app.
We currently use the following logic to mask the inputs:
Set a specific class to several inputs <input type="text" class="typenumdec" />
In document.ready() bind a propertychange event with the rules:
$('table tbody > tr > td.tiponumdec').children('input[type=text], input[type=number]')
.add('input[type=text].tiponumdec, input[type=number].tiponumdec').bind('input propertychange', function () {
$(this).val($(this).val().replace(/[^0-9,]/g, ''));
});
But we wanted to centralize the logic and make it more streamlined for our developers so they dont have to add/modify the bindings.
Something like this:
Developer defines somewhere the format and its name (javascript globals? key/value array?):
var formatmoney1 ='5.2'; //5 integers and 2 decimals
var formatmoney2 ='5.3'; //5 integers and 3 decimals
var formatdays ='3'; //3 integers
Developer sets the format to a data-atribute or css class (recommended option?)
<input type="text" class="formatmoney1" data-formatx="formatmoney1" />
On document.ready() a generic function parses the format definitions and the inputs in order to mask them depending on its assigned format
PS: we saw this plugin that seems interesting in order to cover part of the mask logic (your opinions?): http://igorescobar.github.io/jQuery-Mask-Plugin/
We are currently using HTML 5 to make 99% of all validations. You can use them in a very understandable and developer-friendly way.
For example this code will prevent entering everything else then an email address:
<input type="email" />
Or use this with custom regex:
<input type="text" name="dutch_zip_code" pattern="[A-Za-z]{4}[0-9]{2}" />
You can also set the pattern in javascript / jquery like this:
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="jquery.mask.js"></script>
</head>
<body>
<input type="text" name="dutch_zip_code" data-validation-type="dutch_zip_code" />
<script>
$(document).ready(function()
{
$('input[type=text]').each( function()
{
var type = $(this).data('validation-type');
if (type == 'dutch_zip_code')
{
$(this).attr('pattern', '[A-Za-z]{4}[0-9]{2}');
//
// Use jquery mask plugin:
// https://plugins.jquery.com/mask/
//
$(this).mask('0000SS');
}
}
);
});
</script>
</body>
</html>
You can use modernizr for backwards compatibility.
As mentioned in comments, if you use bootstrap (http://getbootstrap.com/), there is the excellent Jazny Bootstrap (http://jasny.github.io/bootstrap/) plugin which makes input masks extremely easy and tidy.
Here is a fiddle which demonstrates your 3 formats: http://jsfiddle.net/JNfxa/9/
Here is the HTML:
<label>formatmoney1</label>
<input type="text" class="form-control" data-mask="99999.99" data-placeholder=" ">
<label>formatmoney2</label>
<input type="text" class="form-control" data-mask="99999.999" data-placeholder="0">
<label>formatdays</label>
<input type="text" class="form-control" data-mask="999" data-placeholder="#">
And that is it, no extra CSS or JS required.
I have used three different examples for the data-placeholder attribute, this is the character that appears for the empty digits that the user must complete, default is '_'. Where I have used '9', this will restrict the user to enter a number, there are other options detailed here: http://jasny.github.io/bootstrap/javascript/#inputmask
Now, to centralise the data-mask to a single, maintainable variable, you could bind it to an observable ViewModel property using KnockoutJS. (http://knockoutjs.com/index.html)
You can do a lot more than this, but here is an updated fiddle: http://jsfiddle.net/JNfxa/11/
Now there is some JS, to declare our observable properties containing each of the masks:
vm = {};
vm.formatmoney1Mask = ko.observable("99999.99");
vm.formatmoney2Mask = ko.observable("99999.999");
vm.formatdaysMask = ko.observable("999");
ko.applyBindings(vm);
Knockout has an attr binding, which lets you bind the value of an observable property to a custom HTML attribute of your choice. More details here: http://knockoutjs.com/documentation/attr-binding.html
The HTML changes slightly to bind the data-mask attribute instead of setting it directly:
<label>formatmoney1</label>
<input type="text" class="form-control" data-bind="attr: { 'data-mask': vm.formatmoney1Mask }" data-placeholder=" ">
<label>formatmoney2</label>
<input type="text" class="form-control" data-bind="attr: { 'data-mask': vm.formatmoney2Mask }" data-placeholder="0">
<label>formatdays</label>
<input type="text" class="form-control" data-bind="attr: { 'data-mask': vm.formatdaysMask }" data-placeholder=" ">
What's great about this, is you can update the mask observable on the fly, and the HTML will automatically update without refreshing the page, so you could have e.g. a radio button control to choose different input mask types.
I would HIGHLY recommend taking a look at this plugin- Robin Herbots inputMask
This plugin is robust, has alot of callbacks/options and is actively developed. One of the major advantages of this plugin are the extensions which is where you define masks and aliases.
You can define a mask however you want..if you wanted to extend the out of box decimal mask definition you could do it like this...
$.extend($.inputmask.defaults.aliases,
{
'formatmoney1':
{
mask: "99999.99",
placeholder: "_____.__",
alias: "decimal"
},
'formatmoney2':
{
mask: "99999.999",
placeholder: "_____.___",
alias: "decimal"
}
}
Once you have defined your mask, and extended the out of box decimal definition then you could pick up all elements and apply the inputmask.
$(document).ready(function()
{
$('.formatmoney1').inputmask('formatmoney1');
$('.formatmoney2').inputmask('formatmoney2');
});
This mask allows you to have a very high amount of control through the numerous callbacks and is highly configurable by setting default options.
Another option you have with using this plugin is to take advantage of the data-inputmask attribute. For example,
<input type="text" data-inputmask="'alias':'formatmoney1'" />
Be sure you look through the Usage page as well as the extension files, there are a few of them but it looks like you will want to use the jquery.inputmask.numeric.extensions.js
I have a web application which replaces content. This content has jquery ui check buttons. When I replace the content if a button already exists then don't add it again:
if(!$('label[for=checkWeekM]').hasClass('ui-button'))
$('.checkWeek').button();
If I push the button (its state is checked) and if I replace the content, the button starts locked until the same content is replaced again.
I use Backbone.js to replace the content
jsfiddle
How can I unlock the check button?
You are duplicating id attributes and that leads to bad HTML, bad HTML leads to frustration, frustration leads to anger, etc.
You have this in your template that you have hidden inside a <div>:
<input type="checkbox" class="checkWeek" id="checkWeekM" />
<label for="checkWeekM">L</label>
Then you insert that same HTML into your .content-central. Now you have two elements in your page with the same id attribute and two <label> elements pointing to them. When you add the jQuery-UI button wrapper, you end up with a slightly modified version of your <label> as the visible element for your checkbox; but, that <label> will be associated with two DOM elements through the for attribute and everything falls apart.
The solution is to stop using a <div> to store your templates. If you use a <script> instead, the browser won't parse the content as HTML and you won't have duplicate id attributes. Something like this:
<script id="template-central-home" type="text/x-template">
<div data-template-name="">
<input type="checkbox" class="checkWeek" id="checkWeekM" />
<label for="checkWeekM">L</label>
</div>
</script>
and then this to access the HTML:
content.view = new ContentView({
model: content,
template: $('#template-' + template_name).html()
});
Demo: http://jsfiddle.net/ambiguous/qffsm/
There are two quick lessons here:
Having valid HTML is quite important.
Don't store templates in hidden <div>s, store them in <script>s with a type attribute other than text/html so that browser won't try to interpret them as HTML.
I took a detailed look at your fiddle after you mentioned this problem. The solution I suggested here was more like a quick fix.
If you want to follow the right thing to avoid long term problems and side effects you should consider what is mentioned here. This way your problem is solved and there are no other bugs.
I have this piece of html code in my application (the ng-app and ng-controller values are defined before):
<div>
<label for="projectSearchDateFrom"><%= Res("Projects.Search.From")%></label>
<input id="projectSearchDateFrom" type="text" ng-model="startDate" ui-date="dateOptions"/>
<img ng-show="hasStartDate()" ng-click="clearStartDate()" src="/_Layouts/ClientPortal/Images/layout/TA/delete-small.png" alt="<%= Res("ToolbarDialog.Clear")%> <%= Res("Projects.Search.From")%>" title="<%= Res("ToolbarDialog.Clear")%>" />
</div>
My AngularJS controller looks like this:
function ProjectSearchCtrl($scope) {
$scope.startDate = '';
$scope.hasStartDate = function () {
return $scope.startDate != '';
};
$scope.clearStartDate = function () {
$scope.startDate = '';
};
$scope.dateOptions = {
dateFormat: "yy-mm-dd",
showOn: "focus"
};
}
This works perfectly: I have a datepicker set up correctly thanks to AngularUI, the AngularJS binding works...
But if I change the showOn value to "button" or "both" (the two possible options which will actually show the datepicker button), everything after the input (containing the ui-date attribute) stops working: ng-show, ng-click... The controller doesn't even get called.
Versions (all is up-to-date):
jQuery 1.7.2
angularJS 1.0.0
angularUI 0.1.0
Chrome 20
Please take a look at this line in the Select2 directive. This is a note to ANYONE writing a directive / implementing a plugin in AngularJS (not just AngularUI):
Any plugin that injects a new DOM element immediately after the linked element runs the risk of disrupting the compiler. The reason is because the way AngularJS works, it caches the index of each DOM element at compile time, and then makes a second pass upon linking. When you inject new DOM, you offset the index of all siblings immediately after the directive.
For this reason, I've been forced to wrap both TinyMCE and Select2 in a setTimeout so that the DOM is injected after the linking is done. Note that I don't bother using $timeout because I really don't need/want $apply() to fire just to turn on the plugin, as there are already callbacks in place that do this when the plugin changes the data.
I'll look into making sure this is uniform across AngularUI. Unfortunately, there appears to be no elegant solution to this problem in AngularJS at this time, however it's a problem I've been thinking about for some time and am constantly looking for a better solution towards.
Read this Google Groups post for more information about compiling vs linking: https://groups.google.com/forum/?fromgroups#!searchin/angular/compile$20link/angular/RuWn5W3Q5I0/KJhcQJ_RNsIJ
You can also open a bug ticket on the AngularUI project in the future.
As suggested by Pete BD in his comment on the question, there is some kind of bug/unwanted behaviour in the way that jQueryUI and angularJS interact. A workaround is to wrap the input control in a div.
<div class="date">
<label for="projectSearchDateFrom"><%= Res("Projects.Search.From")%></label>
<div>
<input id="projectSearchDateFrom" type="text" ng-model="startDate" ui-date="dateOptions"/>
</div>
<img class="clear" ng-show="hasStartDate()" ng-click="clearStartDate()" src="/_Layouts/ClientPortal/Images/layout/TA/delete-small.png" alt="<%= Res("ToolbarDialog.Clear")%> <%= Res("Projects.Search.From")%>" title="<%= Res("ToolbarDialog.Clear")%>" />
</div>
Now I can use showOn "both" or "button".
This is fixed in the latest release!