I Have A Fundamental Misunderstanding Of Change Detection In Angular 2 Beta 8

Learning Angular 2 is, without a doubt, an uphill battle. Thankfully, most of the rock scrambles result in beautiful scenic overlooks. But, the one peak that I have yet to climb is the concept of change detection. For weeks, I have been baffled by errors in change detection using ngModel. But, at least in that case, I was actually trying to do something complex. This morning, however, I wasn't trying to do anything clever. In fact, I was barely trying to doing anything at all. And yet, I am still getting change detection errors. Right now, I feel like I need to get in my car, drive to Ikea, buy a desk, drive home, assemble the desk, and then flip it out of sheer frustration! Clearly, I have a fundamental misunderstanding of change detection in Angular 2, and it's making me feel hella dumb!

Run this demo in my JavaScript Demos project on GitHub.

To put a face to my frustration, I was experimenting with the ViewChildren feature in which Angular 2 will inject the component controller with an iterator containing embedded component instances. In my experiment, all I am doing is trying to set up the ViewChildren metadata and then echo out the number of embedded components that were found within the rendered view:

<!doctype html> <html> <head> <meta charset="utf-8" /> <title> I Have A Fundamental Misunderstanding Of Change Detection In Angular 2 Beta 8 </title> <link rel="stylesheet" type="text/css" href="./demo.css"></link> </head> <body> <h1> I Have A Fundamental Misunderstanding Of Change Detection In Angular 2 Beta 8 </h1> <my-app> Loading... </my-app> <!-- Load demo scripts. --> <script type="text/javascript" src="../../vendor/angularjs-2-beta/8/es6-shim.min.js"></script> <script type="text/javascript" src="../../vendor/angularjs-2-beta/8/Rx.umd.min.js"></script> <script type="text/javascript" src="../../vendor/angularjs-2-beta/8/angular2-polyfills.min.js"></script> <script type="text/javascript" src="../../vendor/angularjs-2-beta/8/angular2-all.umd.js"></script> <!-- AlmondJS - minimal implementation of RequireJS. --> <script type="text/javascript" src="../../vendor/angularjs-2-beta/8/almond.js"></script> <script type="text/javascript"> // Defer bootstrapping until all of the components have been declared. // -- // NOTE: Not all components have to be required here since they will be // implicitly required by other components. requirejs( [ /* Using require() for better readability. */ ], function run() { var App = require( "App" ); ng.platform.browser.bootstrap( App ); } ); // --------------------------------------------------------------------------- // // --------------------------------------------------------------------------- // // I provide the root App component. define( "App", function registerApp() { var Item = require( "Item" ); // Configure the App component definition. ng.core .Component({ selector: "my-app", directives: [ Item ], // Here, we are asking Angular to inject a QueryList for the // collection of Item directives rendered within our component // view. This will inject `itemList` as a public property on // the App component instance. // -- // CAUTION: View queries are set before the ngAfterViewInit() // life-cycle method is called, but after the ngOnInit() life- // cycle method is called. As such, the `itemList` property will // not exist until the View is initialized. queries: { itemList: new ng.core.ViewChildren( Item ) }, // Notice that our view has 2 Item instances. And, that we are // using the injected `itemList` property to display the count // of items currently rendered in the view. template: ` <h2> Item Count: {{ itemList?.length }} </h2> <item></item> <item></item> ` }) .Class({ constructor: AppController }) ; return( AppController ); // I control the App component. function AppController() { // ... nothing to do here. } } ); // --------------------------------------------------------------------------- // // --------------------------------------------------------------------------- // // I provide a super simple Component for no other purpose than to have a // directive that can be rendered in the App component. define( "Item", function registerItem() { // Configure the Item component definition. return ng.core .Component({ selector: "item", template: "This is an Item!" }) .Class({ constructor: function ItemController() { /* Nothing to do. */ } }) ; } ); </script> </body> </html>

As you can see, I'm not really doing anything. I'm only taking the data that Angular gives me and then I'm rendering it in my view. My controllers have zero logic. And yet, when I run this page, I get an error:

What don't I get? Where is my misunderstanding? Where is there even any opportunity for wiggle room in the logic? Why does Angular 2 change detection continue to confound and befuddle me?

And, so help me, if anyone even dares suggesting that I try using OnPush change detection, I will freaking curl up in a ball on the floor and start crying! Please don't make me cry. Change detection strategies are about optimization, not about avoiding errors.

The same goes for putting the code in "Production Mode". All that does it "cover up" the error, not avoid it.

Source: https://www.youtube.com/watch?v=CfPfDacXG4Y

Ok, sorry for the rant. I'm just feeling very frustrated right now because I cannot seem to close this knowledge gap on change detection. It's been weeks of experimenting and reading and looking at source code and still, I simply don't get it. I am feeling very less than intelligent right now. And I'm acting out. So I appreciate your patience during my temper tantrum.

Tweet This Groovy post by @BenNadel - I Have A Fundamental Misunderstanding Of Change Detection In Angular 2 Beta 8 Woot woot — you rock the party that rocks the body!







