angular – Brian Hann https://brianhann.com Thu, 14 Dec 2017 17:57:52 +0000 en-US hourly 1 87222886 What to Do When Your AngularJS Project Goes Wrong https://brianhann.com/what-to-do-when-your-angularjs-project-goes-wrong/ https://brianhann.com/what-to-do-when-your-angularjs-project-goes-wrong/#comments Fri, 02 Oct 2015 12:07:00 +0000 https://brianhann.com/?p=677 It all started with such promise. You carefully chose how to structure your application. You picked a build system, or not, based on your experience and the community. You planned, and thought, and planned some more. But then something happened: you made a choice. It seemed like the right choice at the time. You were sure! Well, pretty […]

The post What to Do When Your AngularJS Project Goes Wrong appeared first on Brian Hann.

]]>
It all started with such promise.

You carefully chose how to structure your application. You picked a build system, or not, based on your experience and the community. You planned, and thought, and planned some more.

But then something happened: you made a choice. It seemed like the right choice at the time. You were sure! Well, pretty sure. I mean, how bad could it be if you were wrong, right? … Right?

 

AngularJS spaghetti code

Delicious when eating, not when coding.

Your app started to feel a little funky after that choice: less clean; less immaculate.

The funkiness increased each time you added more code. After a while it all started to congeal together, like spaghetti noodles that have sat out too long.

You couldn’t tell where your good choices started and where your bad ones began.

What do you do?

300 !@#!&(* Controllers?!

I recently came across a question on the Angular google group asking for help with an app where the author was trying to handle synchronous AJAX calls.

That’s not the part that stuck out to me. Here’s what did:

… I have created almost 300 controllers …

I had to do a double take. Three hundred controllers?  Why? What use case could possibly merit that?

Am I an old fogey developer, out of touch with the masses? Maybe separating your logic out into hundreds of tiny pieces is the wave of the future.

Somehow I don’t think so.

First I want to apologize to the person who asked this question.

I’m not ridiculing or chastising you. I seriously want to help you and anyone else who’s gotten themselves into a pickle when creating AngularJS applications. Let’s be honest: that’s all of us.

User frustration

I have written some absolutely terrible Angular apps that are still in active use in our company. I was new, and learning, and my users bore the brunt of my mistakes. Sorry, users!

Stop, and Reflect

When you’re writing applications and you’ve dug yourself into a hole, sometimes the only thing you can do is start digging another hole. The business won’t pay for you to trash your app and fix your mistakes.

But before you do, it’s important to take a moment for reflection. If you pause for reflection you will deepen the learning experience. The choices, decisions, and process that brought to this place will become more concrete.  And then you’ll know that you can make different decisions in the future.

Ask yourself some important questions.

  • Is this where I wanted to be?
  • How did I get here?
  • What choices did I made?
  • Would I make the same choices over again?
  • What did I choose to not inform myself of or read about? (Best practices, new tools, old paradigms, etc)
  • If there were options, how did I compare them?
  • How long did I take to make the decision? Was it a snap judgment or a drawn-out process?

It may be easier to work backwards from where you are now, until you reach the point of inflection where your decision train jumped the track. But if that feels overwhelming, just ask the questions above.

It’s important not to critically follow your decision breadcrumb trail, questioning yourself at every step.

Instead, what you want is better self-understanding: to see how you make decisions; whether you’re impatient or controlled, risk-adverse or venturesome, methodical or chaotic.

None of these character traits is good or bad, but if you know how to tend to act you can self-modulate in advance, protecting yourself from making similar mistakes that arise from your behavior patterns.

Be Gentle

When you do this sort of reflection it’s important to be gentle with yourself. Beating yourself up and calling yourself names in your head is absolutely not going to help you learn and improve.

Accept the situation for what it is: a chance to learn and grow. You will be a better developer for this experience. It is the uncomfortable places that stretch us where we grow the most.

How Did He Get Here?

Our author above didn’t start with 300 controllers.

They most likely grew our of a combination of two things:

  1. A decision that single controllers were the right place to put distinct bits code
  2. A whole bunch of distinct bits of code

