Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bootstrap Maxlength Js Bundle Laravel Package

stinger/bootstrap-maxlength-js-bundle

A Laravel bundle that packages the bootstrap-maxlength plugin with ready-to-use assets, so you can add live character counters and input length limits to Bootstrap forms without pulling in npm or managing vendor files manually.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is a thin Symfony2 asset wrapper around the bootstrap-maxlength jQuery plugin (the character counter that renders next to Bootstrap inputs). It ships no PHP logic beyond bundle registration — treat it as "vendor the JS via Composer" rather than as a framework feature.

Minimal steps

  1. Install (Composer, PHP 5.x/Symfony 2.x era):
    composer require stinger/bootstrap-maxlength-js-bundle
    
  2. Register the bundle in app/AppKernel.php. The class lives at the package root; conventionally:
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Stinger\BootstrapMaxLengthBundle\StingerBootstrapMaxLengthBundle(),
        );
    }
    
    Verify the exact class name first — this package has no docs worth trusting:
    ls vendor/stinger/bootstrap-maxlength-js-bundle/
    cat vendor/stinger/bootstrap-maxlength-js-bundle/*Bundle.php
    
  3. Publish the assets so the JS is reachable from web/:
    php app/console assets:install web --symlink
    
  4. Include it in your base layout after jQuery and Bootstrap:
    <script src="{{ asset('bundles/stingerbootstrapmaxlength/js/bootstrap-maxlength.min.js') }}"></script>
    
    Confirm the real path with find vendor/stinger/bootstrap-maxlength-js-bundle/Resources -name '*.js' rather than copying the line above blindly.
  5. First use case — a contact-form message field:
    {{ form_row(form.message, {'attr': {'maxlength': 140, 'data-maxlength-counter': 'true'}}) }}
    
    $(function () {
        $('[data-maxlength-counter]').maxlength({
            alwaysShow: true,
            threshold: 20,
            warningClass: 'label label-success',
            limitReachedClass: 'label label-warning'
        });
    });
    

Where to look first

  • Resources/public/js/ — the vendored plugin, and its version (check the banner comment; it dates the JS, not the bundle).
  • The bundle class itself — if it is ~10 lines with an empty load()/no extension, that confirms there is nothing else to learn.
  • composer.json of the package — whether it declares symfony/framework-bundle constraints at all.

Implementation Patterns

Global, delegated initialization (the default pattern). One script in your base layout, initialized from data attributes rather than hardcoded selectors. This survives AJAX, dynamically added collection rows, and per-form variations:

$(document).on('focus', '[maxlength][data-counter]', function () {
    var $el = $(this);
    if ($el.data('maxlength-initialized')) { return; }
    $el.data('maxlength-initialized', true).maxlength({
        alwaysShow: $el.data('counter-always') === true,
        threshold: $el.data('counter-threshold') || 10,
        warningClass: 'text-warning',
        limitReachedClass: 'text-danger'
    });
});

Note the focus trigger rather than ready — the plugin needs the element to be laid out to measure placement correctly, and in modals/hidden tabs it will mis-position if initialized while display:none.

Symfony form integration. Keep the limit in one place — the form type — and let the attribute drive both HTML5 validation and the counter:

$builder->add('message', TextareaType::class, [
    'attr' => [
        'maxlength'  => 140,
        'data-counter' => true,
    ],
    'constraints' => [new Assert\Length(['max' => 140])],
]);

A small form theme (form_theme.html.twig or form_div_layout.html.twig override) that auto-adds data-counter to any widget with maxlength is the highest-leverage move — it removes the per-field attr noise across an entire app:

{% block form_widget_simple %}
    {% set attr = attr|merge({'data-counter': 'true'}) %}
    {{ parent() }}
{% endblock %}

Pair with Assetic / javascripts block if the project already uses it, so the plugin gets minified and versioned with the rest of your JS instead of a bare <script> tag. Otherwise just asset() it — Assetic is overkill for one file.

AJAX and collection forms. Dynamic rows are the main real-world need. Initialize on collection:entry:added / your ajaxComplete handler, or use the delegated focus pattern above and skip the bookkeeping entirely.

Server/client parity. The maxlength attribute is advisory; the Length constraint is the contract. Keep them generated from the same constant or form option so a limit change can't drift.

Gotchas and Tips

  • It is abandoned. Last release 2015-07-27, zero stars, zero dependents. There will be no fix for jQuery 3 deprecations, Bootstrap 4/5 class changes (.label → .badge), or Symfony 4+ bundle registration (config/bundles.php). Budget for forking or replacing it with a plain npm/Webpack Encore dependency — for a single jQuery file, the bundle buys you almost nothing over cp node_modules/bootstrap-maxlength/src/bootstrap-maxlength.js assets/js/.
  • assets:install is not optional. A 404 on the script is the #1 "bundle doesn't work" symptom. On newer Symfony without assets:install, symlink manually into public/bundles/.
  • Verify the plugin actually loaded before debugging options. In the console: typeof $.fn.maxlength must be 'function'. If it's undefined, it's an include-order or path problem, not a config problem.
  • maxlength vs. the counter are subtly at odds. Native maxlength truncates input, so the "limit reached" red state only fires at exactly the limit, never beyond — that's expected, not a bug. Don't strip the attribute to get the red state; you'd lose the no-JS fallback and native validation. If you want soft limits, drop maxlength and enforce Length server-side only, but then the plugin has no limit to read and you must set the attribute before init.
  • UTF-8 / emoji counting. Default utf8: true counts code points, while PHP's Assert\Length counts via mb_strlen. They mostly agree, but surrogate pairs and combining characters will produce off-by-N mismatches between the badge and your validation error. Test with emoji if your users type them.
  • Placement inside input groups, modals, and position: relative parents. Wrong placement is almost always a container issue. Try appendToParent: true, or pass an explicit placement: 'top'/'bottom', and re-initialize after any animation/modals finished opening.
  • Hidden tab panes. Initializing while hidden yields a zero-width input and a mis-positioned counter. Initialize on shown.bs.tab or lazily on first focus.
  • Re-initialization leaks counters. Calling .maxlength() twice on the same element appends a second badge. The data-*-initialized guard above is the cheap fix; .maxlength('destroy') style teardown is unreliable in the vendored version — verify before relying on it.
  • Extension points are effectively zero. There's no DI configuration, no Twig function, no form type in this package. If you need options configurable per-project, the correct move is a one-file JS wrapper (maxlength-init.js) in your app bundle that reads data-* attributes — not a PR to a 2015 bundle nobody maintains.
  • Pin the version in composer.json and commit composer.lock. With no releases since 2015 there is no security maintenance; you are accepting the vendored JS as-is.
  • On Symfony 4+, skip the bundle entirely: it was written for AppKernel and asset publishing that no longer exists in that shape. Copy the JS into assets/ and add it to your Encore entry — same result, no dead abstraction.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin