Showing posts with label web. Show all posts
Showing posts with label web. Show all posts

Sunday, September 23, 2018

My Thoughts About Angular

Angular (not to be confused with AngularJS) is a web development platforms that I've used professionally and on a few personal projects.

Here are my thoughts on Angular and how I think it is compared to its peers (such as Ember, Vue.js, and React).

TypeScript Support (/Requirement?)


I think that one of the biggest things that separates Angular from other web development libraries is its TypeScript support. I'm a fan of TypeScript; it's a programming language that introduces static type checking to JavaScript.

As far as I know, Ember.js doesn't officially support TypeScript. React and Vue support TypeScript, but their documentation for it is somewhat sparse.

But Angular went all-out on their TypeScript support. All examples throughout the official site use TypeScript, and its API documentation has plentiful information about typing. When coding Angular apps using TypeScript, I rarely struggle with finding the right types to use.

But Angular's depth of support for TypeScript has come at an apparent price - I cannot find much information on Angular's official site about how to actually use Angular with JavaScript. I think that there used to be a page on the Angular site for using Angular with JavaScript, but I can no longer find it.

In my opinion, learning Angular requires having to know or learn TypeScript. Otherwise, you're learning Angular by writing code that has little in common with code examples.

Learning Curve


When I started learning Angular, I struggled. I picked-up TypeScript at the same time as I did Angular, so I had to learn both technologies at the same time.

Once I got past the "hello world" phase, I found out that I had a third thing to learn RxJS. Some things in Angular, most notably its HTTP Client, are built using RxJS, so becoming well-versed in RxJS is essential for fluent Angular application development.

It took me a while to understand Angular, TypeScript, and RxJS. But once I did, the act of making Angular applications felt natural. Angular suggests using an application structure (app/component/view/CSS) that is pleasant to work with and excels at separating concerns into manageable pieces. Angular's boilerplate is terse but is easily understood and serves an obvious purpose. And I don't spend a lot of my time looking up what to do, which allows me to focus on actually building applications. I still struggle a bit with RxJS, but I find it pleasant to work with in the context of an Angular app.

Ember.js (pre-2.0) was similarly tough for me to learn, but I never reached the point where developing applications Ember.js was easy for me. Its more "magical" aspects made it rather difficult for me to fully comprehend what my code was actually doing.

Compared to Angular and Ember.js, I had a much easier time learning React. But React is "just" a view library. Angular has more built into it, including dependency injection, URL routing, the aforementioned HTTP client, and extensive tooling.

Tooling


The largest tool in Angular's toolbox is Angular CLI. It's the recommended and best way to develop Angular applications. It handles quite a bit: application scaffolding, dev server instantiation, unit testing, and production builds.

But on one of my applications, I ran into issues with Angular CLI. I'll admit - I ran into an uncommon problem. I was working on a white-label site that's designed to work from any subdirectory, and I couldn't get Angular CLI to build correctly for the site. As it turns out, you don't have to use Angular CLI for your Angular applications. The official site used to have extensive notes about using both webpack (used by Angular CLI) and Rollup. It's mostly gone now, but it was present while I was working on the application, and I successfully built the app using both Rollup and webpack (which I migrated to because of issues with Rollup).

Angular's support for alternative build systems is there, but it's hidden away a bit.

Conclusion


When I build applications for myself, I tend to favor using multiple, small libraries over one large library. I preferred Sinatra over Ruby on Rails, Flask over Django, MVVM Light (and my own KSMVVM.WPF) over larger MVVM libraries for WPF, and React over the original AngularJS.

But I also like modern Angular - it seems every bit as purpose-built as the smaller libraries that I tend to prefer. And even though the documentation is very spotty when it comes to going "off-road", I can appreciate how it does its best to guide developers into a specific way of implementing applications by making decisions for you. Using Angular to its fullest requires using a specific language (TypeScript), a specific way of structuring applications (app/component/view/css), and a specific build system (Angular CLI/webpack). By making these decisions for developers, I feel that Angular is a great fit for larger web-based applications.

