The Fault in our Select Default

One thing I haven’t quite figured out in Angular 2 yet relates to the ‘select’ element on forms.  Creating one by *ngFor – ing through a dataset makes sense, but every time the ‘selected’ option fails to set, leaving me with a drop down that appears empty until clicked.

What I wanted was a generic default option- like you’d normally do by setting the ‘selected’ property on the element.  Something like ‘Select Item Below’- so people know there’s the option, but the first element in the actual select array isn’t shown yet.  But it just didn’t seem to want to work.  Even if I manually added an option element above the *ngFor loop through the other option elements (provided by a call to the Angular controller), and set the ‘selected’ attribute on that first element, it just appeared in the list- it was not selected by default.

I’m betting there’s an easy way to do this, but I didn’t find it.  Instead, this is what I did in a ‘setDropdownOptions’ method on my component:

this.optionsUser = this.audit
    .map(entry => entry['user_key'])
    .filter((name, index, arr) => {
        return index === arr.indexOf(name);
    });
this.optionsUser.unshift('Choose User Email');

this.optionsUser is just an empty array to begin with.  The first part loops over the audit array to get the user_key (an email address), then return just that into the optionsUser array (the extra .filter call weeds out any duplicates).  Then, we unshift (add to the front of the array) the generic text for the default menu item.

One more step in the component- the select element is part of a FormControl Angular element.  We set this specific control to a variable called ‘this.termUser’ when initializing the form.  That way, once the array of ‘optionsUser’ is populated, and we unshift the generic title on the front, we can set the initial value:

this.termUser.setValue('');

Finally, in the template, we just loop over the optionsUser array as normal to display the available options from the database, but on each iteration, check to see if the option === our default text (‘Choose User Email’ in this case).  If so, set the value for this select element to an empty string (making it the default selected, but also meaning that if it’s submitted, it won’t alter our returned array at all):

<select class="form-control" formControlName="user_key">
    <option *ngFor="let item of optionsUser" value="{{item === 'Choose User Email' ? '' : item}}">
    {{item}}
    </option>
</select>

Does it work?  Yes.  Is it pretty?  Maybe.  Is there a better way?  Probably.  We’re working on abstracting it a bit more to make it a reusable option (instead of hard coding the default text, pass it as variable)- more on that later!

Order must be maintained

Javascript can make you forget that order is sometimes important.

Hoisting usually allows you to declare variables anywhere in your code and they will work- even if you’ve technically used them before that line of declaration.  I know- it’s bad practice, but sometimes there’s a slight tradeoff.  If I have a bit of code that depends on specific variables, it can be easier to declare them directly above that bit of code (closest to the actual use).  Of course, they’re ‘hoisted’ up to the top of the enclosing function to be initialized, so it’s really just a readability trick (though the block scoping ‘let’ keyword should clear up some of this confusion with es6).

So I try to keep my old ‘var’ declarations at the top of each function, but sometimes fail.  And that can lead to bad habits- like forgetting to check order in other aspects of my code.  For example: we had to go back to our Angular 1.5 project to add some custom validation to a contact form.  It was just using the default HTML5 ‘required’ validations- useful, but not really in line with the rest of the page’s look n feel.

So, we added some checks and used the cool ng-messages module to show some custom notices.  Worked great- mostly.  However, the form allows the user to reset after submission- in case they want to submit another question.  For some reason, the form would not clear.  More specifically, the scope was never removing the original submission object.

We tried everything: resetting each $valid/$invalid attribute, setting the ‘passedValues’ object that was storing the submitted values to an empty object after submit, setting it to ‘null’, resetting the form using a plain old document.getElementById(‘contact-form-id’).reset() call- none of it worked.  The screen would clear out old values, but the scope would not reset- the initially submitted values were still there.  We could tell because at one point, we just started logging everything to the console (there are a lot of properties on a submitted Angular form!).

Long story short (too late), the issue was order of operations.  We had the call to reset the actual html form after calling ‘setPristine’ on the angular model.  So, it looks like the values were still technically stored in the form object when the reset() method was called on the form, and they (the original values) were re-inserted right back in, and then cleared from display.

Or maybe it’s something completely different, but that’s the best explanation I can come up with because as soon as we moved the form.reset() declaration to the top of the $scope.reset function, everything worked great.  Maybe we are correct- the order of operations was wrong.  Or maybe the form Gods took pity on us after we burned way too much time trying to figure it out and fixed the reset for us.  Either way, it works now!

Contacts Deleted!

