Encrypt

ofcourse this will not work,

<li class="{{ Request::is('home') ? 'class="active"' : '' }}"><a href="{{URL::to('clients')}}">Home</a></li>

should be

<li class="{{ Request::is('home') ? 'active' : '' }}"><a href="{{URL::to('home')}}">Home</a></li>

see the difference?

CODEheures

My solution with named routes (Test on Laravel 5.3):

1°) Get the current route name

<?php $routeName =  \Illuminate\Support\Facades\Route::getFacadeRoot()->current()->getName() ?>

2°) Test if the current route name is same as href route name

<a href="{{ route('home') }}" class="{{ $routeName == 'home' ? 'active' : '' }}">Home</a>
Devon
Devon
1 year ago (56,090 XP)

Like @seomike, I prefer using a view composer than anything else. However, I don't like hard-coding that logic into a view composer. Therefore, I opted to take a bit of a hybrid approach. What I did was:

  1. Create a ViewComposerServiceProvider as explained in the 5.3 docs on View Composers.
  2. Update the app\config.php to load the new service provider.
  3. Create an ActiveLinkComposer, as illustrated in the PHP snippet below.
  4. Configure my ViewComposerServiceProvider to load the ActiveLinkComposer
  5. Use the activeRoute and activePath methods in my views, as illustrated in the blade snippet below.

Active Link Composer

<?php

namespace App\Services\ViewComposers;

use Illuminate\View\View;
use Illuminate\Support\Str;
use Illuminate\Http\Request;

class ActiveLinkComposer
{
    /**
     * @var \Illuminate\Http\Request
     */
    private $request;

    /**
     * @var string
     */
    private $activeClass = 'active';

    /**
     * @var string
     */
    private $template = ' class="{classes}"';

    /**
     * Create a new profile composer.
     *
     * @param \Illuminate\Http\Request $request
     * @return void
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    /**
     * Bind data to the view.
     *
     * @param  View  $view
     * @return void
     */
    public function compose(View $view)
    {
        $view->with('link', $this);
    }

    /**
     * Determine if a link should be marked as active based on the provided route(s)
     * and return the class attribute. Optionally, you can override the template
     * to only return the class list or individual active state for the link.
     *
     * @param $routes string|array A single or list of routes to match against.
     * @param string $classes string A string of classes to be returned with the state.
     * @param null $activeClass string A string to override the $this->activeClass.
     * @param null $template string A string to override $this->template
     * @return string
     */
    public function activeRoute($routes, $classes = '', $activeClass = null, $template = null)
    {
        return $this->buildClassList(
            $routes, $this->request->route()->getName(), $classes, $activeClass, $template
        );
    }

    /**
     * Determine if a link should be marked as active based on the provided path(s)
     * and return the class attribute. Optionally, you can override the template
     * to only return the class list or individual active state for the link.
     *
     * @param $paths string|array A single or list of paths to match against.
     * @param string $classes string A string of classes to be returned with the state.
     * @param null $activeClass string A string to override the $this->activeClass.
     * @param null $template string A string to override $this->template
     * @return string
     */
    public function activePath($paths, $classes = '', $activeClass = null, $template = null)
    {
        return $this->buildClassList(
            $paths, $this->request->decodedPath(), $classes, $activeClass, $template
        );
    }

    /**
     * Determine if a link should be marked as active based on provided patterns(s)
     * and return the class attribute. Optionally, you can override the template
     * to only return the class list or individual active state for the link.
     *
     * @param $patterns string|array A single or list of patterns to match against.
     * @param $value string The string to match the patterns against.
     * @param string $classes string A string of classes to be returned with the state.
     * @param null $activeClass string A string to override the $this->activeClass.
     * @param null $template string A string to override $this->template
     * @return string
     */
    private function buildClassList($patterns, $value, $classes, $activeClass, $template)
    {
        if ($this->matches($patterns, $value)) {
            $classes = trim(implode(' ', [$classes, $activeClass ?: $this->activeClass]));
        }

        return str_replace('{classes}', $classes, $template ?: $this->template);
    }