Thursday, April 2, 2015

An Article With Browserify Impressions

This is an opinionated article that discusses Browserify and compares it to RequireJS and Bower.

What is Browserify?

Browserify brings Node.js's require() function to client-side JavaScript. You can call require(...) to import npm packages, so Browserify solves two problems:
  1. Client-side package management (by using npm)
  2. Loading modules

Benefits of Using Browserify

Browserify has some 'magic' built-in that allows you to easily re-use most server-side code in your client-side code.

Because npm handles package management for Browserify, you can specify (most) of your client-side dependencies in your package.json along with your server-side dependencies. In my experience, having one place for dependencies really simplifies the process of adding a new dependency. Otherwise, you have to choose which package manager you want to use for packages that are available through multiple managers.


Problems With Alternatives

Like everything in JavaScript development, there are alternatives to Browserify. RequireJS can be used to load modules. For client-side package management, there's Bower. Either solution has problems that would be reduced or eliminated by using Browserify instead.

RequireJS requires using RequireJS modules or shims. Jam provides packages for RequireJS, but I found extremely out-of-date packages for popular libraries like Moment.js and Modernizr. For the most part, using RequireJS with popular client-side libraries means using shims. RequireJS shims can lead to a few minor problems as you either have to copy & paste JS files into your project or split dependencies into multiple specification files. Browserify shares this problem with RequireJS, but it's a smaller problem for reasons described below.

Bower has some problems of its own. I'll explain, but I must warn you ahead of time: friends and family of Bower should skip to 'Problems with Browserify.'

Bower is an incomplete package management solution. Every Bower package that I have ever installed dumps the complete contents of a Git repository into a directory. And there does not seem to be a standard for Bower package structure or documentation or use in client-side applications.

Every time I use a Bower package, I'm left wondering how to actually use the package in my code. I feel that this is a massive issue that prevents Bower from being as useful as it could be. npm and Browserify typically do not suffer from this problem for a few reasons:
  • npm packages almost always tell you how to start using the package
  • You typically don't need to know the structure of npm packages due to how they're used.
  • If you need to look in the node_modules folder, packages typically use a standard structure.

Problems With Browserify

Browersify is a full solution, but it comes with problems of its own.

It's unclear what npm package should be used for some client-side libraries. Modernizr has a few npm packages: browsernizrmodernizr, and browsernizr2. The first package is outdated, and the second package installs a tool that generates modernizr.js. I only found out about the third package as I was doing research for this article, and I don't know anything about it except its existence.

If a library doesn't have an easy-to-use npm package, you have to shim it with browserify-shim. This complicates inclusion of client-side vendor scripts in your project, and Browserify shares this issue with with RequireJS. Unlike RequireJS, this problem should be uncommon due to the availability of npm modules for popular client-side libraries.

Addendum: Comparison of Browserify and RequireJS

Browserify and RequireJS solve most of the same problems, so I figured that now would be a good time to give a quick rundown of differences and similarities.

Differences

Browserify loads npm modules.
RequireJS loads RequireJS modules.

Browserify always loads synchronously at compile/bundle time.
RequireJS loads asynchronously in the browser (but can be bundled using an optimizer).

Browserify always bundles scripts.
Bundling RequireJS apps requires a special optimizer.

Browserify uses npm.
RequireJS can be used with Jam, but you may be better off with using shims for third-party code.

Browserify allows you to access many Node.js functions and modules from client-side code.

RequireJS allows you to load non-JavaScript files (such as text files and Ember.js templates).

Similarities

Both load modules for client-side JavaScript.
Both have Grunt and Gulp plugins.

Sunday, August 24, 2014

RequireJS Impressions

RequireJS is a JavaScript library for defining and loading modules. It provides an alternative to including multiple <script> tags in your HTML in the right order and defining countless global variables (or properties for global variables.)

I've been using it quite a bit lately, so I have some opinions about it.

Good News

