ColorlibHQ / AdminLTE

AdminLTE - Free admin dashboard template based on Bootstrap 5
https://adminlte.io
MIT License
43.76k stars 18.17k forks source link

How to remember the toggle state of the sidebar menu? #1240

Closed Smaele closed 7 years ago

Smaele commented 7 years ago

I am using AdminLTE in combination with Codeigniter3. Therefore a lot of HTML is generated on the back-end of my application. I'd like to find a way to let the back-end know the toggle state (collapsed or open) of the sidebar menu so that I can generate the appropriate HTML for a page.

I tried to use the href in: <a href="<?= site_url("site/toggle");?>" class="sidebar-toggle" data-toggle="offcanvas" role="button"> but that doesn't work because the href is not used.

Does anyone know a way to achieve this?

REJack commented 7 years ago

Do you mean something like a remember sidebar toggle state on page change? If yes, there are 2 ways

  1. jQuery AJAX on sidebar toggle change to update a CI3 Controller for db storage or cookie creation + HTML rendering over PHP
  2. A jQuery AJAX on sidebar toggle change to update/write a cookie thats written by pure JS and switchs toggle state on page(dom) ready after a page change

I hope this helps, I'll create later a PR for this enhancement (as a config option in app.js).

REJack commented 7 years ago

Look at my fork and try it 😃 (its disabled by default) https://github.com/REJack/AdminLTE/tree/master/dist/js Tested with FF 49.0.2

I'll test it with other browsers and then i create a PR for it.

REJack commented 7 years ago

Here the script as external plugin. I have read, that @almasaeed2010 doesn't accept new features for v2 since he started with v3. almasaeed2010's Comment

This script/plugin needs to be loaded after app.js/app.min.js.

$.AdminLTESidebarTweak = {};

$.AdminLTESidebarTweak.options = {
    EnableRemember: true,
    NoTransitionAfterReload: false
    //Removes the transition after page reload.
};

$(function () {
    "use strict";

    $("body").on("collapsed.pushMenu", function(){
        if($.AdminLTESidebarTweak.options.EnableRemember){
            var toggleState = 'opened';
            if($("body").hasClass('sidebar-collapse')){
                toggleState = 'closed';
            }
            document.cookie = "toggleState="+toggleState;
        } 
    });

    if($.AdminLTESidebarTweak.options.EnableRemember){
        var re = new RegExp('toggleState' + "=([^;]+)");
        var value = re.exec(document.cookie);
        var toggleState = (value != null) ? unescape(value[1]) : null;
        if(toggleState == 'closed'){
            if($.AdminLTESidebarTweak.options.NoTransitionAfterReload){
                $("body").addClass('sidebar-collapse hold-transition').delay(100).queue(function(){
                    $(this).removeClass('hold-transition'); 
                });
            }else{
                $("body").addClass('sidebar-collapse');
            }
        }
    } 
});
Smaele commented 7 years ago

Hello REJAck,

Thank you very much for your extensive response. I'll get going at your response asap. Will let you know if I can get it working.

Greetings.

Smaele commented 7 years ago

I have tried the above solution. It works on closing (collapsing) the menu. But when I reopen the menu it doesn't seem to respond, it stays closed (collapsed).

I have two more questions: 1) does the javascript also need to respond to the expanded.pushMenu trigger? and how. 2) what would a AJAX call to CI3 Controller function look like?

Thanks in advance for your help.

REJack commented 7 years ago

I've fixed the external Script, with this is your 1. question answered :) i missed this on exporting it from my app.js

to the 2. question, thats a bit complicated, that would need a new CI Controller with 3 functions (2 create cookie + get created cookie/state) and you would need to switch in the JS the cookie based functions with $.post or $.get to get/send data to the CI Controller.

$.AdminLTESidebarTweak = {};

$.AdminLTESidebarTweak.options = {
    EnableRemember: true,
    NoTransitionAfterReload: false
    //Removes the transition after page reload.
};

$(function () {
    "use strict";

    $("body").on("collapsed.pushMenu", function(){
        if($.AdminLTESidebarTweak.options.EnableRemember){
            document.cookie = "toggleState=closed";
        } 
    });
    $("body").on("expanded.pushMenu", function(){
        if($.AdminLTESidebarTweak.options.EnableRemember){
            document.cookie = "toggleState=opened";
        } 
    });

    if($.AdminLTESidebarTweak.options.EnableRemember){
        var re = new RegExp('toggleState' + "=([^;]+)");
        var value = re.exec(document.cookie);
        var toggleState = (value != null) ? unescape(value[1]) : null;
        if(toggleState == 'closed'){
            if($.AdminLTESidebarTweak.options.NoTransitionAfterReload){
                $("body").addClass('sidebar-collapse hold-transition').delay(100).queue(function(){
                    $(this).removeClass('hold-transition'); 
                });
            }else{
                $("body").addClass('sidebar-collapse');
            }
        }
    } 
});
Smaele commented 7 years ago

