lorenzofox3 / Smart-Table

Code source of Smart Table module: a table/grid for Angularjs
http://lorenzofox3.github.io/smart-table-website/
1.8k stars 513 forks source link

Restore the state of a SmartTable - filters and page #825

Open moldovans opened 6 years ago

moldovans commented 6 years ago

AngularJs v 1.6.6; SmartTable v 2.1.8

I have items in row like [id] [name] [edit].

When the user edits an item(/items/1/edit), the item's EditPage contains a "back to list" link (/items);

That link returns the user to the list of the items. Now the problem is that in that case the page is always the first one, and any applied filters are lost.

Is there a way to say to the SmartTable to remember and restore the "before-edit mode" table state?

MrWook commented 6 years ago

Hello @serhioV,

there is an example of the Persist table state in the documentation. Here is the plunk for it

moldovans commented 6 years ago

Thank you, MrWook, for the precious suggestion. In my case it really restores the filter, but resets the pagination. because of

angular.extend(tableState, savedState
ctrl.pipe(); /* here the pagination is gone */

I have the following directive that filters the table for min-max update date:

app.directive('stDateRange', [function () {
    return {
        restrict: 'E',
        require: '^stTable',
        templateUrl: '/templates/stDateRange.en.html',
        scope: false,
        link: function (scope, element, attr, ctrl) {
            var tableState = ctrl.tableState();
            scope.$watchGroup(["minDate", "maxDate"],
                function (newValues, oldValues) {

                    if (newValues) {
                        var start = newValues[0] || Date.MIN_VALUE;
                        var end = newValues[1] || Date.MAX_VALUE;
                        if (start && end) {
                            var d1 = new Date(start);
                            var d2 = new Date(end);
                            ctrl.search({
                                after: d1.getTime(),
                                before: d2.getTime()
                            }, 'modifiedOn');
                        }
                    }

                }
            );
        }
    };
}
]);
moldovans commented 6 years ago

I observed that the pagination is gone when I refresh the page because the saveCopy the first time collection is loaded in SmartTable table controller is empty.

The filter/search keyword is perserved, but the pagination is gone.

  function StTableController($scope, $parse, $filter, $attrs) {
    var propertyName = $attrs.stTable;
    var displayGetter = $parse(propertyName);
    var displaySetter = displayGetter.assign;
    var safeGetter;
    var orderBy = $filter('orderBy');
    var filter = $filter('filter');
    var safeCopy = copyRefs(displayGetter($scope)); // empty here, so the pagination reset to '0'
MrWook commented 6 years ago

Hello @serhioV,

i don't know what you are doing wrong, but in the example it will also save the pagination. If i should help you more i suggest that you create a plunk with your problem.

moldovans commented 6 years ago

Hi @MrWook thanks for your reply.

Here is my plunk

When refreshing image

the pagintion is reset to the first page,

and another * problem is that the "data" filter also is not remembered.

MrWook commented 6 years ago

Hello @serhioV,

this took me a while to figure out the problem..... Since version 1.2.7 the Persist table state example isn't working anymore because inside the pipe function the pagination start will be set.

For async data it will work when you delete ctrl.pipe(); inside the stPersist directive but this will destroy the Persist table state for sync data.

And last but not least there is a lot of thing that you can improve in your code. The pipe function from smartTable is triggered 6 times in your example and because of that the workaround that i mentioned won't work.

You are mixing Angular coding styles. When you use the "Ctrl AS tc" stop using the $scope inside the controller and only use the object notation within the html.

But the most important thing is try not to use ngInit. It's even written in the angular documentation. The things you want to "init" aren't necessary for it. You can just define it inside the controller.

moldovans commented 6 years ago

Hello @MrWook and thanks for trying to help ! )

I use ngInitbecause I need to pass some data from the ServerSide code (ASP.NET) to Angular Controller, so I didn't find another way to do it.

I commented in the plunk the ctrl.pipe(), but this didn't help...

I observed that the problem is that the safeCopy is empty the first time (after the page reload) it passes to the Controller in my case, and this is why the pagination is reset.

MrWook commented 6 years ago

Hello @serhioV,

if you want to refere to the documentation with

injecting data via server side scripting.

This is something like this with PHP: <div ng-init="data=<?php echo $some_data;?>"></div>

So it is literally injecting some data from php into the HTML inside the ng-init. But if you can use $http requests you don't need ng-init. This can be done in the controller.

Like i said your code triggers the pipe() function 6 times it should only trigger it once. the are reseting the pagination at some point.

moldovans commented 6 years ago

actually my ASP.NET code is like this image

But even if it triggers it 6 times... is the very first time of "pipe" that the pagination is reset, because when the SmartTable controller is loaded, the safeCopy appears to be empty, but it should not be...:

image

MrWook commented 6 years ago

Then i didn't said anything sry.

Did you initialized the variables for the table, displayed and tc.table.records ?

moldovans commented 6 years ago

yes, but I suppose the controller is loaded first, only then the function go() is thiggered, and then I asynchronoysly load the records, as in the plunk image

with the Promise.resolve(myRecords) I simulate the $http.get('/records') Here I load all the all.records, and then replace the theme, relevance etc ids by respective textual values...

finally, the resulting table.records serves as source for the SmartTable, but is too late, the controller already reset the safeSoucre and then pipe() reset the pagination.

MrWook commented 6 years ago

I did't meant the controller.

Are those two variables displayed and tc.table.records initialized as an array before the data is loaded asynchronoysly ?

moldovans commented 6 years ago

in the plunk I provided tc.table.records where not initialized as an empty array. I added this but this didn't fix the problelm. The displayed was initialized, now the plunk code looks like: image

moldovans commented 6 years ago

I even changed the $scope usage, you said do not mix, I changed like this

image

and then changed

app.controller("tablesController", ["$scope", "$http", "$q", function ($scope, $http, $q) {
    var tc = this;
    tc.all = {};
    tc.table = {};
    tc.table.records = [];
    tc.table.displayed = [];

but all that didn't help to keep the current page to be reloaded correctly. I am on the plunker chat, if needed ;)

MrWook commented 6 years ago

Okey but still your table triggeres the pipe function 6 time and that will overwrite the tablestate. If you fix that it should work. Another option would to create your own pipe function and try handle the tablestate on your own.

I can't debug your whole code that is something that you need to do on your own. After all SmartTable is working like it should be.

YussufElarif commented 6 years ago

The solution I came up with. It works for me haha.

I've added var initialLoad because it seems that smart-table overrides pagination.start on initial load even when disabling ctrl.pipe();

So i've decided to put the the intial storage state loader into a method called getSavedState and when smart-table initialises, it will trigger the watch and that's when we call the getSavedState method to extend ctrl.tableState() but only if it is the initial load.

.directive('stPersist', function () {
            return {
                require: '^stTable',
                link: function (scope, element, attr, ctrl) {
                    var nameSpace = attr.stPersist;
                    var initialLoad = true;

                    // save the table state every time it changes
                    scope.$watch(function () {
                        return ctrl.tableState();
                    }, function (newValue, oldValue) {
                        if (newValue !== oldValue) {
                            if (initialLoad) {
                                getSavedState();
                                initialLoad = false;
                                return;
                            }

                            localStorage.setItem(nameSpace, JSON.stringify(newValue));
                        }
                    }, true);

                    function getSavedState() {
                        // fetch the table state when the directive is loaded
                        if (localStorage.getItem(nameSpace)) {
                            var savedState = JSON.parse(localStorage.getItem(nameSpace));
                            var tableState = ctrl.tableState();

                            angular.extend(tableState, savedState);
                            ctrl.pipe();
                        }
                    }
                }
            };
        });
mjumelet commented 5 years ago

For anyone running into this issue, the start page is set to 0 when something is changed, if you do some async loading in your controller, it will always set to 0

I changed

$scope.$watch(function() {
    var safeSrc = safeGetter($scope);
    return safeSrc ? safeSrc.length : 0;
}, function(newValue, oldValue) {
    if (newValue !== safeCopy.length) {
        updateSafeCopy();
    }
});
$scope.$watch(function() {
    return safeGetter($scope);
}, function(newValue, oldValue) {
    if (newValue !== oldValue) {
        // tableState.pagination.start = 0;
        updateSafeCopy();
    }
});

It will prevent setting the page to 0 after an async update.