370

Currently I have an Angular.js page that allows searching and displays results. User clicks on a search result, then clicks back button. I want the search results to be displayed again but I can't work out how to trigger the search to execute. Here's the detail:

  • My Angular.js page is a search page, with a search field and a search button. The user can manually type in a query and press a button and and ajax query is fired and the results are displayed. I update the URL with the search term. That all works fine.
  • User clicks on a result of the search and is taken to a different page - that works fine too.
  • User clicks back button, and goes back to my angular search page, and the correct URL is displayed, including the search term. All works fine.
  • I have bound the search field value to the search term in the URL, so it contains the expected search term. All works fine.

How do I get the search function to execute again without the user having to press the "search button"? If it was jquery then I would execute a function in the documentready function. I can't see the Angular.js equivalent.

Roshana Pitigala
  • 8,437
  • 8
  • 49
  • 80
Duke Dougal
  • 24,359
  • 31
  • 91
  • 123
  • are you using `$routepProvider`? Use it to connect to service in your app that provides data for the search results. Don't think in `document.ready` terms as with jQuery. Hard to help a lot without seing how you have search wired up currently – charlietfl Mar 17 '13 at 11:47

14 Answers14

438

On the one hand as @Mark-Rajcok said you can just get away with private inner function:

// at the bottom of your controller
var init = function () {
   // check if there is query in url
   // and fire search in case its value is not empty
};
// and fire it after definition
init();

Also you can take a look at ng-init directive. Implementation will be much like:

// register controller in html
<div data-ng-controller="myCtrl" data-ng-init="init()"></div>

// in controller
$scope.init = function () {
    // check if there is query in url
    // and fire search in case its value is not empty
};

But take care about it as angular documentation implies (since v1.2) to NOT use ng-init for that. However imo it depends on architecture of your app.

I used ng-init when I wanted to pass a value from back-end into angular app:

<div data-ng-controller="myCtrl" data-ng-init="init('%some_backend_value%')"></div>
Dmitry Evseev
  • 11,533
  • 3
  • 34
  • 48
  • 6
    @Duke, can't you just put the contents of the init() function at the bottom of your controller? I.e., why do you need to use ng-init and have an init() function? When the user hits the back button, I assume you are switching views, hence a new `myCtrl` controller is created and executes. – Mark Rajcok Mar 18 '13 at 15:41
  • 25
    While this solution works, I downvoted because the ng-init documentation advises against this. Specifically, "The only appropriate use of ngInit for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope." – Jason Capriotti Dec 04 '13 at 20:59
  • Hi, I am not quite understand the mark's solution. if init() is at the bottom of a controller, it will get executed straight away, not after page loaded, isn't it? – GingerJim Jan 28 '14 at 18:19
  • 1
    Controller is instantiated after html is on its place. – Dmitry Evseev Jan 28 '14 at 23:01
  • 3
    What if the controller is reused in other pages but you just want to fire it on a specific page? – Roberto Linares Jun 02 '14 at 02:10
  • 3
    @RobertoLinares I personally find directives to be a better place for reusable functionality. You can have a directive with an init parameter: `
    `. Of course it depends on a certain usage case.
    – Dmitry Evseev Jun 02 '14 at 15:20
  • Be sure to have your ng-controller attribute precede the ng-init attribute as they run at the same precedence. If you reverse the order ng-init will be calling a function that doesnt exist. – mccainz Sep 30 '14 at 21:16
  • 1
    i know this is an old post, but it's worth noting if you use asp.net mvc, ng-init is a fine way to pass your @model back to the controller and then in your controller you can just `function init(viewmodel){$scope.vm = viewmodel};` and now you can interpolate the entire model. it's bad design to initialize a $scope variable in the html because that's not the responsibility of the html (and it's slow). However, in asp.net mvc it makes sense and works effectively. – smurtagh Dec 07 '17 at 21:04
  • I agree with Evseev, it really depends on the architecture of the application. – Brain Apr 17 '18 at 06:03
136

Try this?

$scope.$on('$viewContentLoaded', function() {
    //call it here
});
holographic-principle
  • 19,688
  • 10
  • 46
  • 62
  • 5
    Thanks for the reply but Dmitry's answer was an exact match to what I'm looking for. Further research seemed to recommend against $viewContentLoaded – Duke Dougal Mar 18 '13 at 03:16
  • 7
    The use cases are just different. I use Mark's method all the time as well. – holographic-principle Jun 20 '13 at 23:21
  • For my use case this was a perfect solution. We needed to autoselect the text to encourage user to copy it. The page could be reloaded/revisited many times, so $viewContentLoaded was an exact match. Thanks! – Olga Gnatenko Jul 07 '15 at 14:26
  • Is it possible that this is executed several times? I am getting a behavior that seems to be mimicking that ... – MadPhysicist Jul 31 '16 at 02:14
  • 1
    This one did not work for me but $scope.$watch worked. What is the main difference? – Burak Tokak Aug 23 '16 at 08:51
60

I could never get $viewContentLoaded to work for me, and ng-init should really only be used in an ng-repeat (according to the documentation), and also calling a function directly in a controller can cause errors if the code relies on an element that hasn't been defined yet.

This is what I do and it works for me:

$scope.$on('$routeChangeSuccess', function () {
  // do something
});

Unless you're using ui-router. Then it's:

$scope.$on('$stateChangeSuccess', function () {
  // do something
});
Owen Blacker
  • 4,117
  • 2
  • 33
  • 70