Is there anything worse than having to delete a whole bunch of items from a website on an individual basis?  Click delete, ‘are you sure?’ confirmation, actual deletion, repeat over and over.  No one should have to do that.

Luckily, it’s fairly easy to allow for multiple deletes using simple checkboxes and Javascript.  We wanted to include this in our new Angular 2-driven contact management system.  We started by creating a deleteList array in the main component class for the contact list.  Then, on the form html template, we added a column for ‘Delete?’ with a checkbox in each row.  That checkbox gets a few important attributes: the name and id are both set to {{contact.pk}} – this is the primary key for that contact in the database.  The ngModel is two-way bound to a contact.selected variable (which is initialized on the component class), and the change event is given a horribly named function: addRemove($event.target).

Back to the component class- we need a main delete function and a helper function.  The helper is very simple- it gets the target (passed in as $event.target), and checks to see if we are checking or unchecking the box.  If checking, we add it to the deleteList array.  If unchecking, we remove that id from the array:

addRemove(target) {
    if(target.checked) {
        this.deleteList.push(myTarget);
    } else {
        this.deleteList.splice(this.deleteList.indexOf(myTarget), 1);
    }
}

Then, we need the function to do the actual deletion.  It’s going to use a few pre-defined services (do we still call them that in A2?) to communicate with the backend and delete, as well as show a loader and notification of success/failure:

deleteContacts() {
    this._loadingService.show();
    this._contactService.modifyData(this.deleteList, '/contactdeletemultiple')
        .subscribe(
            response => {
                this._notificationService.error(response._body);
                this.getContacts();
                this._loadingService.hide();
            },
            error => {
                this._notificationService.error(error);
                this._loadingService.hide();
            }
        )
    this.deleteList = [];
}

This event is attached to a ‘Delete’ button on the html template. We added a little [hidden] attribute to only show that button if at least one box is checked.  Note that at the end of the function, we also reset the deleteList array to avoid passing in ids for items that were already deleted.

It seems to work great.  Quick and easy, as soon as we played with $event a bit.  Next week, we’ll jump back to Angular v1.5 a bit for an important lesson in order of operations- aka: Why I spent multiple hours unable to reset a simple form.

Components and Controllers and Contacts

So Angular 2 is pretty cool.  I’m still feeling my way through some parts, but it just seems to make more sense to me than version 1.  Components instead of controllers, sometimes using shared services, reaching out to templates- it’s all good.

We’ve been working on an example form for adding contacts to the new system.  Setting up the form and submitting it was a breeze, though figuring out validation was a bit tricky.  Maybe because of the options- there are 3 or 4 different ways to do it in Angular 2- the one that made the most sense to me is to create a ControlGroup on the component and manage it fully from there.  You can then create individual Controls on each filed to specify what validation you want (and even what initial value you might want for that field).

Though there was one minor thing lacking- there’s no way to reset the form after submission.  You could do a simple form.reset(), but that’s only going to clear the fields, not reset the Angular-driven ControlGroup.

The answer: create a new ControlGroup each time.  First, we added a method to the component- setForm().  This handles all the initialization of our controls:

this.contact_active = new Control(true);
this.contact_name = new Control('', Validators.required);
this.contact_sn = new Control('', Validators.required);

Be sure to use the ‘new’ keyword- we are creating a new object each time this method is called.  Then, we set up the formBuilder group- which acts as a bridge to the actual html form in our template- assigning the validators we created above to the actual fields:

this.newContact = this._formBuilder.group({
  'contact_active': this.contact_active,
  'contact_name': this.contact_name,
  'contact_sn': this.contact_sn
})

Now we just need to call setForm() in the right places inside the component.  Once in the constructor (or ngOnInit if you prefer) to set the initial form.  Then, call it at the end of your method used to actually add the new contact (the call to your backend to save the data).  And boom- you have a reusable form, with validation!

Next week- combining Angular 2 with some traditional Javascript tasks.  We wanted to be able to use checkboxes to delete multiple contacts at once.  Best method?  I have no idea, but we’ll look at our solution!

Keeper of Records

The photo album feature will be shipped this weekend, so it’s time to stop talking about that (because if we stop talking about it, maybe it won’t crash and burn when pushed to production).

Next up: the recordkeeping feature.  It’s fairly useful- we use it for our own internal tracking- but it’s also fairly ugly.  And fairly non-user-friendly.  It needs a facelift: new interface, easier to navigate, no more tables.  It also needs to work out a bit: strengthen up the process by adding drag/drop functionality for adding documents or images to a record.  Finally, it needs to learn some new skills: we’ve had multiple requests to allow a custom request form feed directly into a recordkeeping entry, and I think this is where the bulk of the work will end up going.