Each time the author had a new thing to add to his app, he fired up ol app.controller() and went to town.

At some point he had 5 controllers handling some parts of his application. Perhaps they were all centered around a certain section and he needed to jump to another. Bing! five more controllers. The process continues as he grows his app: 20 controllers, 50, 100, 200.

What the author is seeing and saying to himself is not “man, that’s a lot of controllers!”, rather “man, that’s a lot of work and business logic!”.

Do It Different

Do it differentlyHow would you handle this situation differently? What are the patterns, best practices, and experiences that you can apply to this situation?

Could you use generators to create controllers? If you can make them through simple configuration then they should be generate-able as well.

If they are configurable then maybe composing different pieces together with nested views in UI-Router could be the best thing.

If the controllers represent different little widgets then directives are probably the answer. And you can always stack directives as well. You can create generic directives and have more specific ones stack functionality.

Do you have another idea? Share it in the comments section below!

 

The post What to Do When Your AngularJS Project Goes Wrong appeared first on Brian Hann.

]]>
https://brianhann.com/what-to-do-when-your-angularjs-project-goes-wrong/feed/ 1 677
4 Great Resources for AngularJS UX https://brianhann.com/4-great-resources-for-angularjs-ux/ https://brianhann.com/4-great-resources-for-angularjs-ux/#comments Mon, 13 Jul 2015 14:24:41 +0000 https://brianhann.com/?p=624 If you write Angular apps you spent a lot of your time making decisions for your users. You decide what they will see, and when. You decide how the app responds to their interactions. I often struggle knowing which is the right decision. Do I put this widget on the left or the right? Do I […]

The post 4 Great Resources for AngularJS UX appeared first on Brian Hann.

]]>
If you write Angular apps you spent a lot of your time making decisions for your users.

You decide what they will see, and when.

You decide how the app responds to their interactions.

I often struggle knowing which is the right decision.

  • Do I put this widget on the left or the right?
  • Do I only show one at a time, or multiple?
  • Do I separate things into different sections or have a long page where it’s all available at once?

Like most developers, I spend time seeing what others are doing and try to cherry-pick the good stuff.  What follows are some resources that I’ve come across that really help me make better decisions.

Better decisions mean by application functions better for my users. It also means it’s easier to understand, which means less support calls for our Help Desk (yay!).

I’m not perfect, of course. I still make wrong decisions. But I think I make them less! And hopefully these resources help you to make better design and user experience decisions when you create Angular apps.

Pressed for time? I’ve compiled a list of 20 take-aways from these resources. These are the things that I’ve found most helpful, and most impactful for me. Interested in checking it out? Get access to the 20 UX Take-Aways PDF .

User Onboarding

User OnboardingSamuel Huelick’s User Onboarding takes you into the guts of how other apps and sites treat new users.

Sam takes a given site or application and deconstructs their “onboarding” process into steps. Each step has a screenshot that he annotates with what’s good, what’s bad, and suggestions for improvement.

This is insanely useful where you’re creating new Angular apps, or upgrading old ones. You can learn loads from the mistakes that others have made, and from their successes as well. Sam does a great job of extracting those mistakes and successes, and processing them for you.

There’s a lot to absorb here, so I suggest you pick a couple that sound interesting and take notes. My favorites so far are:

Don’t Make Me Think

Don't Make Me ThinkDon’t Make Me Think by Steve Krug is a modern classic. Classic in the sense that the original version is 15 years old, and it’s still perfectly relevant today.

Steve’s overarching principle, what he calls the First Principle of Web Usability, is this:

Your user shouldn’t have to think when they land in your app.

The user’s next step should be self-evident.

Imagine the most technologically helpless person you know, and they’re using an Angular app you wrote. If you give them a simple task, will they know what to do? Yea, my users wouldn’t either.

And you know what? That’s really difficult. And it takes time. I’m not saying that you need to dumb everything down and cater to the lowest common denominator if you’re writing tools for software engineers, but here’s the thing:

Less decisions mean less mistakes.

The fewer choices you allow your user to make, the less likely they are to choose the wrong one. I’ve seen this time and time again in our business.

We unfortunately rely on many 3rd-party applications which give far too many options. We only want one type of exported file. The app allows 6. How many different types do you think we have sent in?

Yea, you got it.

Reduce decisions. Don’t make your users think. They’ll thank you and you’ll thank yourself.

Badass: Making Users Awesome

Badass: Making Users AwesomeKathy Sierra’s book, Badass: Making Users Awesome, was pure enlightenment for me.  It reframed how I think, at a core level:

  • What does my user really care about doing, and becoming? Kathy calls this their compelling context.
  • If I’m working on a business app, how does the app help my users achieve their compelling context?
  • What concepts does my user need to know, and what I can cut?
  • When do I introduce things so that I don’t overwhelm, bore, or alienate my users?

Even though a lot of my time is spent creating line-of-business apps, I still found this book incredibly relevant.

At our business we have several applications being used by employees you could lovingly refer to as “low-tech”. By that I mean that email is a new concept for them.

That’s not necessarily a bad thing, but it’s a hurdle for an application developer. How do I need to design, structure, and teach the applications I write so that I don’t frustrate my users, and don’t frustrate myself by watching them struggle?

My users don’t really care about being “good” at using my apps. They want to be “good” at their jobs. They want to advance. and grow (most of them anyway…).

The best thing I can do is create apps that meet them where they’re at. That makes the app more useful to them, and it reflects better on me.

What about you? Can you take what Kathy teaches and significantly improve your applications, while helping your users? I think so. Absolutely.

GoodUI

GoodUI

This site, from Jakub Linowski, is a running list of suggestions to make UIs easier to use. It also focuses on conversion rates. That may or may not apply to what your’re creating. If not, there’s still lots of great tactics that you should note. Even better, add them to a Best Practices notebook. You have one of those, right?

Each suggestion has an example sketch of what it looks like to follow and not follow it. This demonstration makes it easy to skim and absorb the suggestions quickly. There’s a text explanation for each of them, but most can be understood just by the examples.

Honestly a lot of these tips should be Common Sense. But I think that I could take almost every suggestion and find a project where I’ve gone the other way.

Has that ever been the right choice? Probably not.

Some tips that I have found helpful:

Summary

Those are your four resources, Mr or Mrs (or Ms!) Angular developer. You might be asking yourself:

Why only four…?

Here’s the answer:

There’s so much content in all of these that more would be superfluous.

I’ve gone through each of these resources in depth, and I find myself going back to them time and time again.

My brain needs to refresh. I need to continue building patterns, and remembering the things that are going to help my users the most.

diveMy suggestion to you is to start diving in.

Take notes (yes, get a pen or file up the ol’ text editor) of what you can apply right now in the Angular applications that you’re writing.

That’s going to have the largest impact on your, and your users.

Happy learning!

Interested in what I personally found most helpful? Check out my 20 UX Resource Take-Aways download below. Enter your email address and I’ll send you a link to my Bonus Content area where you can find this and other helpful resources.

I hate spam. I’ll never send you anything I don’t think is extremely helpful.

20 UX Resource Take-Aways PDF

Have a suggestion, question, or comment about the resources above, or on writing Angular applications with your users in mind? Leave a comment below ↓

The post 4 Great Resources for AngularJS UX appeared first on Brian Hann.

]]>
https://brianhann.com/4-great-resources-for-angularjs-ux/feed/ 4 624
Angular 1.4 Breaking Changes to Be Aware Of https://brianhann.com/angular-1-4-breaking-changes-to-be-aware-of/ https://brianhann.com/angular-1-4-breaking-changes-to-be-aware-of/#comments Thu, 11 Jun 2015 15:03:00 +0000 https://brianhann.com/?p=431 The Angular 1.4 release introduced a lot of breaking changes. You might have had trouble upgrading your 1.3 apps (if you even decided to try). There’s a good run-down on how to migrate your code in the Angular docs. We’ll cover some of the same ground here but also dive into some extra approaches for adjusting […]

The post Angular 1.4 Breaking Changes to Be Aware Of appeared first on Brian Hann.

]]>
The Angular 1.4 release introduced a lot of breaking changes. You might have had trouble upgrading your 1.3 apps (if you even decided to try).

There’s a good run-down on how to migrate your code in the Angular docs. We’ll cover some of the same ground here but also dive into some extra approaches for adjusting your code, as well as some cases I’ve run into where I’ve had to support both 1.3 and 1.4.

Jump to a section to read the changes.

 

$animate

JavaScript and CSS animations can no longer be run in
parallel.

In older versions, if both JS and CSS animations were detected they would be run at the same time. This won’t happen automatically any more, but you can use $animateCss to create CSS-based animations in your code.

 

The function params for $animate.enabled() when an
element is used are now flipped. This fix allows the function to act as
a getter when a single element param is provided.

I ran into this guy when trying to upgrade UI-Grid to Angular 1.4. The fix is to just swap the order of your parameters, unless you have to support multiple versions. In that case you’ll have to do some fun (not) version checking:

if (angular.version.major > 1 || (angular.version.major === 1 && angular.version.minor >= 4)) {
  $animate.enabled($elm, false);
}
else {
  $animate.enabled(false, $elm);
}

 

In addition to disabling the children of the element,
$animate.enabled(element, false) will now also disable animations on
the element itself.

Just something to keep in mind. It’s unlikely that you were implicitly relying on this, but it could bite you.

 

Animation-related callbacks are now fired on
$animate.on instead of directly being on the element.

If you were listening for events on your element with something like  element.on('$animate:before', ...) you’ll need to switch to $animate.on(element, 'enter', ...).

 

There is no need to call $scope.$apply or $scope.$digest inside of an animation promise callback anymore since the promise is resolved within a digest automatically (but a digest is not run unless the promise is chained).

Yay! This makes things a bit cleaner. It wouldn’t really hurt to keep any remaining $timeouts in your animation callbacks, but you don’t need them to trigger a $digest anymore.

 

When an enter, leave or move animation is triggered then it will always end any pending or active parent class based animations (animations triggered via ngClass) in order to ensure that any CSS styles are resolved in time.

This will probably help you out by not having multiple animations running when you don’t intend them.

 

Prior to this fix there were two ways to apply CSS animation code to an anchor animation. With this fix, the suffixed CSS -anchor classes are now not used any more for CSS anchor animations.

Animation anchoring lets you pair up elements that are in different structural parts of the application, like views, so that it appears that a single element is transitioning. You can read about it and see a demo in the Angular docs.

