1

I've built a basic Angular app that successfully displays the results of an HTTP GET request.

I'd like to include fallback code that displays two static HTML elements in place of the remote content if the GET request fails.

I can just call a vanilla JS function to do DOM manipulation, but I'd like to do this the Angular way. I've read a lot of documentation and articles, but I'm not seeing a straightforward way to do this. Code below.

I'd like to replace the call to updateUIError() with Angular code that performs the same task.

Here's a Plunk: https://plnkr.co/edit/PDwSUCXGNW2cwAIwl9Z9?p=streamer

HTML:

  <div class="scene fullheight" id="attractions" ng-app="listApp">
    <article class="content">
        <h1>Upcoming Events</h1>
        <div class="main" ng-controller="ListController as eventsList">
          <div class="search">
            <label>search: </label>
            <input ng-model="query" placeholder="Search for events" autofocus>
            <label class="formgroup">by:
              <select ng-model="eventOrder" ng-init="eventOrder='start.local'">
                <option value="start.local">Date</option>
                <option value="name.text">Name</option>
              </select>
            </label>
            <label class="formgroup">
              <input type="radio" ng-model="direction" name="direction" checked>
              ascending
            </label>
            <label class="formgroup">
              <input type="radio" ng-model="direction" name="direction" value="reverse">
              descending
            </label>
          </div>

          <ul class="eventlist">
            <li class="event cf" ng-repeat="item in eventsList.events | filter: query | orderBy: eventOrder:direction">
              <div class="info">
                <h2><a href="{{item.url}}" target="_blank">{{item.name.text}}</a></h2>
                <p>{{item.start.local | date:"dd MMMM ', ' h:mma"}}</p>
              </div>
            </li>
          </ul>
        </div>
    </article>
  </div>

Angular:

angular.module('listApp', [])
  .controller('ListController', ['$scope', '$http', function($scope,$http) {
    var eventsList = $scope.eventsList;
    $http.get(URI)
    .success(function(data) {
      console.log(data);
      eventsList.events = data.events;
    }).error(function() {
      updateUIError();
    });
  }]); 

  function updateUIError() {
    var events = document.querySelector('#attractions article');
        events.innerHTML = '<h1>Local Attractions</h1><p>There's lots to do nearby.</p>';
    }

3 Answers 3

2

You need to create a static error and show it when the error occurs using ngIf

<div class="scene fullheight" id="attractions" ng-app="listApp">
    <div ng-controller="ListController">
        <article class="content" ng-if="hasUIError">
            <h1>Local Attractions</h1>
            <p>There's lots to do nearby.</p>
        </article>
        <article class="content" ng-if="!hasUIError">
            <h1>Upcoming Events</h1> 
            <!-- REST OF THE HTML -->
        </article>
    </div>
</div>

Then, in your controller, you need to set the flag to false by default:

$scope.hasUIError = false;

And when there's an error in the ajax, set it to true

$http.get(URI).then(
    function(response) {
        console.log(response);
        $scope.events = response.data.events;
    },
    function() {
       $scope.hasUIError = true;
    }
);
Sign up to request clarification or add additional context in comments.

6 Comments

I've been playing with ngIf as well as ng-show and ng-hide, but I am unable to get them to work. The code above does not work for me. No matter whether I set hasUIError to true or false in my controller, the second article is displayed and the first is hidden.
Try to combine my answer and @jbrown's - I didn't noticed that you had controllerAs in your view - Implement the part with the vm on the controller side. Alternatively - You can change your view and set ng-controller="ListController" and also remove all the eventsList. prefixes in the view
@quietquake Note that I change my answer, replaced eventsList.events = data.events; with $scope.events = response.data.events; so it will work well if you DON'T use the controllerAs method
@quietquake I added a new change to my code, I forgot to include the ng-controller part in the view
Okay, I figured this out and updated the Plunk. The issue was that the .success function seems to return after vm.events = data.events, so the code I was adding to change the value of vm.showError was never running. I reordered things so vm.events = data.events comes after the showError value, and things work great. Thanks for your help!
|
2