Creating a form that dumps entered info into a database isn’t too hard (though security concerns should always be taken seriously).  But when the form can be customized by the customer, it seems to become a lot more complex.  For example: when creating the table to store the form data- how many attributes should it have?  One form might have 3 text fields, 2 checkbox groups, and a drop down select list.  Another might have 7 text fields.  How should we design that table to store the info?  Does it make sense to create a new table for each form, and just make the columns ‘on the fly’ as the first form is created?  The advantage to that is that each subsequent form (of that same category) would already have the table, and could simply be entered.  Or maybe we restrict forms to be used with a database backend to a limited number of fields- let’s say 5 for example.  But even then, those five fields could be any one of 8 options for field type, so making sure the schema matches the data coming from the form could be an issue.

We don’t have any answers yet, but sometimes it helps just to write down the questions.

:First!

The DOM Tree Of Life!
The DOM Tree Of Life!

One problem (of many) I ran into while adding validation checks on our request form feature was limited to IE8.  The code worked great in other browsers- my function used find() to get the correct error notice wrapper to display and switched it on if a field was blank or otherwise invalid.  But not in IE8.

In that world, the function would show the error notice wrapper for every field.  I made a little progress by fixing my PHP code- it originally inserted an error wrapper on each form field- even ones that weren’t required.  I figured it didn’t matter, as they’re all ‘display: none’ by default, but my first step was to add a conditional to my PHP to ensure that error wrappers only appear attached to required form fields.

That didn’t solve the problem- though at least it reduced the amount of red I saw on my test form.

Still- anytime a form was submitted with any unfilled (but required) field, any required field below that in the form was marked as invalid (even if it had a valid value).  Only in IE8.

The answer was to use the :first selector (https://api.jquery.com/first-selector/).  It looks like IE9 and later (and all other browsers) will stop at the first instance when using find(), but 8 will not- it will get all matches.  However, when I added the :first selector to my function that processed each error, 8 fell into line with the rest of them.  It stopped popping error messages all the way down the tree.

Another example of the notion that development can go so well for the majority of a project, and one little problem can stall for way too long.  Should I have thought of the :first selector earlier?  Probably, but because the code worked everywhere else, it was a bit of a head-scratcher for me.

Hope this helps anyone with a similar issue.  Actually- what I really hope is that everyone drops support for IE8 and no one has to read about this!

Stop Bugging Me!

We’re nearing completion on the request form feature validation, but I need a bug zapper.

We were so proud of ourselves early in the week.  Everything was working.  The functions were a nice mix of plain JS and jQuery.  Feedback given to a user while they filled in the fields of a form was informative without being frustrating (finding that line proved to be tricky, but hopefully we nailed it).  We though it would be a matter of some final testing and a push live.

Then I checked it in IE8. The rest of the week has been a frustrating, but informative, experience. Lessons learned:

  1. If you’re going to use jQuery, use it.  It seemed like we should try to use plain JS when possible, but in reality, if you’re targeting as much backward compatibility as possible… Well- there was a reason jQuery was created.  It smooths out the rough edges between ie, Firefox, and Chrome.  It makes things just work!
  2. Always test in an old browser early.  We’d really love to just drop support for IE8, and likely will eventually, but not like this.  The checks almost work, even in IE8, but are just flawed enough to make the feature very hard to use in that browser.
  3. Modern, updated browsers will tolerate multiple elements with the same id tag.  Older ones will not.

Point 3 was a tough one.  Because the form is being generated via a php/mysql backend, we don’t know what it will look like.  Therefore, all the validation checks have to be done with just a general knowledge of what the final form will look like.  For example- the main issue was with checkboxes.  There may be 4 required checkbox groups, and 2 non-required.  We found a way around this by using a wrapper div around a checkbox group and adding a ‘required’ class tag via php if that group is marked as required in the database.  Works great, but only if the wrapper div has the same id as the checkbox group.  Again, not a problem for most, but IE8 did not appreciate this at all (and I know, it’s also bad practice).  I think we found a way around it (by removing a possibly unnecessary id tag on a sub element), but it was not easy to find why some really odd bugs were popping up in IE8.

If I were to have a lesson #4, it would be that jQuery is quite awesome.  I know it’s not the new hotness that some js frameworks are, but it’s super useful.  I had a couple document.getElementsByClassName calls- they all failed in IE8.  Switch it out with a jQuery dollar sign and we’re money!  JQ also provides an awesome iterator function: .each().  That really helped, as again- we didn’t know how many of each type of field we would be checking.  It also smooths out the ‘this’ keyword- items within the .each() function have exactly the ‘this’ you would expect them to- none of the global window ‘this’ garbage.

Hopefully next week will be a wrap up of a successful launch.  Then maybe some different topics!

Arrays In Action

I know these form validation entries are getting dull.  But it’s a major project and has led to some interesting/frustrating issues.

For example-  we wanted to be able to automatically scroll a user directly to the spot on the form where they had the error.  The simple way is to use input.focus – works fine if there’s just one error.  But there were some drawbacks:
1) Can’t seem to ‘focus’ a checkbox or radio group.
2) If multiple errors, it could focus one field, then another, then another in quick succession- a very confusing user experience.
3) The focus option didn’t scroll the screen high enough for the user to see the field label.  Sure, it’s easy to scroll up a bit to see what they need to enter, but again, not good user flow.