With this change you can just specify an .ng-anchor CSS class for your element (like .my-element.ng-anchor rather than having to make -anchor-suffixed extra rules.

Also note that you can use .ng-anchor-in and .ng-anchor-out if you want to target those phases of the animation.

 

If your CSS code made use of the ng-animate-anchor
CSS class for referencing the anchored animation element then your
code must now use ng-anchor instead.

They just renamed the class. If you want to support both 1.3.x and 1.4.x you could add an extra rule for.ng-anchor anywhere you have .ng-animate-anchor.

 

Partially or fully using a regex value containing
ng-animate as a token is not allowed anymore. Doing so will trigger a
minErr exception to be thrown.

This refers to using the classNameFilter() method in $animateProvider; ng-animate is essentially a reserved word. You can’t do this:

$animateProvider.classNameFilter(/my-animations ng-animate/);

but you could do this if you wanted to:

$animateProvider.classNameFilter(/ng-animate-mine/);

 

 

$animateCss

The $animateCss service will now always return an object even if the animation is not set to run.

You won’t need to check and see if $animateCss(element, { ... }); has returned anything any more.

 

 

$cookies

$cookies no longer exposes properties that represent the current browser cookie values. Now you must explicitly the methods described above to access the cookie values. This also means that you can no longer watch the $cookies properties for changes to the browser’s cookies.

The $cookies API originally polled for changes, synchronizing the local set with the browser. This was expensive and had out-of-sync issues.

Now you need to use the API methods to access the cookies:

var allCookies = $cookies.getAll();

 

 

filter

Previously, the filter was not applied if used with a non array. Now, it throws an error. This can be worked around by converting an object to an array, using a filter such as https://github.com/petebacondarwin/angular-toArrayFilter

The “filter” filter would fail silently if you used it on an object. As explained in the initial bug report  this would fail silently:

<div ng-repeat="item in object | filter:{display:true}">stuff</div>

You’d just see stuff repeated twice in your DOM with no explanation.

With 1.4.0 you’ll now get an error that looks like this: Expected array but received: {“display”:false,”foo”:”bar”}. Using PBD’s filter is a good way to make sure you’re using the proper object type with filter.

 

 

$http

transformRequest functions can no longer modify request headers … This behavior was unintended and undocumented, so the change should affect very few applications

The $http property  transformRequest can be used to alter a given request’s http request body. If you want to modify headers, the changelog says you can do it in your $http request with a headers function:

$http.get(url, {
  headers: {
    'X-Custom-Header': function(config) {
      return 'my-custom-value';
    }
  }
});

Or if you want more global alteration you can modify $httpProvider‘s header defaults:

$httpProvider.defaults.headers.common['X-Custom-Token'] = 'abc123';

 

 

limitTo

limitTo changed behavior when limit value is invalid.
Instead of returning empty object/array it returns unchanged input.

The limitTo filter creates a subset of an array, much like slice in vanilla JS. If you supply a bad value to limitTo, like a string or NaN, you’ll now get the unaltered array back rather than an empty array.

 

 

ngMessages

The ngMessagesInclude attribute is now its own directive and that must
be placed as a child element within the element with the ngMessages
directive.

Angular 1.3 introduced a nicer way to display form validation messages called ngMessages. You can read a nice intro in Pascal Precht’s blog post.

When displaying validation messages you would often need to use the same message over and over. This could lead too a less than DRY situation in your app. ngMessageInclude was a way to write a template for a validation message once and then include it in multiple places. It still exists; it’s just a directive instead of an attribute now:

<script type="script/ng-template" id="required-message">
  <ng-message when="required">
    This field is required!
  </ng-message>
</script>

<ng-messages for="myForm.username.$error">
  <div ng-message="minlength">...<div>
  <div ng-messages-include="required-message"></div>
</ng-messages>

You can read more about the change in Pasca’s follow-up on ngMessages.

 

 

ngOptions

When using ngOptions: the directive applies a surrogate key as the value of the <option> element. This commit changes the actual string used as the surrogate key. We now store a string that is computed by calling hashKey on the item in the options collection; previously it was the index or key of the
item in the collection.

This shouldn’t have too much effect on you, as you shouldn’t be relying on ngOptions‘ surrogate key. If you do then you’ll want to either add a track by expression to control how the surrogate key is defined, or just access the property that the select’s ng-model  is bound to.

Be Aware: If you were relying on the actual value of a select box in your unit tests, you’ll need to adjust for this. We ran into this with UI-Grid. Instead of $('.my-select-box').val() returning 5, you’ll get "number:5".

 

When iterating over an object’s properties using the (key, value) in obj syntax
the order of the elements used to be sorted alphabetically. … in practice this is not what people want and so this change iterates over properties in the order they are returned by Object.keys(obj).

If you were relying on this implicit ordering in ngOptions you’ll need to add a sorting filter.

 

Although it is unlikely that anyone is using it in this way, this change does change the behaviour of ngOptions in the following case:

  • You are iterating over an array-like object, using the array form of the ngOptions syntax (item.label for item in items) and that object contains non-numeric property keys.

In this case these properties with non-numeric keys will be ignored.

Hopefully you would not treat yourself (or your coworkers) so villainously, but there’s probably at least one person out there who has, at least unintentionally. If you find code with arrays that have extra properties and the repeater is iterating over them in this way, change it to the object iterator syntax: value.label for (key, value) in items.

 

 

ngRepeat

Previously, the order of items when using ngRepeat to iterate
over object properties was guaranteed to be consistent by sorting the
keys into alphabetic order.

Now, the order of the items is browser dependent based on the order returned from iterating over the object using the for key in obj syntax.

This is the same as the breaking change to ngOptions above. If you were relying on implicit sorting in ngRpeat when using an object as the source, then you’ll need to add your own sorting filter. Otherwise the elements in your repeater will come out in whatever order the user’s browser decides on.

Be Aware: The changelog says “…the best approach is to convert Objects into Arrays by a filter such as https://github.com/petebacondarwin/angular-toArrayFilter or some other mechanism, and then sort them manually in the order you need.”

The post Angular 1.4 Breaking Changes to Be Aware Of appeared first on Brian Hann.

]]>
https://brianhann.com/angular-1-4-breaking-changes-to-be-aware-of/feed/ 6 431
Pass Data to a UI-Bootstrap Modal Without $scope https://brianhann.com/pass-data-to-a-ui-bootstrap-modal-without-scope/ https://brianhann.com/pass-data-to-a-ui-bootstrap-modal-without-scope/#comments Thu, 23 Apr 2015 12:07:24 +0000 https://brianhann.com/?p=323 Angular UI Bootstrap‘s $modal service has a $scope argument that lets you assign it whatever $scope you want, so if you wanted your modal’s controller to use the same $scope as your parent controller you could do that. But what if you don’t want to use $scope? What if you’re using controllerAs and you want […]

The post Pass Data to a UI-Bootstrap Modal Without $scope appeared first on Brian Hann.

]]>
Angular UI Bootstrap‘s $modal service has a $scope argument that lets you assign it whatever $scope you want, so if you wanted your modal’s controller to use the same $scope as your parent controller you could do that.

But what if you don’t want to use $scope? What if you’re using controllerAs and you want to keep your code all scope-less, neat, and tidy?

Luckily there’s an alternative. You can send your data to the modal as resolves. Resolves first appeared with ngRoute, as a way to make sure information was available to a route’s controller before the view was initiated. It prevents elements shifting around in your view as data would otherwise  be loaded after the view shows up. It also keeps common data-loading tasks out of your controller.

Let’s look at an example. You’ve got a list of people in a table. Each has a name, and a delete button. When the user clicks the delete button a modal will come up confirming the delete.

We’ll pass the list of people and the person to delete into the modal’s controller using the resolve argument. Each property in the resolve object can be a string or function. If it’s a string, Angular will look for a service with that name and inject it into the modal controller. If it’s a function you can either return the value directly or return a promise if you’re fetching the data asynchronously.

Main.$inject = ['$modal'];
function Main($modal) {
  var vm = this;
  
  vm.deleteModal = deleteModal;
  vm.people = [
    'Fred',
    'Jim',
    'Bob'
  ];
  
  function deleteModal(person) {
    $modal.open({
      templateUrl: 'modal.html',
      controller: ['$modalInstance', 'people', 'person', DeleteModalCtrl],
      controllerAs: 'vm',
      resolve: {
        people: function () { return vm.people },
        person: function() { return person; }
      }
    });
  }

And just for clarity here’s our view, with the people ng-repeated in a table:

<table class="table">
  <tr ng-repeat="p in vm.people">
    <td>{{ p }}</td>
    <td>
      <button type="button" class="btn btn-sm btn-primary" ng-click="vm.deleteModal(p)">Delete</button>
    </td>
  </tr>
</table>

Alright, there’s our people and our person; person is passed in to the deleteModal function when the user clicks a delete button, deleteModal in turn passes people and person to DeleteModalCtrl as injectables.

Our modal controller can look like this:

function DeleteModalCtrl($modalInstance, people, person) {
  var vm = this;
  
  vm.person = person;
  vm.deletePerson = deletePerson;
  
  function deletePerson() {
    people.splice(people.indexOf(person), 1);
    $modalInstance.close();
  }
}

The injectables get passed in as arguments and we don’t need to use $scope at all, hooray! Finally, here is an example plunker showing how this all works together:




Sign Up

Like what you read? I'd love to send you more helpful tips and information, and believe me, I hate spam way more than you do!

The post Pass Data to a UI-Bootstrap Modal Without $scope appeared first on Brian Hann.

]]>
https://brianhann.com/pass-data-to-a-ui-bootstrap-modal-without-scope/feed/ 22 323