adam0101
  • 29,096
  • 21
  • 96
  • 174
  • 1
    You are awesome, my friend. – taco Apr 21 '15 at 16:14
  • Exactly what I needed. I was literally looking for this for two days! – hellojebus Apr 26 '16 at 05:22
  • 3
    `$scope.$on('$routeChangeSuccess'` fires every time parameters in url changed (via `$location` for example), and thus you can use just `$scope.$on('$locationChangeSuccess'`. If you need some trigger at page load/reload, you can use `$scope.$watch('$viewContentLoaded'` as was suggested here. It will not fire during url parameters changes. – Neurotransmitter Dec 14 '16 at 07:54
  • using AngularJS 1.17.2, inside controller *$routeChangeSuccess* worked amazingly...long live adam0101... – ArifMustafa Aug 03 '18 at 18:37
48
angular.element(document).ready(function () {

    // your code here

});
Carlos Trujillo
  • 657
  • 5
  • 8
32

Dimitri's/Mark's solution didn't work for me but using the $timeout function seems to work well to ensure your code only runs after the markup is rendered.

# Your controller, including $timeout

var $scope.init = function(){
 //your code
}

$timeout($scope.init)

Hope it helps.

guico
  • 369
  • 3
  • 2
  • 3
    Finally. This is the only one that worked for me. Thanks. – zmirc Aug 20 '15 at 09:17
  • 1
    This works. Also don't forget to clear the timeout with $timeout.cancel(timer) where you have var timer = $timeout($scope.init, milliseconds); once your initialization is done. Also, I don't think $scope should have a 'var' def in your code above – Emmanuel N K Nov 22 '16 at 04:26
  • This should be the accepted answer. It was the only one that worked for me (tried all answers above). It even works if you are waiting for an iframe to load. – ph_0 Aug 03 '17 at 12:24
28

You can do this if you want to watch the viewContentLoaded DOM object to change and then do something. using $scope.$on works too but differently especially when you have one page mode on your routing.

 $scope.$watch('$viewContentLoaded', function(){
    // do something
 });
CodeOverRide
  • 4,431
  • 43
  • 36
  • 7
    Specifically, if you use $rootScope.$watch instead of $rootScope.$on it won't fire on route changes, so you can use logic specific to the initial page load. – csahlman Mar 18 '14 at 12:30
20

You can use angular's $window object:

$window.onload = function(e) {
  //your magic here
}
MCGRAW
  • 777
  • 14
  • 37
4

Another alternative:

var myInit = function () {
    //...
};
angular.element(document).ready(myInit);

(via https://stackoverflow.com/a/30258904/148412)

Community
  • 1
  • 1
ANeves
  • 6,219
  • 3
  • 39
  • 63
3

When using $routeProvider you can resolve on .state and bootstrap your service. This is to say, you are going to load Controller and View, only after resolve your Service:

ui-routes

 .state('nn', {
        url: "/nn",
        templateUrl: "views/home/n.html",
        controller: 'nnCtrl',
        resolve: {
          initialised: function (ourBootstrapService, $q) {

            var deferred = $q.defer();

            ourBootstrapService.init().then(function(initialised) {
              deferred.resolve(initialised);
            });
            return deferred.promise;
          }
        }
      })

Service

function ourBootstrapService() {

 function init(){ 
    // this is what we need
 }
}
Anik Islam Abhi
  • 25,137
  • 8
  • 58
  • 80
Leo Lanese
  • 476
  • 4
  • 5
3

Yet another alternative if you have a controller just specific to that page:

(function(){
    //code to run
}());
Chipe
  • 4,641
  • 10
  • 36
  • 64
3

Found Dmitry Evseev answer quite useful.

Case 1 : Using angularJs alone:
To execute a method on page load, you can use ng-init in the view and declare init method in controller, having said that use of heavier function is not recommended, as per the angular Docs on ng-init:

This directive can be abused to add unnecessary amounts of logic into your templates. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, as seen in the demo below; and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope.

HTML:

<div ng-controller="searchController()">
    <!-- renaming view code here, including the search box and the buttons -->
</div>

Controller:

app.controller('SearchCtrl', function(){

    var doSearch = function(keyword){
        //Search code here
    }

    doSearch($routeParams.searchKeyword);
})

Warning : Do not use this controller for another view meant for a different intention as it will cause the search method be executed there too.

Case 2 : Using Ionic:
The above code will work, just make sure the view cache is disabled in the route.js as:

route.js

.state('app', {
    url           : '/search',
    cache         : false, //disable caching of the view here
    templateUrl   : 'templates/search.html'   ,
    controller    : 'SearchCtrl'
  })

Hope this helps

Kailas
  • 7,350
  • 3
  • 47
  • 63
  • 1
    This only had one up and was at the very bottom, glad I went all the way down, the `cache: false` did it for me – thanks! – Rani Kheir Aug 17 '16 at 11:40
3

I had the same problem and only this solution worked for me (it runs a function after a complete DOM has been loaded). I use this for scroll to anchor after page has been loaded:

angular.element(window.document.body).ready(function () {

                        // Your function that runs after all DOM is loaded

                    });
2

You can save the search results in a common service which can use from anywhere and doesn't clear when navigate to another page, and then you can set the search results with the saved data for the click of back button

function search(searchTerm) {
    // retrieve the data here;
    RetrievedData = CallService();
    CommonFunctionalityService.saveSerachResults(RetrievedData);
} 

For your backbutton

function Backbutton() {
    RetrievedData = CommonFunctionalityService.retrieveResults();
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Heshan
  • 772
  • 8
  • 22
0

call initial methods inside self initialize function.

(function initController() {

    // do your initialize here

})();
Uditha Prasad
  • 695
  • 5
  • 13