0

I’m really struggling to write a complex function in Angular that depends on promises. This is my first time writing a promise and I'm still not sure I fully understand how to do what I want to do with my code.

I have a variable var query = searchQuery.getQuery() in a controller ProfileNavCtrl. Then in my searchQuery service, getQuery fetches the value of localStorage.getItem('searchQuery') and checks if it’s an empty string or null. If it’s not empty or null, it simply returns the value to the controller. The value should be an array of slugs like ['foo','foo-bar','foo-bar-baz'].

If it is null or empty, it executes an $http.get call to fetch a JSON object and parse it. This is where things break down for me. I need getQuery() to return the value from $http.get (if the initial value of query is null) so that the controller variable query is assigned that value. As it is now, query (in the controller) is always set to null or undefined.

The $http.get call also calls setQuery() so that the query is persisted and future calls are avoided.

Here is my controller:

app.controller('ProfileNavCtrl', ['$scope', '$http', '$location', '$q', 'searchQuery',
function($scope, $http, $location, $q, searchQuery){
    var query = searchQuery.getQuery;
// do something with query

And here is my service:

app.service('searchQuery', ['$http', '$timeout', '$q', function($http, $timeout, $q){
    var query = [];

    this.getQuery = new Promise(function(){
        var query = localStorage.getItem('searchQuery');

        if(query == "" || query == [""] || query == null){
            var slugArray = [];
            var query = $http.get('/companies.json')
            .then(function(resp) {
                if(resp && resp.data) {
                    for(var i in resp.data) {
                        var result = resp.data[i];
                        if(resp.data[i].name){
                            slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
                        }
                    }
                    setQuery(slugArray);
                } else {
                    resetQuery();
                }
            }, function(err) {
                resetQuery();
            }).then(function(resp){
                return resp;
            })
            return query;
        } else {
            return query;
        };
    }).then(function(success){
        return success;
    });

UPDATE: 2nd Attempt Here is my controller code: var getQuery = searchQuery.getQuery();

getQuery.then(function(query){
    query = searchQuery.getQuery();
    // Check if user is on main site or portal
    if(location.pathname.split('/')[3] == null){
        var currentProfile = location.pathname.split('/')[1];
    } else {
        var currentProfile = location.pathname.split('/')[3];
    };

    // Get the next/prev query element (if any)
    console.log('6: ');
    console.log(query);
    var prev = query.slice(query.indexOf(currentProfile)-1)[0];
    var next = query.slice(query.indexOf(currentProfile)+1)[0];

    // Check if next/prev is undefined and if so, set to first/last element in query array
    if(prev){
        var prevProfile = prev;
    } else {
        var prevProfile = query.pop();
    };

    if(next){
        var nextProfile = next;
    } else {
        var nextProfile = query[0];
    };

    $scope.goToPrev = function() {
        // Check if user is on main site or portal
        if(location.pathname.split('/')[3] == null){
            var profileUrl = location.origin + '/' + prevProfile;
            // window.location = profileUrl;
            console.log(profileUrl);
        } else {
            var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + prevProfile;
            // window.location = profileUrl;
            console.log(profileUrl);
        }
    };

    $scope.goToNext = function() {
        // Check if user is on main site or portal
        if(location.pathname.split('/')[3] == null){
            var profileUrl = location.origin + '/' + nextProfile;
            // window.location = profileUrl;
            console.log(profileUrl);
        } else {
            var profileUrl = location.origin + '/' + location.pathname.split('/').slice(1,3).join('/') + '/' + nextProfile;
            // window.location = profileUrl;
            console.log(profileUrl);
        }
    };
});

Here is my updated service: this.getQuery = function(){ return new Promise(function(){ var query = localStorage.getItem('searchQuery');

        if(query == "" || query == [""] || query == null){
            var slugArray = [];
            return $http.get('/companies.json')
            .then(function(resp) {
                if(resp && resp.data) {
                    for(var i in resp.data) {
                        var result = resp.data[i];
                        if(resp.data[i].name){
                            slugArray.push(resp.data[i].name.toLowerCase().split(' ').join('-'));
                        }
                    }
                    setQuery(slugArray);
                } else {
                    resetQuery();
                }
                return slugArray;
            }, function(err) {
                resetQuery();
            });
        } else {
            return query;
        };
    });
};
2
  • 1
    getQuery should be a function that returns a promise if you want to call it. Commented Jun 25, 2015 at 21:46
  • I restructured my code a bit so getQuery returns a promise, but now I'm struggling with how to handle the promise in the controller. I want to get the result of the promise when it's successful and then define some methods on the $scope. Commented Jun 25, 2015 at 22:37

1 Answer 1

1

In Angular promises are provided through the $q service. See the documentation for more detail.

The basic outline to implement $q promise in your service is outlined below, I'll leave the detail on how to save to local storage etc to you:

this.getQuery = function(){
    var deferred = $q.defer();
    var query = localStorage.getItem('searchQuery');

    if(query == "" || query == [""] || query == null){
        $http.get('yoururl').then(function(resp) {
            // assuming resp is an array, else do your parsing to get array
            query = resp;
            deferred.resolve(query);
        }, function(err) {
            query = null;
            deferred.reject(err);
        });
    } else {
        deferred.resolve(query);
    };
    return deferred.promise;
};

You can then use this in your controller like:

var query = null;
searchQuery.getQuery().then(function(result) {
    query = result;
}, function(err) {
    // Error occured
});
Sign up to request clarification or add additional context in comments.

4 Comments

I tried a similar setup before that didn't work for me. I've since been leaning away from using deferred as I've read several blog posts such as this one that say it's a bad practice. I posted my 2nd attempt in my question. I've got a promise being delivered to the controller now, I'm just not sure what to do with it.
$q is Angular's implementation of promises and tightly integrated throughout all Angular built-in services. q$ can be used in either a deferred fashion or something that resembles ES6 promises to some degree. But deferred fashion is by far the most used way in Angular and 3rd party Angular modules. Either way, you are left to using $q and not ES6 promises directly as you are currently trying to do. $q is also integrated with the scope model observation mechanism in angular.
So are you saying that I have to use $q and that the ES6 approach I've been trying to implement won't work? I'm at a point now where I've got a promise object Promise {[[PromiseStatus]]: "resolved"} being delivered to my controller, but I'm not sure how to handle it.
I finally conceded defeat with the ES6 approach and tried your solution using $q. I moved all my query-dependent controller logic inside the then function and everything seems to work perfectly now! I'm still curious to know why the ES6 approach wouldn't work in Angular?

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.