Red Green Repeat Adventures of a Spec Driven Junkie

Learning Angular - Property Bindings

Last article covered how to get data out and data in to class.

This time, I want to manipulate elements on the page according to a value set by class, the next step in having a dynamic page.

I will work through to get a simple “human validator” system going. Have submit button off by default, if the text input matches a certain value, enable the submit button.

DIY human verifier

This will show another part of the Angular framework designed to dynamically set this up.

The article will take you about five minutes to read.

Men Treading Grapes", Folio from a Materia Medica of Dioscorides

Introduction

To see how to enable/disable an element like a Submit button, let’s start with code for an enabled and disabled Submit button:

Enabled submit button:<br>
<input type="submit" enabled ><br>
<br>
Disabled submit button:<br>
<input type="submit" disabled ><br>

Enabled and Disabled Submit buttons

The key is having the enabled or disabled keyword in the attribute field.

Following Along

If you would like to follow along with this code, you can use the Github link to get a final copy of the code or view the project in your browser using this StackBlitz link.

Solving using {\{}\}

Given what we know about getting values from a component class, a possible solution would be:

Dynamically enabled submit button:<br>
<input type="submit" {\{isDisabled}\} ><br>

The AppComponent class would have:

isDisabled=true;

Results?

Let’s see what happens when running this code in a browser:

Error: Submit not disabled

Well, with such a small change, this isn’t expected, the button still works. Let’s see if the console has a hint to the problem:

Console error: Submit not disabled

ERROR DOMException: Failed to execute 'setAttribute' on 'Element': '{\{isDisabled}\}' is not a valid attribute name.
	at EmulatedEncapsulationDomRenderer2.setAttribute (http://localhost:4200/vendor.js:87590:16)

This message isn’t useful at all. I don’t know what most of those words mean!

Backtracking

OK, it looks like using {\{isDisabled}\} is not the way to dynamically set up the disabled property on an element.

As I don’t know what that error message meant, I will go read the Angular documentation. I will start at template syntax as it covers components and templates.

I read over the documentation and see this code snippet:

<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>

source

This looks exactly what I want to do!

Data Binding & HTML

Let’s modify the current version to see if the previous snippet works in my situation:

<p>isDisabled is {\{isDisabled}\}</p>
<input type="submit" [disabled]="isDisabled" >

(Having <p>isDisabled is {\{isDisabled}\}</p> keeps me sane.)

Change AppComponent variable: isDisabled to just return true instead of a string:

	isDisabled=true;

Result:

Running in a browser, I see:

Submit button when isDisabled=true

Looking good, let’s see what happens when I change isDisabled=true to isDisabled=false:

Submit button when isDisabled=false

Perfect, this is the syntax I need!

Reading the documentation further, what I need is: property binding, that sets DOM properties, like disabled.

Before moving on: double negatives

Did you notice anything in the results in the previous section?

  • isDisabled = true produced a non-working submit button.
  • isDisabled = false produced a working submit button.

This confused me as I wrote the article. I expect true to be what I want and false to be not what I want.

My preference is not to have double negatives, like not false == true. A computer can handle the logic easily, I prefer to have statements in the positive sense: true == true.

Here, using isDisabled on disabled is too confusing for me. Double negatives do make a positive, there is a better way, say: humanValidated, which would be the opposite of what is required for disabled:

<input type="submit" [disabled]="!humanValidated" >

This is a bit easier to read and to keep track of.

Dynamically assigning humanValidated

Using another variable to set disabled doesn’t solve the problem to dynamically change the button state.

One thing we do know: using the keyup function on the input text field can call a component function, such as: humanCheck.

How about putting those together: setting humanValidated value using humanCheck function that keyup calls, passing in $event data, such as:

	humanCheck(event: any) {
		if (event.target.value == (2+3)) {
			this.humanValidated = true;
		}
		else {
			this.humanValidated = false;
		}
	}

Update the app.component.html template file:

Human check: in field below, enter 2 plus 3<br>
<input (keyup)="humanCheck($event)"><br>
<input type="submit" [disabled]="!humanValidated" >

Let’s put this together and see the result:

DIY human verifier

Ta-da. You have your own ReCaptcha! :-)

Additional Discovery

Trying to use {\{}\} syntax to output disabled from isDisabled just blew up Angular. It might seem like it would work because HTML is just text rendered by the browser and setting an attribute like disabled dynamically would be the right fit for this.

Looking over the Angular Documentation, the better approach is to use property bindings, designed to work with DOM properties.

Scrolling down the documentation further, there’s a section titled: Property Binding vs Interpolation, that has the following:

However, when setting an element property to a non-string data value, you must use property binding.

This explains why the page did not even render and the error message was vague when trying to use {\{isDisabled}\}.

Conclusion

The Angular framework designed string interpolation to output to strings and property bindings to set a DOM property, such as disabled.

When working against the design, error messages can be vague and it helps to revisit the documentation to get hints on what else is available to solve the problem.