Thursday, January 5, 2017

You hear this one asked all the time: how do I get a DOM element from a template?

Read more about it here:

Tuesday, January 3, 2017

Now that I've redone my website--a decade overdue--I'm starting to collect my writing there.

I've written a number of ES6 and Angular2 articles:


I'm sure I'll post here again, and I'll try to keep the links updated now and then. 

As always, thanks for visiting!

http://www.tcoz.com

(Articles are in the Errata area). 

Saturday, December 24, 2016

Monday, April 18, 2016

When a MYSql Stored Procedure Error isn't: Syntax Error on Line 1

This one cost me three hours today.

Easiest stored procedure in the world: originally much more complex, but I base cased it.

CREATE DEFINER=`root`@`localhost` PROCEDURE `myStoPro`(
IN p_keyval VARCHAR(45)
)
BEGIN
INSERT INTO touchhistory (
keyval
    )
    VALUES (
p_keyval
    );
END

No good. Check your SQL Syntax at line 1 near ')'.

Oh did all the SQL guys got on about this one. You are defining the query incorrectly. You have to do this and that, etc. etc. None of it made sense, the basic documentation clearly shows this is a valid stored proc and it validated in two MySQL tools. Regardless of "better" ways, this way should work and work fine.

So, I removed the query from the DB entirely. And STILL got the same error.

So I knew it had nothing to do with the query at all, and went to the Python code.

It turns out that you can receive this very misleading error if you are using the Python MySQLdb interface. If any of your arguments are previously unset/undefined variables, it will throw the Stored Procedure Syntax error.

for instance, say you do this:

cursor = db.cursor()
args = [keyval] # note that keyval has not been set, previously declared, etc
cursor.callproc ('myStoPro', args)

You will get the MySQL stored procedure syntax error.

Sunday, January 10, 2016

Angular: Testing (Karma/Jasmine) "controllerAs" Controllers that use External Templates and ng-repeat


I've been working with Angular professionally for quite a while now, hours and hours every day. Over time, you learn a lot about what's good and bad, and adjust. So, some of the ways I do things are not what you'd typically expect based on the usual books and Stack Overflow answers. For instance, I never use link functions...ever...and everything is a module. The only things those modules contain are controllers, directives, and services (and I do use .constant, that's handy).

This thinking tracks with Angular 2.0, where they have done away with all the variety of services and so on (everything is a component). So be prepared to let that all go.

That said, this article is an attempt to pull together many things that I have been asked and sought answers to in relation to basic project structure and testing. Those things include:
  • Should I use JQuery, or is JQLite enough?
  • How do I do basic tests of a controller if I use ngRoute.
  • How do I test directives with "controllerAs" syntax.
  • How do I access the controller of a directive compiled with controllerAs syntax.
  • How do I test a directive contained in a template of another directive is working. 
  • How do I trigger an ng-repeat in a compiled directive, and test that it's working.

The project and code are deliberately bare bones, focusing on the basic code. Robustness is absent to minimize the code you need to understand.

Here's the GitHub link. I will reference that as opposed to repeating the code in this article.

https://github.com/tcoz/test-nest-directives

Requirements

You have node and the latest Chrome installed (you can add Firefox if you like, but I use Chrome), are familiar with the basic use of npm and running a simple node script.

View the README for more detail, including what commands fire up the app and to run the tests. If you want a list of the deps, open up package.json.

You should also be familiar with the basic setup and use of Karma and Jasmine; this article isn't a tutorial on that tech. If you don't know the basics of how to use these technologies and you're an Angular developer (or really any kind of JS developer), you should stop reading this article right now and head over to the Karma site. Return when you've done a basic tutorial.

JQuery

I use it for the powerful selectors in my directive template testing. JQLite's .find ( ) only works with tag names; if it worked like regular JQuery, I wouldn't use JQuery, but JQLite's .find ( ) is so hamstrung as to be kinda useless. Yes, you can work with and around the limitations of JQLite, and you can always use native selectors via $document and so on (and you may have to if JQuery is verboten). I just prefer not to. As long as you make sure you don't slip into "the JQuery way" in your Angular code, you're good.

Karma and Jasmine