The answer? Arrays in action!

We created a resultArray variable- on each check in the $(form).on(‘submit’) handler, if something failed, we pushed that jquery object to our resultArray.  Then, at the end of all the checks, we $(body, html).animate() to 50px above the entry at resultArray[0] (found by getting resultArray[0].offset().top – 50).

And it seems to work great!  The array holds all errors- we can then scroll directly to whatever one is first in that array.  The project is about a week away from being done- so probably only one or two more entries on this.  Next week- the process of inserting ‘live’ checks, so a user can get real time feedback on fields that initially gave an error, but may have been fixed.  It was an interesting balance of enough feedback to let them know that ‘this field is now good to go’ with ‘stop giving me an update every time I type a damn key’.  Hopefully we got it right.

Radio Check

Big(ish) victory over the radio option field type this week.  Still working on implementing legitimate validation checks for our custom request forms feature.  In the current process, a user’s submission is checked via a complicated process- php code ‘prints’ out javascript functions, so that it can use a variable snagged from the database to see if that field is required.  Seems to work, but it also seems to have 2 major drawbacks:

1) Only works with alert boxes- if we get much more complicated with it, it all breaks down
2) It doesn’t really check for valid forms.

#2 means that it checks a very basic form, but admins can create very complex forms.  They might have 4 non-required type=text fields, 2 required type=text fields, a required radio fieldset, a non-required checkbox set, and so on.  The current validation really only checks one level- if there’s a required field of a certain type, and there’s an entry in a required field of a certain type, it returns true.

But what if there are two required fields of the same type?  That’s where it gets tricky.  Seems to be ok on type=text, but with others (radio, checkbox, select box), it will return true if only one (of possibly many) fields is actually filled out.

So it’s been a bit of an ordeal to figure out.  First, we check if the field exists and contains a required property.  Then, we can use jquery to get a jq object of that field, and do whatever we need to do (in this case, turn the outline red and show the error div for that field).  Instead of printing javascript functions in order to have access to php to get variables from the database, we inserted a simple, tiny bit of php into each html element on the form:
<?php print ${$required_prop} ?>
That will print ‘required’ if the database tells us the admin has set that field as required, or nothing if not.  Then, we inserted the same php into the class definition of the parent element (for checkboxes and radio groups, a wrapper div)- that way we can grab it and outline the whole group if an error is returned.

Seems to be working well, but still a lot of testing to go.

This field is required

More adventures in user experience design and form validation this week.  For a little while, I had the ‘error’ div at the top of the form.  If a user submitted an incomplete form, it would fade in and the screen would scroll to the top so they could see what went wrong.

But it seems to make more sense to scroll to the field where there’s an issue, so switched to an inline div to the right of the field.  Each field on the form is set up with an error div that’s initially display:none.  That div has an id that matches the form field it’s ‘next to’ in the dom tree.

My js function then checks to see if the form field has the ‘required’ attribute.  If that’s true, the function checks if the field is empty (or invalid, in the case of an email address).  If that’s also the case, the error div is set to display:inline-block.  The function is set to check through all fields by type- so we start with type=text.  There can be none on a form- in this case, the initial check will bypass that whole block (it’s all wrapped in an if statement that checks for the existence of a type=text input field).  There can be some on a form, but none that are required- or all could be required- or some required and some not.  This was the tricky part- but a simple loop through the collection of input type=text fields found on the form, followed by an ‘if’ statement checking if the field is required, solved the issue.

The task now is to refine the process for input type=text, then implement it for all other form types an admin might set up (textarea, checkbox, radio, date, captcha, and so on).  They should all be somewhat similar, but we’ll see!