    /**
     * Determine if any of the provided patterns match the given value.
     *
     * @param $patterns string|array A single or list of patterns to match against.
     * @param $value string The string to match the patterns against.
     * @return bool
     */
    private function matches($patterns, $value)
    {
        foreach ((array) $patterns as $pattern) {
            if (Str::is($pattern, $value)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Set the active class to be used on future calls.
     *
     * @param $activeClass
     * @return $this
     */
    public function class($activeClass)
    {
        $this->activeClass = $activeClass;

        return $this;
    }

    /**
     * Set the template to be used on future calls.
     *
     * @param $activeTemplate
     * @return $this
     */
    public function template($activeTemplate)
    {
        $this->template = $activeTemplate;

        return $this;
    }
}

Blade Template

(Do note that they should be used in {!! !!} tags to avoid being escaped when in HTML. All examples assume I'm on setup/requirements and it's named setup.requirements)

{{-- Single path wildcard, matches. Returns: class="active" --}}
{{ $link->activePath('setup/*') }}<br>

{{-- Single path, matches. Includes other classes. Returns: class="link-item active" --}}
{{ $link->activePath('setup/requirements', 'link-item') }}<br>

{{-- Multiple paths, matches. Overrides active class. Returns: class="link-item active-item" --}}
{{ $link->activePath(['setup/*', 'mismatch/path'], 'link-item', 'active-item') }}<br>

{{--Single route wildcard, matches. Overrides template. Returns: link-item active--}}
{{ $link->activeRoute('*.requirements', 'mismatch.route', 'link-item', null, '{classes}') }}<br>

{{-- Single route, matches. Sets new default active class and template. Returns: data-class="link-item activated" --}}
{{ $link->class('activated')->template(' data-class="{classes}"')->activeRoute('setup.*', 'link-item') }}<br>

{{-- Multiple routes, matches. Uses above template and class. Returns: data-class="link-item active-item" --}}
{{ $link->activeRoute(['setup.requirements', 'mismatch.route'], 'link-item', 'active-item') }}<br>

{{-- Multiple routes, matches. Uses above template and class. Returns: data-class="link-item active-item" --}}
{{ $link->activeRoute(['setup.requirements', 'mismatch.route'], 'link-item', 'active-item') }}<br>

{{-- Multiple routes, doesn't match. Returns: ng-class="link-item" --}}
{{ $link->activePath(['requirements', 'mismatch.route'], 'link-item', 'active-item', ' ng-class="{classes}"') }}<br>

So basically it's pretty short and sweet but yet is flexible for those edge cases where you just need a bit more control. You can also wrap your links in a separate template and @include('partials.link', ['var' => $val, …])them with variables passed as additional arguments to try and keep your templates a bit cleaner.

Awks
Awks
1 year ago (12,890 XP)

Hello,

My turn, what i'm doing to achieve this :

My routing names convention

    admin.dashboard
    admin.portfolio.index
    admin.portfolio.create
    admin.portfolio.store
    admin.portfolio.show

        ...

My view

<ul class="nav nav-tabs">
    <li role="presentation" class="{{ strpos(Route::currentRouteName(), 'admin.dashboard') !== false ? 'active' : null }}">
        <a href="{{ route('admin.dashboard.index') }}"><i class="fa fa-pie-chart"></i> Dashboard</a>
    </li>
    <li role="presentation" class="{{ strpos(Route::currentRouteName(), 'admin.portfolio') !== false ? 'active' : null }}">
        <a href="{{ route('admin.portfolio.index') }}"><i class="fa fa-archive"></i> Portfolio</a>
    </li>

    ...

</ul>

It will set the active class for the parent route (admin.portfolio) and his childrens (admin.portfolio.*).

What do you think ?

Enjoy!

consil
consil
1 year ago (14,730 XP)

@awks If you specifically check for equality to zero, then you can make sure the route starts with the relevant string:

{{ strpos(Route::currentRouteName(), 'admin.dashboard') === 0 ? 'active' : '' }

That will match admin.dashboard.bar but not foo.admin.dashboard.bar. That may or may not be what you want, but it's something you can do.

I would also return and empty string instead of null, then there is no type-casting needed, since {{ ... }} will be expecting a string to be returned, or will need to cast whatever is returned to a string. Just start with a string and get it over with.

Awks
Awks
1 year ago (12,890 XP)

@consil Thanks for your opinion, i'm using it now :)

Devon
Devon
1 year ago (56,090 XP)

@Awks, @consil You can also take advantage of Str::is() in place of strpos() to add support for pattern matching.

https://github.com/laravel/framework/blob/5.3/src/Illuminate/Support/Str.php#L119

Awks
Awks
1 year ago (12,890 XP)

@Consil Oh nice, thanks!

mustard
mustard
1 year ago (16,120 XP)

There is a very simple way to sort this out in Laravel 5. In your layout/master file, add the following function:

<?php 
  function current_page($uri = "/") { 
    return strstr(request()->path(), $uri); 
  } 
?>

Then in your menu, just test against this logic and apply the "active" class:

 <li class="nav-item {{current_page('about') ? 'active' : '' }}">
       <a class="nav-link" href="/about">About</a>
 </li>

Simple!

consil
consil
1 year ago (14,730 XP)

@mustard Yes, you can look at the URL path, but you must stay aware of what the implications of that are.

Paths are generally (and should always IMO) be defined in the routes.php map. Outside of that script, I don't believe views or controllers should be aware of what those paths are. Paths can easily change as an application develops and grows. The names of the paths however, can remain fixed since they define a business function.

But horses for courses - it's not wrong, just understand what its advantages and limitations are.

stevenbauman

For anyone viewing this in the future, laravel supports wildcards in the Route::is() method. There's no reason to use any complicated regular expressions or custom classes:

<a href="/accounts" class="{{ Route::is('accounts.*') ? 'active' : null }}">Accounts</a>

This link will have an active class when viewing any routes that start with accounts..

You can also use prefix wildcards, so you can check if the current route contains any name at all:

<a href="/accounts" class="{{ Route::is('*.accounts.*') ? 'active' : null }}">Accounts</a>

The above link will be active if the users current route contains .accounts..

luddinus

This is what I use (keep it simple)

app/helpers.php

function active_if($routeNames)
{
    $routeNames = (array) $routeNames;

    foreach ($routeNames as $routeName) {
        if (Route::is($routeName)) {
            return ' class="active"';
        }
    }

    return '';
}
    

some view

<li{!! active_if('login') !!}><a href="{{ route('login') }}">Login</a></li>
consil
consil
1 year ago (14,730 XP)

@stevenbauman when did Route::is() appear in Laravel? I can't see it in the latest on github, but I may be looking in the wrong place.

Request::is() has been a thing for a long time, and takes wildcards, but works on URL paths, not route names.

rulatir

All these solutions are unsatisfactory. The real problem here is as follws:

GIVEN: a URI

DETERMINE WHETHER: if the given URI were requested, would the request hit the same route/controller/action triplet as the current request and would the action also receive the same parameter values?

A satisfactory solution would be one that solves this problem without ANY manual hints like assigning names to routes. If I have a route that parses the page slug with a wildcard, then naming the route is not enough. I don't just wan't to know that I have hit the page route, I also want to know if I hit the same specific page the given URI would hit. Route naming doesn't solve that.

kristjan.reinsberg

why not just use javascript? Get current URL... window.location.protocol

Please sign in or create an account to participate in this conversation.