angular js: ng-repeat no longer allowing duplicates

Just ran into an interesting issue with angularjs combined with local storage.

Some facts:

1. angularjs adds a $$hashKey property to objects in collections that are being used with ng-repeat and don’t otherwise have an identifier configured.

2. the $$hashKey is typically generated by incrementing a shared variable. This is a fairly simple way to reliably generate new ids.

In our app, we are using local storage to continually save changes that the user has made on the page . This allows them to always have a working draft that they can discard at any time. If the browser dies or they go away for a while, they never lose their work.  They always have the option to submit or discard available to them.

Now, if you store an object in local storage, you’re typically storing the whole object unchanged. We were, of course, which meant that we were including the $$hashKey property on all of our objects kept in local storage.

So, when you reload the page, we would load the draft data from local storage, including the $$hashKeys.

For items added that didn’t have a $$hashKey set yet, angular would generate one. Only, because it’s an incrementing value, it would be behind some of those already existing in other tracked objects. Collisions! Duplicate IDs!

This totally just happened:

Error: Duplicates in a repeater are not allowed …

Oh NO!

In angular 1.1.4, it now throws an error when there are duplicate objects in an ng-repeat, where this was previously tolerated.

There are a couple of ways to get around this.

In our case, we only ran into this issue because we were storing our objects in local storage and then retrieving them again later on new page loads.
We were storing our objects as JSON, and so we were using JSON.stringify and JSON.parse when we get from local storage.  So it was easy to extend our stringify method to strip out the hash properties by passing in a replacer, like so:

JSON.stringify(value, function (key, val) {
     if (key == '$$hashKey') {
         return undefined;
     }
     return val;
});

When we load objects and feed them to an ng-repeat, angular will add hash keys as it needs, so this works fine for us.

But… there is an easier way if you expect duplicates to be possible in your collections. It’s just hard to find. Sometimes looking at the source really is the best documentation…

Anyway,  one way you can handle this is to tell angular that you’d rather have the ng-repeat treat the items’ indexes as the id rather than expecting an id (the default, which like in our case results in generated hash keys when there was no id).

This can be done by defining the ng-repeat using the following style:

<ul>
    <li ng-repeat="thing in things track by $id($index)">
    .....
    </li>
</ul>

Whammy.

Lesson learned, sometimes you’re not just storing what you think you’re storing. You might also be storing framework state information.  And that state may not always be valid.

What are you learning right now?

I’ve been thinking more and more about the active pursuit of learning and improving one’s self.

I’ve sometimes thought about the idea that there are 2 types of people, active and passive thinkers.

Active thinkers are curious, self-critical, and creative. They are continually re-evaluating themselves and trying to improve.

Passive thinkers rely on external motivation. They coast until some person or external circumstance pushes them to think about something.

Way too many of us are currently stuck on passive.

In the tech industry, change is the norm. New technologies and ideas pop up all the time and we are expected to stay on top of the current trends. Without a constant pursuit of learning, we can’t be expected to recommend the best solutions or technologies, and certainly can’t be trusted to effectively build systems.

Learning in a constantly changing landscape could appear to be an overwhelming task.  After all, if what we learn now is going to change soon, why spend our precious time? If we’re good enough at learning on the job, maybe we can just pick up things as we need them..

If you’re not actively learning, you are holding yourself back. Scratch that. You are holding yourself, your team mates, and your company back. We all learn by challenging each other and helping each other to be more awesome. Great people want to be surrounded by other great people and do awesome things.

I’ve often thought that the best solutions come out of great debates.  When you have a team full of active thinkers that understand their craft and take ownership of a project, when everyone is learning, teaching, and challenging each other, amazing things are possible. Isn’t that the team that you’d want to be on?

Or maybe your career goal is gradually becoming the irrelevant old guy that still thinks and solves problems like it’s 1999.

So, if learning and pushing ourselves is so important, why doesn’t everyone do it?

It’s hard. Harder than watching TV and sitting on the couch. Harder than drinking beer. Harder than just showing up in the morning, leaving at 5, and collecting a cheque. Certainly harder than making excuses. After all, we were born awesome and life is short.

Because learning and practising our craft takes so much time, right? And those of us with other hobbies and family just don’t have enough time, right? Right…

When I hear “I don’t have enough time”, to me it really translates to “I don’t know how to manage my time”.

How much time does the average person spend watching TV, aimlessly surfing, playing games, etc.?

We waste an incredible amount of time every day. Hours. And you don’t even have to give up all of that. You could probably still survive just wasting slightly less time.

I don’t think there is anyone who couldn’t make an effort to put in half an hour a day toward learning and improving themselves, investing in their career, and in those around them.

It’s not about brute force number of hours!  It’s about consistency.  Consistently putting a small amount of time and effort into developing yourself will pay off huge over the long term. Do you really want to be the guy (or gal) who is completely oblivious to industry trends, only understands things at a basic level.. and is pretty sure he’s awesome ?  Or do you want to be the guy (or gal) that actually is awesome, reliable, knowledgeable, and is respected by the awesome people around him (or her) for contributing to the awesomeness that they create?

There are lots of ways to learn:

  • Read – books, blogs, twitter. Try to understand and contemplate the thoughts of others.
  • Start a hobby software project. Create something. Be responsible for it’s evolution.
  • Contribute to an open source project. Learn from others.
  • Code katas – practice solving problems with tools & languages of your choice
  • Learn a new language. Try a different approach to thinking about software.
  • Join a group. Meet people. Learn from them.
  • Give a talk or teach something to others. Explaining something can force you to understand something more fully and think about it from a different angle.
  • Write. Revise your thoughts and opinions and put them out there. Listen and learn from feedback.
  • Think and formulate opinions. Discuss with your peers. Debate. Learn from them. Teach them.
  • Other

If you’re not doing any of those things, YOU ARE DOING IT WRONG.  You are being left behind.

You’re stuck in grade 1 and your peers that are actively thinking and learning are moving on to the next grade. You’re a passive thinker, waiting for someone else to tell you what’s important. You are getting older and no better at what you do.

So get out there. Do something. Make yourself better. Make your peers and your company better. They’ll make you better in turn. And then you’ll do great things together.

Or you could just watch more TV. You choose.