Both Angular and Rxjs give you the ability to easily listen to events. Using Angualr's @HostListener() annotation allows a declarative binding while using Rxjs event binding allows for filtering, debouncing and throttling of events, to name a few.
The prefered way to bind to events in an Angular component is to use the @HostListener() annotation. This provides a clean declarative binding from an event to a method, and best of all works without the need to access any browser specific APIs such as document or window. Avoiding direct browser DOM APIs becomes important if you want to use the Universal server-side rendering or run the applicaton in a WebWorker. Keeping your code open to either of these possibilities, even if you have no plan at the moment, allows you to take advantage of future optimizations.
Throttling Resize
You may ask, why do I need to listen to resize events?
You may not need to. As many of your UI elements as possible should respond to form factor changes through CSS cues rather than scripted cues. Mobile and Tablet devices don't have any real stories where the viewport changes sizes that aren't handled by the deviceorientation event. Their are probably some edge cases like split screen and some virtual keyboards pushing up on the browser window.
Their may be some exceptions to this where you will need to script the responsiveness. For example, you may have a menu that is pinned open on desktop form factor, pinned in a minimized mode on tablets in landscape mode, and hidden behind a hamburger menu on portrait tablets and phones.
Even with this scenario a valid situation where a user is resizing, not rotating, the browser window is at best an edge case.
In reality the use case is a developer story. As a developer you want to be sure that your UI behaves at every width between the smallest acceptable width to the largest. You want to be able to stretch your responsive browser developer tools from tiny to extra-large and ensure that the app will display correctly for every pixel width.
Nice and clean. But there is a problem with this. We are accessing the DOM window directly. We could wrap this in a try/catch, or inject a window into our component that gets replaced with a fake if we run on the server, but then if we decide to run in a WebWorker we are out of luck and will need to replace this code.
The Solution
We can use the Rjxs throttle method in conjunction with Angular's @HostLister() decorator annotations.
In our example below we create a Subject of type number named resizeSubject. We will call the next() method on resizeSubject in our event handler.
We create an Observable from our Subject and throttle it for 200 miliseconds. Then within our ngOnInit() we will subscribe to our throttled observable.
The prototype property of a JavaScript constructor function is one of the most often misunderstood aspects of JavaScripts OO paradigm. Luckily for everyone ES6's (ES2015) class keyword makes the need for attaching methods directly to prototype go away.
What about this?
It is common to see methods attached directly to this instance within the constructor. Is this a bad thig? If your constructor is a singleton, then no, it is not a bad thing. But it is 2016 and you are creating components with AngularJS, React, Vues.js, Polymer... whatever. These components are reusable class/constructors that may exist any number of times within a page.
Why is it bad?
When you define a method directly on this within the constructor, that method is not an instance method, it is a unique copy of the method for each instance. This will have a memory impact because the method will be duplicated for each instance.
Let's say, for example, that we are going to create a MineSweeper app. For each cell in minesweeper we will create a component. Let's use AngularJS 1.5 for this example:
In the above contrived example the onClick method would exist for each cell. If we have a 10 by 10 grid, we would have 99 more instances of this function than is needed.
As you can see that is much clearer than the previous example. This is a contrived example purley for demonstration purposes. The above method would would not have much of a memory impact. But in the real world your component methods are not going to be one liners.
When can I use this.method = function() {}
If you are only using your controllers or compoents as a single instance you will have no benefit from having prototype or class methods other than having a single clear coding style standard.
If you are working on a team, or your code will outlive your involvement in a project -- it may be unclear why some controllers or components are binding methods to this and some are using class instance methods.
Just for the sake of consistancy developers should be using instance methods in ES6 and prototype methods in ES5.
In JavaScript (and other dynamicly typed languages) data contracts are usually implimented manually. Even when data transfer objects are automatically generated breaking changes in your code will not be found.
For example, if your data contract changes a property name from firstname to firstName, in JavaScript both are valid property names. In TypeScript only the later would be valid. The hard to spot bug would be caught by the TypeScript compiler in CI.
This is where having statically typed data contracts can save a lot of time and effort. For developers who have worked with staticly typed languages this concept will be nothing new.
This is essential if you want to be able to catch integration bugs before they get to QA or production. Even fully automated inteagration tests can't catch every bug like this that are caught a build time using data contracts.
If you are using a statically typed language (C#, Java, Scala, Rust, Go, TypeScript, etc) you should be using data contracts for your Rest or JSON/HTTP services.
Infact this concept should not just be limited to TypeScript clients. You should be using data contracts with Java and Swift clients as well.
Where to Start
Two options available for generating your data transfer objects are:
Single langugage source where your client and server are written in the same langauge.
Generate your data transfer objects from one language to another.
Generate your client and server data transfer objects from a declarative schema language.
Single Language
This is the simplest option. You may have a Node.js backend and browser frontend both written in TypeScript. In this case you can simply consume the same TypeScript data transfer objects in both your client and server.
Languge to Languge
The second option is fairly simple as well. There are a few existing tools that will allow you to easily do this like:
TypeLITE that let you generate TypeScript types from C# classes.
ts-java that lets you generate TypeScript classes from Java jar files.
Declarative Contract
The third option is much more robust and removes tight coupling between the client and server. Instead both client and server will both consume the same data contract.
Two obvious choices for data contract language are XSD (XML Schema Definition) and JSON Schema.
Off the Shelf Tools
There are a number of tools to generate types in many languages from XSD and JSON Schema.
If the off the shelf tools don't fit your specific needs creating your own code generator is not rocket surgery.
Workflow
Let CI do much of the heavy lifting. When a breaking change is made to the data contract those changes should affect and break the builds of the client and server.
It may be asked, Why do we want to break builds? It is better to break a build rather than have runtime contract mismatches. The compiler will guide you in resolving the breaking changes.
We can import the data contracts into our client and server repositories as a git submodule. If we set fetch.recurseSubmodules to true each update will retrieve the latest changes to the data contract.
Ideally you will want to configure CI to automatically update the the Data Contract submodule in the Client and Server repositories when the Data Contract repo changes.
Once the workflow has been setup you will the flow will look something like this.
Changes are made to the data types in the Data Contract repository.
The Client and Server repositories are updated and the new data contract is brought in through the submodule.
CI will report build errors related to the data contract breaking changes.
This simple workflow can be expanded on. You will probably want to version your contract and tie specific consumer repositories to specific contract versions.
There are numerous advantages to using a statically typed language. A statically typed data contract is of the often overlooked but very powerful tools that can easily be implimented.
After working with AngularJS with TypeScript for the last couple of years there are a few best practices that seem to be missing from most tutorials.
These practices apply whether you are developing in plain old JavaScript (ES5), ES6 (ES2015) or TypeScript.
Code should never be future proofed, instead it should be extendable. Following these practices should help you create code that will easily be upgraded to Angular2 -- as well as be more maintainable.
Use External Modules
Using external modules will do two things for you.
When bundling your application external module use will automatically order your dependencies for you.
External modules will eliminate the need to use ///<reference path="..." /> notation.
Before the common use of external modules it was commonplace to use TypeScript internal modules, now renamed namespaces.
TypeScript namespaces are based on the JavaScript Internal Module pattern. This pattern came about because of the lack of module encapsulation in JavaScript. With the introduction of CommonJS and ES6 modules and module syntax, the internal module pattern should be avoided.
Use external commonJS/ES6 modules instead of TypeScript namespeces.
Don't use IIFE (Immediatly-Invoked Function Expression)
IIFEs are common in JavaScript development as they allow you to encapsulate your code.
(function(){// encapsulated closure protected from other code
})();
Using external modules eliminates the need for explicity wrapping your code in IIFE closures. Instead a build time task (browserify, jspm or webpack) and module system will bundle and load your code into closures.
IIFEs will also cause problems exporting types from your modules.
Only create AngularJS Modules for a purpose
Keep the number of AngularJS modules to the minimum needed. Have a reason for creating new AngularJS modules.
Such as:
Sharing common code among different applications
Making code more testable
AngularJS modules were created because of the lack of module system in JavaScript. With ES6 module syntax and commonJS modules AngularJS internal modules are a legacy artifact of AngularJS 1.x.
Define Services as Classes
Defining your services as classes, which AngularJS's DI mechinism will instantiate as singletons, will make your code cleaner, easier to read, easier to maintain and easier to test.
Lastly, defining your service as a class will make your code more friendly to static typing with TypeScript.
In the previous example we demonstrate clean type encapsulation using imports.
We import the type import {UserProxy} from './userProxy';
Then we use the imported type in the constructor injector, constructor(private userProxy: UserProxy) {}
Finally we demonstrate use of the static typing that was imported this.userProxy.login(model).then(
This is a much more manageable approach than using TypeScript internal modules/namespaces to access types.
Only use the Factory Method when Needed
In most cases your services will be singletons. Use the service method to register these with AngularJS's DI, app.service('userProxy', UserProxy).
The obvious use case for the factory method is when using the factory pattern. Let's use the previous UserProxy class as an example. For this example let's assume there are more than one JSON/HTTP end points that impliment this same API. We can make this class reusable with a factory.
Bind Directly to Controller Properties and Methods
Many examples still bind methods and properties to the injected $scope within the controller.
// DON'T DO THIS
exportclassPersonController{static$inject=['$scope'];constructor(private$scope:ng.IScope){$scope.name="Person's Name";$scope.save=()=>{// . . .
}}}
This is a bad practice for a few reasons.
This has a memory impact. Every instance of this controller will have it's own copy of each method bound to the scope. If the methods were instead defined as instance methods the implimentation will be shared across instances.
The API for this class is not exported and usable in unit tests.
With larger nested applications you will run into scope inheritance colisions. These colissions will cause strange behaviour that can seem to defy reason. They are often hard to track down.
It becomes easier to pass a value by reference when you include the properties parent object in the expression, person.name rather than name.
With ng-router and ui-router you will just need to name your instance of the controller with the controllerAs configuration property.
Use TypeScript for Unit Tests
One of the main advantages of using TypeScript are the type annotations for the classes you want to test. Fear of breaking tests should not prevent you from refactoring rotting code. Using types with your tests will help keep your tests clean and readable.
Summation
TypeScript can be a great tool to keep your code base and easier to refactor. Your services will have solid APIs that your controllers and components will consume. Bugs will be found at compile time rather than in QA or Production.
Even without the TypeScript, many of these practices can be applied to both ES6 and ES5. In ES5 you will just need to use commonJS require syntax instead of the ES6 import systax -- and the JavaScript Prototype Pattern instead of the class keyword.
Just a couple final thoughts:
Use a build system -- gulp or grunt.
Use NPM -- don't use bower. You only need one JavaScript package manager system. Bower is redundant.