10 Sep 2014By design, in Angular, all forms and inputs are $invalid
by default. They are also $pristine
, meaning they haven't been touched. So if you need to display an error message next to a field, you not only have to check if the field is $invalid
, but also if it's $dirty
.
Now imagine you load your page, containing a form. You have several fields, and none have any validation rules applied (no ng-required
, nor ng-pattern
). At this stage, you form is $invalid
, because at least on of its fields is $invalid
.
Each field will switch to $dirty
when a value is typed in it. Switching to $dirty
will also trigger validation rules (if any), and updating its $valid
/$invalid
property accordingly. When all fields will become $valid
, the parent form will also automatically become $valid
.
The problem with that is when you want to submit your form without entering anything in any of the fields. Your form is considered $invalid
. You have to manually focus each field and enter a value to make it become $valid
.
I haven't found a nice way in Angular to trigger validation rules on all fields of a form at once, to recalculate its validity. What I've found is that if I update the viewValue
, then the field becomes $dirty
and validation rules kicks in and can pass my field to $valid
. So updating the viewValue
with itself (thus, not changing anything) becomes a hackish way to trigger validation.
So I coded the following method. It takes a $scope
as argument and will loop through each form in the scope, and trigger each field validation in this form through the setViewValue
hack explained above. It will also handle nested forms.
function setAllInputsDirty(scope) {
_.each(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form
if (value.$addControl) {
return setAllInputsDirty(value);
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
return value.$setViewValue(value.$viewValue);
}
});
}
Note that it does some weak duck typing to check if an element is a form or an input by checking for some methods. This will easily break, so be careful.
06 Sep 2014Ever had your UTF-8 string wrongly encoded in your output .css
files when using Sass ? Well, it might be an issue with the way Sass determines the encoding of your files.
Starting from Ruby 1.9, Sass will now try to guess the encoding of your sources files before processing them. The algorithm used is defined in the documentation but comes with a few caveats.
It will first check for the BOM in your file, and if found will consider the file as being UTF-8. Using BOM is a very bad practice when dealing with UTF-8 and you should absolutely not include it.
So, if no BOM is found, Sass will then look for a @charset
definition. It looks for it exactly as the very first character on the very first line of the document. If not found, it will then use the Ruby default encoding.
And when you're on windows, the default encoding is usually CP1252
. And in the end, you end up with every UTF-8
char encoded as three separate CP1252
chars.
Solution ?
Well, as adding a BOM is definitely not possible, you could add a @charset
rule on top of EVERY SINGLE SASS FILE. Which is not a good solution either. Changing the encoding OS-wide is not better.
What you can do is force the output encoding with the -E
switch when using sass on the commandline : sass -E utf-8 main.css
. And this will correctly convert everything.
Unfortunatly, if you're using Compass in addition to Sass, there is no easy way to pass this switch to the underlying sass. What you can do instead is editing the config.rb
file that comes with your Compass project and add the Encoding.default_external = 'utf-8'
line. This will change the Ruby default encoding and output utf8 correctly.
But hey, what if you're using compass through the grunt-contrib-compass
task ? There is no config.rb
file that you can edit, and the task does not expose the needed configuration switch. Your only solution is to use the raw
option to pass the Encoding.default_external
config.
{
options: {
raw: 'Encoding.default_external = \'utf-8\'\n'
}
}
Mind the \n
at the end and the quote escaping. This is needed for the task to correctly parse.
Hope this helped. Note that all this tweaking is not necessary if you're using Ruby 1.8.
24 Aug 2014I have been coding in CSS for a few years. I started when IE6 was the major browser but had to stop when HTML5 started to became a buzzword. Too bad. At that time, I had a job where I mostly handled server-side stuff and couldn't really keep on the newest CSS topics. Then I took a year off abroad.
Now that I'm back, I decided to get up to speed with all the goodness the CSS world now has to offer. I decided to rewrite the default Hyde theme of this blog with my own to better understand how it's done. Here is what I learned along the way.
SCSS
This is the first time I'm using a preprocessor, and the simple joy of being able to use variables and mixins is great. The nesting is nice but I try not to use it too much. I'd rather have a OOCSS naming convention than too much hierarchy in my selectors.
Autoprefixer
I added autoprefixer instead of SCSS mixins to handle cross-browser compatibility as it seems a more future-proof approach. It leaves the underlying SCSS files simpler to read.
Fonts
I grabbed a font on FontSquirrel to use on the titles. I simply copy-pasted the relevant CSS code, but still do not understand all the details of it. I'll have to read a bit more to really understand the browsers it covers.
rem
I started using rem
units as most as I could. This really removes the burden of having to handle the math involved in using em
. I still had to use px
for media-queries, though.
media-queries
I still struggle in remembering which one of max-width
or min-width
I need to use in a given context, but otherwise media-queries are great. I coded first for the perfect (or optimal) layout, and then added media-queries to handle bigger and smaller screens.
In my case optimal means enough space to display the sidebar and the text in a way that's easily readable. I then transformed the sidebar into a header for smaller screens, and even added a menu icon to display the menu on really small screens.
It's nice how SCSS lets you define media-queries inside of selectors, and using variables for you breakpoints. This really fluidified the way I write code.
CSS tricks
I had to resort to two small CSS tricks (or mind puzzle) for this new design. I like bending my mind to find creative solutions with a declarative language like CSS. Here I created a menu (hamburger) icon in pure CSS using :before
and :after
borders. I also displayed a menu when this icon is clicked using a <label>
bound to a checkbox
and selecting the menu with a clever +
selector.
I really like not using images or Javascript for this kind of things. CSS is a powerful language, but a hard one to master.
24 Aug 2014If you're using a custom font on your website, you'll probably end up with a bunch of font files in various extensions. But do you know which Content-Type
to use for each type of file ?
SVG
svg
fonts are the easiest. The svg format is well documented, and svg fonts are not very different from svg images. Its mimetype is image/svg+xml
.
EOT
The proprietary Microsoft format has its own (weird) mimetype, application/vnd.ms-fontobject
.
WOFF
Since 2013, woff
also happen to have its own registered mimetype, application/font-woff
.
OTF & TTF
Those two do not have a registered mimetype. You could get away with defining an application/octet-stream
. But if you want to gzip them (and you should), you often need a more precise Content-Type
, one that is not shared with any other type of files.
So to this end, it is considered acceptable to define a custom mimetype of application/x-font-opentype
and application/x-font-truetype
. The x-
is here to tell that it's a custom mimetype and that it should be treated as binary.
08 Aug 2014I love angular for the simplicity offered by the two-way binding between form inputs and displayed values. It also has pretty good mechanisms for validating and sanitizing data.
But I hate angular for how it treats the HTML markup as a second class citizen. You have to pollute your markup with all sorts of attributes, and it ends up with a great deal more logic than it should.
Angular is good for making complex forms, so you'll write quite a bit of them. You'll end up with some very long views, with a great deal of redundancy. This is not DRY at all, and it leaves a bad taste in my mouth.
Let's take a sample form :
<form name="subscription" novalidate>
<label for="firstName">
<input type="text" name="firstName" id="firstName" ng-model="firstName" ng-required>
<label for="lastName">
<input type="text" name="lastName" id="lastName" ng-model="lastName">
<button type="submit">Submit</button>
</form>
Give them a name
Every input field should have a name
attribute. Otherwise you won't know which input holds which value once submitted to your backend. Even if you're not submitting the form in the usual web 1.0 way but using fancy-ajaxy-asynchronous-SPA mechanisms, you're still basically sending a list of key/value to your backend.
Adding some validation
Let's say I need the firstName
to be required. Good thing I'm using Angular, I only have to add the ng-required
attribute and I'm done.
Well... not quite. You'll also have to add a novalidate
to the parent form
otherwise the browser default validation mechanism might interfere.
You'll also need to add a name
to the form
. And make sure that neither the form name
nor the input name
include any dash (-
), otherwise it might silently fail.
So what about accessibility ?
For accessibility reasons, you have to bind your labels to the corresponding input
elements. For radio
and checkbox
element you can get away with wrapping the actual input
inside the label
, but for text element the for
attribute on the label
must be used.
The value of the for
on the label must be equal to the value of the id
on the input. That's too bad we have to use an id
, it would have been so much easier if we could simply reuse the name
field. But no, let's repeat ourselves and add an id
field.
How do I access my data ?
Now that you have a form and Angular, you'll want to access the data right ? There are two ways you can access data from your form in your Controller.
If you give a name to a form, you can access it in the scope simply by referencing it by name. In our example, you now have access to $scope.subscription
on the Controller. I don't really like Angular defining objects in my $scope
from something defined in the view. It might overwrite another object with the same name I defined in my controller.
Anyway, you can then access informations about the form from this object. It contains keys for each input fields (using their name
attribute), which in turn holds informations about their validation ($valid
, $invalid
, $dirty
, $pristine
) and the value it holds ($modelValue
and $viewValue
).
I don't really like tapping into this object either as it seems too low level into Angular own plumbing system.
The other way, the recommended one, is to manually define with which value each input is bound, using the ng-model
attribute. You'll usually re-use the same name you already used for name
and id
(unless you want to be really confusing).
Copy/Paste nightmare
As you've seen, this absolutely does not follow the DRY principle. Angular, contrary to Ember, is not a Convention over Configuration framework, and we can clearly see this weakness here.
This gets worse when you have to add new fields to your form. You won't rewrite them from scratch, so you'll use copy/paste and expose yourself to errors. If you forgot to change one name
, id
or ng-model
you might create weird bugs, hard to find, because their root cause is inside your 300 lines long view file.
A solution ?
I haven't found a satisfying solution to this problem as of today. The only one I can think of could be a macro in your IDE to auto-generate input fields with the right name written in name
, id
and ng-model
. But this is hardly a portable solution. The other one would be to create a metadirective that automatically adds the correct value in the correct attribute. But writing directives that adds other directives is complex and I'm not sure if it will not simply hide the complexity away and make it even harder to debug.