Take a look at karma.conf.js. It's almost entirely the default generated by Karma. The one thing to really note if you're not familiar with it is the last "files" entry, and the preprocessor.

'**/*.html' means, "grab html files in nested directories off the base path". Since that's where all my template files for directives are, that's what I want. If I needed to exclude a file I could always add it to exclusions, and there are other options for this kind of "find all" thing, but this is all I need for my sample project.

Take a look at that preprocessor section. You see that I've pointed the preprocessor 'ng-html2js' at the '**/*.html' files. This tells Karma where to find my html template files (nested directories). The preprocessor is a plugin and does not come by default with Karma; take a look at the package.json and you'll see it's an explicitly installed dependency. The 'ng' should tell you that this is an Angular thing. There's another version available for non-Angular projects. I recommend you at least read some of the basics on this preprocessor, it's extremely useful.

Basic tests of a controller (mainview-controller.js, mainview-tests.js)

Pretty straightforward. If you look in app.js, you see the usual .config ngRouter setup; note the "controllerAs" syntax here. MainView and its controller are also pretty simple. Note the dependencies injected into the MainView module (at the bottom of the file). Sure, you could do all this upfront in app.js, and in some projects that's fine (all dependencies just hang off off the main app module), but that's contrary to the notion of everything being an independent module.

In mainview-tests.js, you see what you expect with one possible exception. You inject the module, get the controller using $controller ( 'NameOfController' ), feed it  the required $scope data, and proceed happily testing away. The exception is that 'NameOfController' is not the ngRoute 'controllerAs'. It makes sense if you think about it, since in your test you aren't using a page load via ngRoute to instantiate the controller.  Otherwise this should all be business as usual.

Testing a Directive that defines its controller using ControllerAs

There are two directives in the project; parent-directive.js, and child-directive.js. In the template for MainView, you'll find markup for a parent-directive. In parent-directive's template, you'll find markup, within an ng-repeat, for child-directive.

First, think though the flow; ngRoute loads MainController and it's corresponding view (mainview.html). That view contains a parent-directive, and feeds it an array as the "data" arg; notice that the array is accessed using the 'controllerAs' name defined in the ngRoute config section of app.js.

Parent-directive in turn compiles and runs the required number of child-directive(s), feeding them items using ng-repeat. Nothing fancy here.

We've already tested that the data for parent-directive exists and can be found on MainView's controller. Now, we want to test that when we compile and run parent-directive with some sample data, we can do so easily enough. Sure, we could always create some dummy data. But why not use the data that is actually on MainView's controller?

parent-directive is nothing unusual; you define the directive, define the props you want on the scope (isolates scope), point at the controller, define the controllerAs, and off you go. No link function, no nada, you don't need it. Just work with the $scope injected into the controller, and leave the scope alone otherwise; all handlers and whatnot go on the controller, and that's where you access them.

The most important thing to notice in parent-directive is how the html templates are made available, and how controllers are accessed.

I won't go into a long explanation on the html templates. I've seen many different ways to do this and of course every Angular developer will tell you their way is the "proper" way (and get angry when you even suggest it might not be). So I'll just say, this is how I do it, it's supremely testable, and nobody I work with has ever rejected it (in fact I get a lot of, "wow where'd you learn this," the answer being, "in Hell").

First note that we inject whatever html templates will be required to run this directive module. This includes the templates used by child directives. If you leave them out, when parent goes to compile child, you'll see a GET failure in the console.

Then we have to load the $templateCache. It might be worth some reading on your part if you've never gotten the gist of what $templateCache does. For now just know that even though we configured Karma to see our templates (the preprocessing stuff mentioned before), when we compile our directive in a test, it (the directive) will attempt to load the required template as usual (causing an error), unless (evidently) it is populated in the $templateCache. If it is, the text won't actually try to load the page and the test will complete.

Note that here I only load up the $templateCache entry for the directive we are compiling (parent-directive)...not the child-directive's template.

With that done, we move onto setting up the scope and accessing the controllers.

To grab a simple controller, like MainView, you just use $controller, the name of the controller (remember, not the "controllerAs" name), feed it the scope, and you're done. Now I can grab the array on MainView's controller and feed it as the "data" arg on the scope of the directive instead of using something dummy (nothing wrong with that, just illustrating the point here).

Grabbing the controller off of a directive using an external template and controllerAs is different. Here's the flow:
  • Set a reference to $rootScope so I can use it throughout the test suite if needed. You don't have to use $new ( ). 
  • Grab the MainView's controller the same way you did in mainview-tests.js. 
  • Set whatever data you want from the controller (or just dummy stuff) onto the scope.
  • Set up your directive template. Notice that the "data" arg feeds in the data by whatever you named it on the scope (which should match the scope spec in the directive, for instance, scope : { data : '=' }. The fact that both the attribute and the val are both "data" is just a result of my convention of using "data", you can call it whatever you like.
  • Compile the directive. Because we set up the $templateCache, it'll find it's template.
  • Apply the scope (no...you don't need to use $digest. $apply works fine and is, as far as I know, the recommended mechanism). This will, for example, make the directive use the data and generate the items for ng-repeat. I put this in bold because it can be VERY confusing to find out how to do this if you just search for it. This, for me, is the most testable and terse way to get the job done.  
  • Grab the directive's controller by using either scope ( ) if the scope isn't isolated, or isolateScope if it is. A catch all is "dirt.scope ( ) || dirt.isolateScope ( )". Again, note that this is different than getting a simple view controller. 

The child-directive test is more or less the same as the parent-directive. To test the data in child-directive, I use a dummy object. I could have instantiated a MainView controller again like you've seen previously, but wanted to illustrate the use of dummy data.  

So that's it. This is most of the baseline structure I use for all my pre-2.0 Angular work these days. I tend to find it much simpler to work with, and more straightforwardly testable, than other seeds I've found.

As always, thanks for visiting. 















Tuesday, October 6, 2015

XBox IE and Angular dynamic styles

Some errata for you:

I was testing an application across some tablets, laptops and phones, and found some of the usual required UI tweaks to make bootstrap a little more cooperative and so on, but this one I just ran into and it seems particular to IE on XBox, which I tested just for the heck of it.

I had coded up the styles inline to get it right before moving it to CSS; this was just me working through it quickly, so I know that the second way here is more correct, and that switching
on the ng-class is typically preferred.

If you set a style dynamically this way, it won't work (only on XBox IE):

<div style="padding-top: 1px; background-color: {{ colors [ data.colorname ] }}">

If you set it this way, it will

<div ng-style="{ 'padding-top' : '1px', 'background-color' : colors [ data.colorname ] }">

The peculiarity is, whether or not it should be done this way, the first one works everywhere, on every phone, tablet, and laptop I had available to check it on (two macs, two PCs, two phones), but the background color did not render on XBox IE.

I changed to the second form and it worked fine.

Again, just some errata.

As always, thanks for visiting.

Thursday, October 1, 2015

Acronyms in Angular Directive names

Here's an interesting quick one.

Many industries live and breathe acronyms. So, it would be no surprise if they wanted to use them in directive names.

Say you were creating a management system. PM is a pretty standard acronym for Project Manager.

If you name your directive this:

myorg.abPMDirective

Then what you might expect, with Angular doing its funky "you wrote it this way but we'll divide it up with dashes this way" thing:

ab-pm-directive

Nope. You will have to use it like this as a directive (assuming attribute restriction in the directive itself):

ab-p-m-directive

Or, rename the directive:

myorg.abPmDirective

Angular evidently does not recognize all-caps acronyms. Certainly not a deal breaker, but I'd like to think that if I was building out that mechanism within Angular I'd have thought of this one.

If there is a solution to this that is very straightforward and uses the framework as-is, nobody I asked is aware of it.

Yes...I am aware of the documentation and spec for this. My point is that a little case detection in the mechanism could solve it. Perhaps a good interview question.

There is also no debug error of course. One of the frustrating things about using directives is, any typo and it just won't work, but nothing will tell you why. A test can fail, but it won't tell you that you declared the markup incorrectly or that the directive didn't start up (why would it). It's one of those sort of old school "you just have to know" things. Ultimately you could probably write a test to figure that out but...well, I don't have all day to toy around with creative tests.

As always, thanks for visiting.