0

I have a simple search form that passes to a controller that fires an AJAX request to Flickr. Upon receiving the returned data I want to change to another view for display of the results. Everything works fine except that I am unable to pass the JSON data between the two views. I've tried various permutations of state.go('results', {...} and adding params to the stateProvider but this doesn't appear to work for me - as this doesn't appear to be a particularly unusual thing to want to achieve I'm obviously missing something rather daft. Presumably.

Main App.js

var photoApp = angular.module('photoApp', ['infinite-scroll', 'ui.router']);

photoApp.config(function ($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/home');

    $stateProvider
    .state('home', {
        url: '/home',
        templateUrl: '/Client/Views/partial-search.html'
    })
    .state('results', {

        templateUrl: '/Client/Views/partial-results.html',      
        params: { 'photos': ''}
    });

});

Controller

//snip
$scope.processForm = function($stateParams){

        //snip

        $http({
            method: 'JSONP',
            url: 'https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=' + key + '&text=' + $scope.formData.search + '&extras=owner_name,tags,description',
            params: {
                'format': 'json',
                'jsoncallback': 'JSON_CALLBACK'
            }
        }).success(function (data) {

            $scope.photo = data.photos;
            $state.go('results', { 'photos': $scope.photo });
        });
    };

    $scope.loadMore = function () {

       // populate an 'infinite scroll in the results page
        }
    };
};
app.controller("FlickrController", FlickrController);

partial-results.html - builds a four-column grid of images

<div ng-controller="FlickrController">
  <div class="row">
    <div infinite-scroll='loadMore()' infinite-scroll-distance='2'>
        <div ng-repeat='image in images'>        
                ...

In case the flow isn't clear: View 1(searchform) -> processForm()(controller) -> view2 (partial-results.html) -> loadMore() (controller). It's getting the $scope.photos to be available in the partial-results view that is the issue.

  • 1
    One way is some service as a storage, I do prefer to to use scope sharing ... see more [here](http://stackoverflow.com/q/27696612/1679310) – Radim Köhler Nov 17 '15 at 18:42
  • 1
    How do you access `$stateParams.photos`? What does your `$scope.images` object look like? The relevant code is missing here. – Andre Kreienbring Nov 17 '15 at 19:06
  • `$scope.images` just helps populate a photo wall - this works fine and isn't related to the above problem - it's part of the `infinite scroll / ng-repeat`. Accessing `$stateParams.photos`? Wrongly as it appears from @RadimKöhler 's link - two instances of the FlickrController. – Fluffmeister General Nov 17 '15 at 19:14
  • Hmm... making `results` a child of `home` as per the linked post above breaks `state.go()` so the results page never loads. – Fluffmeister General Nov 17 '15 at 19:34

1 Answers1

1

Sharing data between controllers should generally be done through services, so should the API calls. It's not mandatory but a best practice, you get a better architecture and you also get less headaches. It's like this:

Controller1 <----> ServiceData <----> Controller2

The $state method should be used for navigation purposes and sending simple parameters between views or states.

Your specific case, TL'DR: (just my 2 cents, there other alternatives)

  • You should have a FlickerService that will:
    1. hold the list of results (ie: FlickerService.someObject={someProperty:"anything", resultsList:[]};) and
    2. will provide a method for you to update that list,with a parameter telling you want more results (for infinite scroll) besides the search parameters. Let's call it grabFlickerData(searchQueryJson, loadMoreResults), Note: the key should be kept in a config file and you will use it in the service, but it's not the priority now .
    3. that method will just return the http promise (return $http.get(.../*your old code here*/...).
    4. On the success callback (.success(function (data) {..}) you will get the data from the API and put it in in your object: FlickerService.someObject.resultsList = data. You can also iterate through the items, process each item and do a FlickerService.someObject.resultsList.push(item) one by one if you need more control over the result items. Note: if you need to repeatedly add data to the list you may want to append the data to the array instead of replacing it.
  • In your partial-search controller you call the service method when you need the data (in processForm or loadMore methods, or both, or wherever)
  • again, in the partial-search controller, you will get the $http promise from the service together with the data and the 2 callbacks: for success and error, just like you did in the service. You don't need to touch the data here but, most important, on the success callback you redirect to the other state: $state.go('results'). Note that you don't need to send any other data. You may want to pass a result if you like, ie:'All Good, we have data' if you feel the urge to communicate between the 2 controllers.
  • in the partial-results controller you will define a scope variable bound to the list in the service, like $scope.greatResults = FlickerService.someObject.resultsList and use it:
  • in the partial-results view: <div ng-repeat="result in greatResults">...</div> or use the list how you want
  • for infinite scroll you just need to call the service method telling it to get you more results (with the parameter described above).

Hope this helps a little; if you have questions add a comment. I may come back later to edit the answer with more code samples if needed.

bosch
  • 1,089
  • 11
  • 16