Question

In working with the API from themoviedb.com, I'm having the user type into an input field, sending the API request on every keyup. In testing this, sometimes the movie poster would be "null" instead of the intended poster_path. I prefer to default to a placeholder image to indicate that a poster was not found with the API request.

So because the entire poster_path url is not offered by the API, and since I'm using an AngularJS ng-repeat, I have to structure the image tag like so (using dummy data to save on space):

<img ng-src="{{'http://example.com/'+movie.poster_path}}" alt="">

But then the console gives me an error due to a bad request since a full image path is not returned. I tried using the OR prompt:

{{'http://example.com/'+movie.poster_path || 'http://example.com/missing.jpg'}}

But that doesn't work in this case. So now with the javascript. I can't seem to get the image source by using getElementsByTagName or getElementByClass, and using getElementById seems to only grab the first repeat and nothing else, which I figured would be the case. But even then I can't seem to replace the image source. Here is the code structure I attempted:

<input type="text" id="search">
<section ng-controller="movieSearch">
  <article ng-repeat="movie in movies">
    <img id="myImage" src="{{'http://example.com/'+movie.poster_path}}" alt="">
  </article>
</section>

<script>
function movieSearch($scope, $http){
  var string,
      replaced,
      imgSrc,
      ext,
      missing;

  $(document).on('keyup', function(){
    string = document.getElementById('search').value.toLowerCase();
    replaced = string.replace(/\s+/g, '+');
    $http.jsonp('http://example.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
      console.dir(data.results);
      $scope.movies = data.results;
    });
    imgSrc = document.getElementById('myImage').src;
    ext = imgSrc.split('.').pop();
    missing='http://example.com/missing.jpg';
    if(ext !== 'jpg'){
      imgSrc = missing;
    }
  });
}
</script>

Any ideas with what I'm doing wrong, or if what I'm attempting can even be done at all?

Was it helpful?

Solution

The first problem I can see is that while you are setting the movies in a async callback, you are looking for the image source synchronously here:

$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
  console.dir(data.results);
  $scope.movies = data.results;
}); 

// This code will be executed before `movies` is populated

imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();

However, moving the code merely into the callback will not solve the issue:

// THIS WILL NOT FIX THE PROBLEM

$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
  console.dir(data.results);
  $scope.movies = data.results;

    // This will not solve the issue

    imgSrc = document.getElementById('myImage').src;
    ext = img.split('.').pop();
    // ...
}); 

This is because the src fields will only be populated in the next digest loop.

In your case, you should prune the results as soon as you receive them from the JSONP callback:

function movieSearch($scope, $http, $timeout){
  var string,
      replaced,
      imgSrc,
      ext,
      missing;

  $(document).on('keyup', function(){
    string = document.getElementById('search').value.toLowerCase();
    replaced = string.replace(/\s+/g, '+');
    $http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
      console.dir(data.results);
      $scope.movies = data.results;

      $scope.movies.forEach(function (movie) {
        var ext = movie.poster_path && movie.poster_path.split('.').pop();

        // Assuming that the extension cannot be
        // anything other than a jpg
        if (ext !== 'jpg') {
            movie.poster_path = 'missing.jpg';
        }
      });
    });
  });
}

Here, you modify only the model behind you view and do not do any post-hoc DOM analysis to figure out failures.


Sidenote: You could have used the ternary operator to solve the problem in the view, but this is not recommended:

<!-- NOT RECOMMENDED -->
{{movie.poster_path && ('http://domain.com/'+movie.poster_path) || 'http://domain.com/missing.jpg'}}

OTHER TIPS

First, I defined a filter like this:

In CoffeeScript:

app.filter 'cond', () ->
    (default_value, condition, value) ->
        if condition then value else default_value

Or in JavaScript:

  app.filter('cond', function() {
    return function(default_value, condition, value) {
      if (condition) {
        return value;
      } else {
        return default_value;
      }
    };
  });

Then, you can use it like this:

{{'http://domain.com/missing.jpg' |cond:movie.poster_path:('http://domain.com/'+movie.poster_path)}}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top