Hi REJack,

Thanks again, it works like a charm! I'll stick with this solution. I really appreciate your help!

kher09 commented 7 years ago

Hi REJack,

I have had the doubt to realize that the sidebar always remembers its state, and I found with your solution. I implemented it, but I had a drawback.

When you disable the transition after page reload, always show the transition, but the condition is met.

if($.AdminLTESidebarTweak.options.NoTransitionAfterReload){
                $("body").addClass('sidebar-collapse hold-transition').delay(100).queue(function(){
                    $(this).removeClass('hold-transition');
                     console.log("with transition"); 
                });
            }else{
                $("body").addClass('sidebar-collapse');
                console.log("without transition");
            }

Have you reviewed this?

vibs2006 commented 6 years ago

@REJack rather than using cookies don't you think that using window.localStorage object would have been better?

EnnioWolsink commented 6 years ago

Thanks @REJack for your script, works nicely!

I adapted it slightly so it uses localStorage. One of the advantages of this approach is that it remembers the state for all pages, whereas the cookie would be set per path. Basically this means the toggle state was remembered on a per page basis (at least in my browser, Chrome 61 on Windows 10), which may be ideal for some uses cases but not mine.

I also tried to reproduce the bug @kher09 mentioned, without much luck. Maybe someone else can chip in there.

    $.AdminLTESidebarTweak = {};

    $.AdminLTESidebarTweak.options = {
            EnableRemember: true,
            NoTransitionAfterReload: true
            //Removes the transition after page reload.
    };

    $(function () {
        "use strict";

        $("body").on("collapsed.pushMenu", function() {
            if($.AdminLTESidebarTweak.options.EnableRemember) {
                localStorage.setItem("toggleState", "closed");
            }
        });

        $("body").on("expanded.pushMenu", function() {
                if($.AdminLTESidebarTweak.options.EnableRemember) {
                    localStorage.setItem("toggleState", "opened");
                } 
        });

        if ($.AdminLTESidebarTweak.options.EnableRemember) {
            var toggleState = localStorage.getItem("toggleState");
            if (toggleState == 'closed'){
                if ($.AdminLTESidebarTweak.options.NoTransitionAfterReload) {
                    $("body").addClass('sidebar-collapse hold-transition').delay(100).queue(function() {
                        $(this).removeClass('hold-transition');
                    });
                } else {
                    $("body").addClass('sidebar-collapse');
                }
            }
        }
    });
Smaele commented 6 years ago

Hi All, I updated my script to the above one and everything still works fine on every page. There is (and always has been) one side effect and that is that when I show a new page it takes a little time for the sidebar menu to be small again. You see the menu width jump. Has anyone else this problem and an idea how to solve this?

gimler commented 6 years ago

@almasaeed2010 can we bring it into the core?

curtchan commented 6 years ago

An example of making it work without 'side effect' of sidebar being 'closed' shortly after page reload (if was closed before page reload). Note that example is using TWIG engine, but should be easibly adaptable to PHP if such need occurs.

Script tag inserted after app.min.js

    <script>
        $.AdminLTESidebarTweak = {};

        $.AdminLTESidebarTweak.options = {
            EnableRemember: true,
            NoTransitionAfterReload: false
            //Removes the transition after page reload.
        };

        $(function () {
            "use strict";

            $("body").on("collapsed.pushMenu", function(){
                if($.AdminLTESidebarTweak.options.EnableRemember){
                    document.cookie = "toggleState=closed";
                }
            }).on("expanded.pushMenu", function(){
                if($.AdminLTESidebarTweak.options.EnableRemember){
                    document.cookie = "toggleState=opened";
                }
            });
        });
    </script>

Body classes (on my example (TWIG SYNTAX), but should be easily adaptable to your case).

{% set bodyClasses = bodyClasses|default([])|merge([
    "hold-transition",
    "sidebar-mini",
    app.request.cookies.get("toggleState")|default('opened') == 'closed' ? "sidebar-collapse" : "",
]) %}