I like that RequireJS works with many browsers. The official site claims compatibility with ancient versions of every major browser. And have you seen its source code? A piece at the top checks to see if it's running on the Playstation 3's browser and does a little workaround just for it. I get the impression that RequireJS will work on anything that one would need it to, and that's a very good thing when you're building web sites that need to work everywhere.

"Ehhh..." News

Module names are case-sensitive. If you get the case wrong, RequireJS will happily reload modules. If those reloaded modules do anything to global state, you're heading for headache.

I could find only one bundling tool that properly bundles RequireJS modules together: the optimizer (r.js) that comes with RequireJS. This tool has "experimental support for source maps" (source: RequireJS Docs - Source Maps), so not using source maps poses two problems:
  1. Serving individual files for development and optimized files for production
  2. Actually using those files in each environment
Solving both of these problems can get tricky, especially in the ASP .NET MVC 4 environment that I've been working with. The easier of the two problems is the first: force the asset pipeline to use the output of r.js instead of its own optimized output. Easier said than done, but it's not impossible.

The second problem, actually using the files, is difficult and gets interesting when you're to use the application cache. One solution that I came up with is absolutely terrible and I will not be sharing it because it's wrong in so many ways. But I came up with it because there isn't much documentation available for taking full advantage of RequireJS's optimizer. Currently, it's a few sparse details and a (non-working) configuration file they put up on GitHub. This documentation could use some serious improvement.

These warts, while major, won't stop me from using RequireJS. It provides worthwhile functionality to client-side JavaScript. Browserify seems like a worthwhile replacement that fixes many of my RequireJS gripes, but I haven't used it yet and its compatibility is not as good as RequireJS's.

Saturday, July 12, 2014

jQuery Color & Body Background-Color WebKit Bug

jQuery Color is a nice jQuery plugin that allows you to animate color changes and much more. When testing a project that I was working on in Chrome (on Windows and Mac), I stumbled on a weird graphics glitch that occurs when animating a background color change for the entire page.

(Screenshot taken from latest Chrome for Windows)
See the light grey stripe at the bottom? It's supposed to be the same color as the rest of the page.

As far as I know, this is not a jQuery Color bug but rather a WebKit issue because it also affects Safari on Mac.

There are quite a few workarounds.

Fix #1 - Force redraw in animation done handler


The accepted answer is very simple; it's just three lines of JavaScript and doesn't require jQuery.

Using this fix with the entire body presented a problem with my project. These rapid changes to display make the page behave like it's scrolling back to the top.

Fix #2 - Apply two-thirds of clearfix to empty div/footer

My instincts told me to add an empty <footer /> to the bottom of the screen and give it clearfix styling. Clearfix corrects this bug. You can actually use two-thirds of a certain recommended clearfix:

footer:after {
    content:"";
    display: table;
}

