Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs: how to revert back to the runtime loaded DOM elements in the previous view on going back (preserve state)

I have an angular application which has two views:

1) List view

2) Detail View

when you click on the thumbnail from list view you go to detail view, here is the route:

app.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/list', {
        templateUrl: 'partials/list.html',
        controller: 'ListCtrl',

      }).
      when('/list/:id', {
        templateUrl: 'partials/detail.html',
        controller: 'DetailCtrl',

      }).
      otherwise({
        redirectTo: '/list'
      });
  }]);

Now there is a function loadmore in 'listCtrl' controller which is used to load

myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', '$http',

function ($scope, $location, Troll, $http) {
    $scope.Trolls = Troll.query();
    $scope.orderProp = 'popular';
    $scope.fromData = {};
    //console.log($scope.Trolls);
    //this will be used to fetch the data in json which is defined in services.js

    $scope.loadmore = function () {
        jQuery.ajax({
            url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
            type: 'GET',
            async: false,
            data: {},
            dataType: 'json',
            success: function (response) {


                if (response != null) {
                    $.each(response, function (index, item) {

                        $scope.Trolls.push({
                            UID: response[index].UID,
                            id: response[index].id,
                            popular: response[index].popular,
                            imageUrl: response[index].imageUrl,
                            name: response[index].name,
                            tags: response[index].tags,
                            category: response[index].category
                        });

                    });
                }
            },
            complete: function () {},
            error: function () {
                console.log('Failed!');
            }
        });
        $scope.text = 'Hello, Angular fanatic.';
        $http.get('trolls/trolls.php?troll_id=' + Troll);

    }

}]);

PROBLEM: now the problem is, After clicking on loadmore if i go to detail view and i come back on list view my newly loaded divs are gone how do i preserve them??

like image 741
Alex Avatar asked Dec 03 '22 19:12

Alex


2 Answers

When you change routes the controller that is in charge of that route is initialized when the route is loaded and destroyed when the route is changed. So the reason you lose your data is the controller is reinitialized and the previous data never exists.

There are two ways to fix this.

  1. Higher level controller that is not destroyed - could potentially live on the body - this passes its scope to the children controllers. But this is not a true modularization of concerns. For this issue... Can be very useful for other issues - Authentication, Profile, etc.

  2. The way I would advocate is to pull this into a service for example - listService - this will fetch and cache the data and pass it back to the listController when its reloaded, thus preventing the data from being lost.


First way to solve could be this...

So if you have a higher level controller in charge of fetching the data or move it into a service which is what I would do, then the data that is loaded from loadMore function will continue to be there, but it needs to be on a higher parent scope that is not destroyed on the route change.

HTML:

<body ng-controller="ApplicationController">
     <!-- Code Here -->
</body>

Controller:

myControllers.controller('ApplicationController', function($scope) {
     var data = [];

     $scope.loadmore = function () {
        // Use Angular here!!! $http not jQuery! 
        // Its possible to write a complete Angular app and not ever true jQuery
        // jQuery Lite the Angular implementation will be used though
        jQuery.ajax({
            url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
            type: 'GET',
            async: false,
            data: {},
            dataType: 'json',
            success: function (response) {


                if (response != null) {
                    $.each(response, function (index, item) {

                        data.push({
                            UID: response[index].UID,
                            id: response[index].id,
                            popular: response[index].popular,
                            imageUrl: response[index].imageUrl,
                            name: response[index].name,
                            tags: response[index].tags,
                            category: response[index].category
                        });

                    });
                }

                return data;

            }
            error: function () {
                console.log('Failed!');
            }
        });

    }
});

However I dont really like this approach as its a bit hacky...and jQuery is used...

Second approach using a service to fetch/cache:

So lets pull it into a service instead.

myServices.factory('listService', function($http, $q) {

   var//iable declaration 
      service = {},
      list = []
   ;
   /////////////////////   
   //Private functions//
   /////////////////////

   function loadMore(url) {
      var deferred = $q.defer();

      $http({ method: 'GET', url: url }) // Need to pass in the specific URL maybe from the DOM scoped function?
      .success(function(data) {
         deferred.resolve(data);
      })
      .error(function() {
        deferred.reject();
        //Do error things
      });   

     return deferred.promise; 
   }

   ////////////////////
   //Public Functions//
   ////////////////////

   service.loadMore = function(url) { 
      // Used for loading more data
      loadMore(url).then(function(data) {
        list.push(data);
        return list
      });
   }

   service.getList = function() {
      // Returns the currently loaded data
      return list;
   }

 return service;

});

Then in your controller:

myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', listService

function ($scope, $location, Troll, listService) {
    $scope.Trolls = Troll.query();
    $scope.orderProp = 'popular';
    $scope.fromData = {};


    $scope.loadmore = function(subItemSize) { //add url specific params here
       var url = 'trolls/trolls.php?troll_index=' + subItemSize;
       return listService.loadMore(url);
    };

}]);
like image 58
Sten Muchow Avatar answered Dec 30 '22 07:12

Sten Muchow


use ui-router instead: https://github.com/angular-ui/ui-router, you can have multiple ui-view and also you can use relative routes, you should define child states, this way the parent state remains unchanged when route changes to a child state, check out this video:https://egghead.io/lessons/angularjs-introduction-ui-router

i created a plunker:http://plnkr.co/edit/FA3DuHgngKD2CIPG5bHW?p=preview

like image 39
hmd.ai Avatar answered Dec 30 '22 07:12

hmd.ai