#[Json]
Return data directly to the client without re-rendering the component.
Basic Usage
Mark a method with #[Json] to return data directly to JavaScript. The method's return value becomes available as a Promise result.
use LiVue\Attributes\Json;
class ProductSearch extends Component
{
public string $query = '';
#[Json]
public function search(string $term): array
{
return Product::where('name', 'like', "%{$term}%")
->take(10)
->get(['id', 'name', 'price'])
->toArray();
}
#[Json]
public function getStats(): array
{
return [
'total' => Product::count(),
'active' => Product::where('active', true)->count(),
];
}
}
Using in Templates
The livue.call() method returns a Promise when calling a #[Json] method.
<div>
<input v-model="query" placeholder="Search...">
<button @click="livue.call('search', [query]).then(data => livue.set('results', data))">
Search
</button>
<ul>
<li v-for="item in results">
{{ item.name }} - ${{ item.price }}
</li>
</ul>
</div>
Using with @script
Combine with @script for cleaner async handling:
<button @click="handleSearch">Search</button>
@script
const handleSearch = async () => {
const results = await livue.call('search', [state.query]);
state.results = results;
// Do something with the results
if (results.length === 0) {
alert('No results found');
}
};
@endscript
#[Json] vs #[Renderless]
Both skip re-rendering, but they serve different purposes:
| Attribute | State Updated | Returns Data | Use Case |
|---|---|---|---|
#[Renderless] |
Yes | No | Actions that change state |
#[Json] |
No | Yes | Fetching data |
Use Cases
Autocomplete
Fetch suggestions as the user types without full re-renders.
Infinite Scroll
Load more items and append them to an existing list.
Form Validation
Check uniqueness or validate data server-side.
Data Export
Fetch data for client-side CSV generation or processing.
Error Handling
Handle errors using standard Promise catch or try/catch with async/await:
// Promise .catch()
livue.call('search', [query])
.then(data => { /* handle success */ })
.catch(err => { console.error('Search failed:', err); });
// async/await
try {
const data = await livue.call('search', [query]);
} catch (err) {
console.error('Search failed:', err);
}