(It's missing clear:both;)

<footer /> can be changed to <div /> or any number of other elements.

Fix #3 - Apply two-thirds of clearfix to body

Adding an empty HTML element seems like a bad idea to me. HTML5 is supposed to be about content, and here I am adding an element just to fix a browser-specific presentation bug.

As it turns out, you can use this bit of CSS and skip the empty footer:
body:after {
    content: "";
    display: table;
}

I don't know why this works

Maybe I'll update this post later when I figure it out.

Wednesday, January 15, 2014

Sinatra Caches Templates

Yesterday, I learned the hard way that Sinatra caches templates when running under a non-development environment by default.

How I Found Out

My company site (Leavins-Software.com) was having serious issues where changes in layouts were not reflected in web browsers. I originally thought that this was a bug in how I set the Last-Modified HTTP header. Then I looked closely at Firefox's web console (specifically, the 'Network' tab). Last-Modified was being set properly!

I started making template changes in production to see what the real cause was. Please don't try this at home. I only did it because I'm a cheapskate working on a low-traffic site.

The test page seemed to update with every other change (which is odd behavior). Last-Modified was being updated properly, but the page wasn't changing. And then I got lucky: I was served an old version of the site with a properly-set Last-Modified date! Then a question hit me: "does Sinatra cache templates?"

Yes, Sinatra caches templates. Just not under the default development environment.

How Sinatra Caches Templates

Sinatra uses Tilt to render templates. While digging around Sinatra's source code, I discovered that Sinatra also uses Tilt's built-in caching for rendered templates. Tilt uses a Ruby Hash object for caching, meaning that Tilt's cache is per-instance.

This is different from how Rails does this. I'm not a Rails expert (I should probably change this if I want to continue working with Ruby), but I know that it uses a variety of different caching methods including page caching, and, to quote Caching with Rails, "Page caches are always stored on disk."

Each Sinatra app instance has its own cache, and I managed to hit the instance with a previously cached version of the template.

There is a Sinatra app setting named 'reload_templates' that controls caching behavior. The default setting is "true" for development and "false" for everywhere else. This is why Sinatra doesn't cache templates by default for the development environment.

Fixing Related Issues

The easiest way to resolve a template update issue is to just restart the Ruby app server. Really!

Friday, November 8, 2013

Getting Started With Bourbon Neat (without Rails)

What is Bourbon Neat?

Bourbon Neat is a Sass (CSS extension language) framework that provides mixins used to make responsive grid layouts for sites. It's built on-top of Bourbon, a helper mixin library also for Sass.

Bootstrap and similar front-end frameworks are great, but sometimes, you want something that's lighter weight. It's very difficult to use Bootstrap with an existing site while keeping the original look of the site, and besides, all I need is a responsive grid. That's where Bourbon Neat comes in.

This post will show you the basics: it covers setup and basic use of Neat in a non-Rails Ruby project.

Initial Setup for Bourbon Neat

I will go ahead and assume that you:
  • Have at least one existing sass/scss file, preferably in its own directory
  • Are using Bundler for your project
Step 1: Add gem "neat" to your Gemfile
Step 2: Run bundle install
Step 3: Go to your Sass directory and run bundle exec bourbon install && bundle exec neat install

This will add files for Bourbon and Neat to your Sass folder. Should you add these files to version control? I'm not sure what the best practice is, but I went ahead and added them to version control on my project.

Using Bourbon Neat in Your Sass File

Drop the following two lines at the top of each main Sass/SCSS file:


@import "bourbon/bourbon";
@import "neat/neat";
 
By themselves, these two lines don't change output CSS much. Including Neat adds some CSS that sets box-sizing for all elements to 'border-box.'

The official Bourbon Neat site has a sample that shows you how to build a semantic grid. The basic principle is that you need something that includes the 'outer-container' mixin and at least one element nested within it that includes the 'span-columns' mixin.

Using Bourbon Neat for Responsive Styles

One of the many nice things about Bootstrap, Foundation, and other heavyweight style frameworks is that their grid systems are very responsive to changes in browser/device width. They'll do things like change a row with two 6-column divs to look like two rows of full-width divs. Bourbon Neat allows you to do this too, but you manually have to specify settings like width breakpoints and what columns do at those breakpoints.

Here's an example SCSS file using Bourbon Neat:

@import "bourbon/bourbon";
@import "neat/neat";
$desktop: new-breakpoint(min-width 768px 12);
$tablet: new-breakpoint(max-width 768px 1);

.header {
  @include media($desktop) {
    @include outer-container;
  }

  .page-title {
    @include media($desktop) {
      @include span-columns(7);
    }

    @include media($tablet) {
      display: block;
    }
  }

  .site-nav {
    @include media($desktop) {
      @include span-columns(7);
    }
    @include media($tablet) {
      display: block;
    }
  }
}


Neat includes a 'media' mixin that allows you to specify styling for a specific breakpoint. As far as I know, Neat has no built-in breakpoints, so you must use the 'new-breakpoint' function to make them.

February 17, 2014 Update: Simplified example file and removed incorrect information about an error that can occur within new-breakpoint.

Friday, August 23, 2013

Using AngularJS with Bootstrap Form Validation

If you are using AngularJS with Bootstrap (formerly Twitter Bootstrap), you will want to use AngularJS's form validation with Bootstrap's form classes. This can be surprisingly tricky and requires careful use of AngularJS's ngClass directive.

This is best illustrated with sample markup. It's extracted from a project that I'm working on:


The general idea behind this is that you need to set Bootstrap's 'has-error' class when loginForm's email field is invalid. Simple enough, right?

AngularJS introduces two issues that are present in the latest unstable version (1.2.0rc1). They could be in previous versions, but I didn't check.
  1. You must have quote marks around the name of the 'has-error' class in the ng-class expression.
  2. The input must be bound to the model by using AngularJS's ngModel directive
If you just put ng-class='{has-error: loginForm.email.$invalid}' your JS will crash within AngularJS because it's an invalid expression.

I have little idea why data binding is required. I suspect that AngularJS requires it because it expects you to use the field's contents somewhere else in your app.

Thursday, March 21, 2013

I Like AngularJS (So Far)

I am building a commercial web app using AngularJS that is currently around 3000 lines of JavaScript code (including comments and spacing). I like AngularJS and I'll tell you why.


Friday, February 15, 2013

Chrome Doesn't Allow Decimals In input type='number'

The current 'main' version of Google Chrome (24.0.1312.57 at the time of this writing) does not allow numbers with decimal points in input text boxes of type 'number'. Mozilla Firefox (18.0.2) and a few other browsers do not have this problem.

This decimal-related behavior is apparently not a flaw in Google Chrome.

Issue 44116: input type=number won't accept decimals

This issue is marked 'WontFix' and the behavior seems to be standards compliant. Included in the issue is a fix: specify the step attribute of the input. In Chrome, the default value of 'step' is '1', meaning that it only accepts integer input. If 'step' is set to 'any', the field allows decimal input.

So, to summarize:

This is bad, but works in Firefox:

<input type='number'
       name='amount' />


This is better for Chrome:

<input type='number'
       step='any'
       name='amount' />

Saturday, September 22, 2012

Quick Guide to Adding a Mobile Stylesheet to Your Site

Do you want your site to look alright on a mobile device? Do you want to avoid using Foundation, Twitter Bootstrap, or any other front-end web framework? Here’s a four-line guide to having a separate stylesheet for iPod Touch/iPhone 4 (and maybe some other devices).

Put this in your web page’s header:


<link href="/css/main.css" rel="stylesheet" media="screen and (min-device-width: 768px)">
<link href="/css/mobile.css" rel="stylesheet" media="screen and (max-device-width: 767px)">


Where “/css/main.css” and “/css/mobile.css” are your desktop and mobile style files.

What with the 768?

It’s the width in the screen resolution of 1024x768. That’s the resolution of the iPad 2, so users on the iPad 2 will use the desktop stylesheet.

What with the 767?


It’s a quick-and-dirty fix so that devices like the iPad don’t try to load both styles at the same time. Foundation’s CSS file constantly references this number with regard to mobile devices, so I might be onto something.

Will This Work With Retina Displays or Tablets?

You need to tweak the numbers a little for tablets. I don’t have access to a device with a Retina display.

Can This Break My Existing Desktop Layout?

Visitors stuck with a display resolution of 800x600 will be forced into viewing the mobile layout. Twitter Bootstrap (at the time of this writing) does the exact same thing. Foundation only does this for visitors at 640x480! Then again, you’ll have to make significant changes to your site’s markup (and existing styles) to use Bootstrap or Foundation.

Can Resizing the Window Force My Page to Use the Mobile Style?

Nope. The media attributes list ‘min-device-width’ and ‘max-device-width’ instead of ‘min-width’ and ‘max-width,’ so resizing the browser window will not change the style. If you want this behavior, use ‘min-width’ instead of ‘min-device-width’ and ‘max-width’ instead of ‘max-device-width’.

Is This Four-Line Guide Perfect?

I’m not an expert in designing interfaces for mobile devices, so no. Use those four lines of information at your own risk!