{% block bodyClasses %} class="{{ bodyClasses|join(' ') }}"{% endblock %}
asirihewage commented 5 years ago

simply use cookies

Grawl commented 5 years ago

Here's my try on Laravel Blade with JS from @curtchan' answer

@php
    $bodyClassList = [
        'sidebar-mini',
        'hold-transition',
        $_COOKIE['toggleState'] === 'closed' ? 'sidebar-collapse' : '',
    ];
@endphp

<body class="{{ implode(' ', $bodyClassList) }}">
rodruiz commented 5 years ago

Hi, do not use cookies. It's easier to use LocalStorage. I did it here.

This works perfectly for me.

Thanks.

Grawl commented 5 years ago

LocalStorage is available only from JS but not from backend so this is not possible to render aside closed in HTML from server to not get opened aside on load and closed on JS ready.

curtchan commented 5 years ago

LocalStorage is available only from JS but not from backend so this is not possible to render aside closed in HTML from server to not get opened aside on load and closed on JS ready.

That's why i used cookies implementation - you dont get that annoying closing/opening animation whenever page is loaded. Further - i managed to implement it into bootstrap's tab - works the same way and without annoying js actions during page load. 💯

ardziej commented 5 years ago

This is my solution:

JS

$.AdminLTESidebarTweak = {};

$.AdminLTESidebarTweak.options = {
    EnableRemember: true,
    NoTransitionAfterReload: false
    //Removes the transition after page reload.
};

$(function () {
    "use strict";

    function setCookie(value) {
        let name = 'toggleState';
        let days = 365;
        let d = new Date;
        d.setTime(d.getTime() + 24 * 60 * 60 * 1000 * days);
        document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
    }

    $("body").on("collapsed.pushMenu", function () {
        if ($.AdminLTESidebarTweak.options.EnableRemember) {
            setCookie('closed');
        }
    }).on("expanded.pushMenu", function () {
        if ($.AdminLTESidebarTweak.options.EnableRemember) {
            setCookie('opened');
        }
    });
});

PHP (/resources/views/vendor/adminlte/master.blade.php)

<body class="hold-transition @yield('body_class') @if (Cookie::get('toggleState') === 'closed') {{ 'sidebar-collapse' }} @endif">

Laravel Cookies Middleware

<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        'toggleState',
    ];
}
DEVRam86 commented 4 years ago

I used Rejack solution. It works but slight change in my requirement. Can any one help me please.

I am using AdminLTE template. I am having menu with child menus. I am redirecting different pages using image button. Now my menu collapses to original state.

If I am navigating in menu alone it is working fine, but if I click an image button in any one of the menu item's web form, Menu is not active, it loses it active state.

Second Image

Highlighted in righttop corner where I am Adding,Editing & Deleting pages are called. Now My requirement is Hardware Menu should be active until I redirects to any other menu.

Javascript code I tried for Menu, It works for Menu items.

var url = window.location; $('ul.sidebar-menu a').filter(function () { return this.href != url; }).parent().removeClass('active');

$('ul.sidebar-menu a').filter(function () { return this.href == url; }).parent().addClass('active');

// for treeview $('ul.treeview-menu a').filter(function () { return this.href == url; }).parentsUntil(".sidebar-menu > .treeview-menu").addClass('active'); Kindly help anyone. Its Urgent. Thanks``

Grawl commented 4 years ago

you have to post your question on StackOverflow

DEVRam86 commented 4 years ago

you have to post your question on StackOverflow

Already posted, there is no reply. Thats why. https://stackoverflow.com/questions/57357857/highlight-masterpage-menu-to-active-state

szabizs commented 4 years ago

My solution is a bit smaller, but it does the trick.

/** Set local storage if expaneded **/
$('body').bind('expanded.pushMenu', function(e){
    $('.hide-collapse-form').show();
    localStorage.setItem("comercial_sidebar","expanded");
});

/** Set local storage if collapsed **/
$('body').bind('collapsed.pushMenu', function(e){
    $('.hide-collapse-form').hide();
    localStorage.setItem("comercial_sidebar","collapsed");
});

/** Retrieve local storage value and change class of body **/
if(localStorage.getItem("comercial_sidebar") !== null) {
    if(localStorage.getItem("comercial_sidebar") === 'collapsed') {
        $('.hide-collapse-form').hide();
        $("body").addClass('sidebar-collapse');
    }
}
danny007in commented 4 years ago
transition 

What for latest adminLTE 3