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

Nova Ajax Select Laravel Package

dillingham/nova-ajax-select

A Laravel Nova field that swaps the default select for an AJAX-powered searchable dropdown, so users can quickly find records in large related resource sets as they type. Works with belongsTo, belongsToMany, and morphTo relationships, with support for custom search columns and query constraints.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the host, then pin. This is a Nova custom field and nothing else — no Nova install, no value. Check your runtime first:

composer show laravel/nova   # major version decides everything
php -v                       # package constraints in composer.json

The last tag is 1.0.4 (Aug 2020), which predates Nova 4's Vue 3 rebuild. Treat Nova 3.x as the supported path; Nova 4/5 is a spike you validate, not an assumption. Install pinned, not floating:

composer require dillingham/nova-ajax-select:1.0.4

1.0.4 is the floor — it fixes the render/submit failure when a field can't resolve a parent() context (#21). Commit the lockfile.

Read the source before the README. There's no doc site, and issues are self-support. The package is small enough to read in an hour:

  • src/ — the field class: what make() accepts and the fluent setters (resource, label column, search columns). This is the API surface.
  • resources/js/ — the Vue component: debounce behavior, what it POSTs to Nova's search endpoint, empty/error rendering.
  • composer.json — the PHP/Laravel/Nova constraints. Confirm your versions intersect before you write a line of integration code.

First use case — one high-cardinality lookup. Pick a single low-traffic Resource whose relationship would never survive a <option> render (customers, tenants, SKUs). Swap the plain Select/BelongsTo:

use Dillingham\NovaAjaxSelect\AjaxSelect;

public function fields(Request $request)
{
    return [
        AjaxSelect::make('Customer', 'customer_id')
            ->resource('customers')   // Nova resource / endpoint handle
            ->label('name'),          // column rendered in results
        // ...rest of the form
    ];
}

Then run the smoke test explicitly: field mounts (not just "form loads"), typing returns results, existing value resolves on the detail/edit view, and the record saves. That's your 15-minute proof of concept. Everything after is authorization, scoping, and operational hardening.

Implementation Patterns

Try the native field first. Nova 4 shipped searchable selects. If built-in searchable relationships cover the case, use them — no dependency for parity. Reach for this package when the option source is genuinely remote (an API, a separate service DB), when search must be decoupled from a plain BelongsTo, or when the option set changes without a redeploy.

Keep search authorization-aware — this is the daily discipline. The field hits Nova's resource search endpoint, so your Nova resource authorization applies if you route through the resource honestly:

  • One Nova Resource per entity, with searchable() returning only columns you intend to expose.
  • Point the field at it: ->resource('customers').
  • Scope the resource's search query by tenant (global scope or a resource gate) so the endpoint physically cannot return another tenant's rows.
  • If the field exposes a query hook (->query(fn ($q) => ...) or similar), enforce scoping there, not in a patched controller. Verify the exact method name against src/ — this is precisely where your code and the package must agree.

Cap at the boundary, not in the component. Debounce is UX polish. Server-side is where the real limits live: minimum query length, hard limit(20..50), orderBy on an indexed column. Front-end speed should not become a full-table-scan vector.

Use it for freshness, not just row counts. The genuinely useful non-obvious pattern: option sets that change without a redeploy — plans, accounts synced from a CRM, feature flags. The AJAX round-trip is a free freshness guarantee versus a cached preload.

Roll out behind a per-Resource flag. Keep the previous input in the field definition behind a config/env toggle until the new field survives a couple of weeks of real admin traffic. Rollback is a flag flip, not a revert.

Instrument the endpoint from day one. Error-rate and latency logging on the search route (a Nova middleware works). The failure mode you care about — endpoint 403/500, field can't resolve a saved value, form becomes unsubmittable — is invisible without it.

Know the shape. This is a single-value typeahead. Tag-style multi-select and server-side grouping are a different build. Dependent fields are called out in the package description but the fluent surface is thin — read src/ before assuming chainable dependency wiring, and don't bend the field to fit a requirement it wasn't designed for.

Have a vendoring path ready. MIT, zero dependents, tiny surface: forking is cheap. Add a VCS repository entry with an internal tag and carry 1.0.4 as the fork baseline. Adopt-and-own, not buy-and-forget.

Gotchas and Tips

parent() bug — pin 1.0.4 and test the modal case. On some Nova configurations the field fails to resolve a parent context and breaks on render/submit at ≤ 1.0.3. 1.0.4 addresses it, but "fixed" may mean graceful degradation rather than correct behavior. Explicitly test a field rendered inside a modal or nested field group where the parent is legitimately absent.

Nova 4/5 compatibility is a gate, not a checkbox. The Vue asset can fail to mount with a silent render failure — no console errors pointing at this field. Your smoke test must assert mounting specifically; a generic "the form loads" test won't catch it. Spike this before committing the dependency.

Authorization is the failure that fails security review. An AJAX endpoint returning model records must enforce Nova resource authorization and per-user/tenant scoping. The naive integration leaks across tenants. Audit end-to-end, including the "query matches nothing" path and the "clear the field and resubmit" path.

Watch the index. LIKE '%term%' on an unindexed column is fine against your 500-row seeder and a page-lock incident on a million rows at 4pm. Verify searched columns have usable indexes; prefer prefix matching on indexed columns or full-text search. Test against a production-shaped table before rollout.

Empty and error states matter more than you think. When search returns nothing or the endpoint errors, the field must degrade to something the admin can still submit — a plain select fallback or a free-text ID input. If the field can't resolve a value on save, the whole form is stuck. Bake the fallback into the integration.

Debounce is not rate-limiting. A stale browser or determined caller can still hammer the endpoint. Put a rateLimiter on the search route — cheap insurance against a self-inflicted DoS.

Configuration quirks live in the source. Fluent setters for resource, label, and search columns are the whole API; there is no documentation site. When something behaves unexpectedly, src/ is the answer. Designate one teammate as SME and record the field's configuration (resource name, label column, scoping model) in the runbook so the next person isn't reverse-engineering a 2020 dependency at 2am.

Benchmark against native before keeping it. If you're on Nova 4 anyway, run the native searchable select on the same Resource and capture page-weight and time-to-interactive. If native hits your numbers, drop the dependency — the best outcome for an 86-star package last released in 2020 is often not needing it.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log