Before solving your issue, there is another issue to address. Specifically, since you are using the ControllerAs approach, you'll want to attach your variables to 'this' rather than $scope.

Then you create a variable called showError that will get evaluated in showing the message. Then you can use the ng-show directive to hide/show the message.

    angular.module('listApp', [])
      .controller('ListController', ['$http',
        function($http) {
          var vm = this;
          vm.events = [];
          vm.showError = false;

          $http.get(URI)
            .success(function(data) {
              vm.events = data.events;
            }).error(function() {
              vm.showError = true;
            });
        }
      ]);
<!DOCTYPE html>
<html ng-app="listApp">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="[email protected]" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.min.js" data-semver="1.5.10"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="ListController as eventsList">
  <div class="scene fullheight" id="attractions">
    <div ng-show="eventsList.showError">
      <h1>Local Attractions</h1>
      <p>There's lots to do nearby.</p>
    </div>
    <article class="content">
      <h1>Upcoming Events</h1>
      <div class="main">
        <div class="search">
          <label>search:</label>
          <input ng-model="query" placeholder="Search for events" autofocus>
          <label class="formgroup">by:
            <select ng-model="eventOrder" ng-init="eventOrder='start.local'">
              <option value="start.local">Date</option>
              <option value="name.text">Name</option>
            </select>
          </label>
          <label class="formgroup">
            <input type="radio" ng-model="direction" name="direction" checked>ascending
          </label>
          <label class="formgroup">
            <input type="radio" ng-model="direction" name="direction" value="reverse">descending
          </label>
        </div>

        <ul class="eventlist">
          <li class="event cf" ng-repeat="item in eventsList.events | filter: query | orderBy: eventOrder:direction">
            <div class="info">
              <h2><a href="{{item.url}}" target="_blank">{{item.name.text}}</a></h2>
              <p>{{item.start.local | date:"dd MMMM ', ' h:mma"}}</p>
            </div>
          </li>
        </ul>
      </div>
    </article>
  </div>
</body>

</html>

5 Comments

I have replaced my code with yours in the controller and the HTML, and still always have the same article element being displayed. I've changed the vm.showError value manually from false to true and back, and there is no change in the browser -- it's always the article showing events that is displayed.
That's probably because of other issues in your code like $http.get(URI) where URI hasn't been defined. So the load of the controller is bombing.
I have a URI, but it includes an API key I can't share publicly. The code does work with the API key.
Did you also go with my html? You had ng-controller assignment in wrong place for what you were wanting to do.
If I could mark 2 right answers, I'd include yours. I marked Alon Eitan's because he walked me through incorporating your suggestions. I gave you an upvote, tho. Thanks for your help!
0

Do something like this:

   angular.module('listApp', [])
  .controller('ListController', ['$scope', '$http', function($scope,$http) {
     var eventsList = $scope.eventsList;
     $http.get(URI)
  .success(function(data) {
     console.log(data);
     eventsList.events = data.events;
}).error(function() {
     eventsList.errorMessage = '<h1>Local Attractions</h1><p>There's lots to do nearby.</p>';
});
}]); 

In the HTML, add a span inside the scope of ListController that will have ngModel="errorMessage". You can add additional property to show / hide the error span and main content div.

4 Comments

What would that additional code for showing/hiding elements look like? That's where I'm stuck now.
set a boolean property within same scope and use ng-show or ng-hide in the respective elements to control visibility
It would be best, if you share a plunker of your problem
The auto bootstrapping was not working because you had two ng-app defined and the first one with name "plunker" did not have a module defined. MainCtrl also didnot exist. I have made removed those, check this plunker plnkr.co/edit/xRAHOmDcQSvGs8nJdaFG?p=preview and let me know if this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.