Skip to content

Commit 060f6f3

Browse files
committed
Handled async callbacks for LazyProp and AlwaysProp
1 parent 2048533 commit 060f6f3

File tree

14 files changed

+165
-28
lines changed

14 files changed

+165
-28
lines changed

InertiaNetCore.Demo/Controllers/HomeController.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,26 @@ public class HomeController : Controller
1010
{
1111
public IActionResult Index()
1212
{
13-
var now = DateTime.UtcNow.ToString("O").Split('T')[1].Replace(".", " ");
13+
var now = GetNow();
1414
return Inertia.Render("pages/PageIndex", new InertiaProps
1515
{
1616
["NowDirect"] = now,
1717
["NowFunc"] = () => now,
1818
["NowAlways"] = Inertia.Always(() => now),
1919
["NowLazy"] = Inertia.Lazy(() => now),
20+
["NowLazyAsync"] = Inertia.Lazy(async () =>
21+
{
22+
await Task.Delay(2000);
23+
return now;
24+
}),
2025
});
2126
}
2227

28+
private static string GetNow()
29+
{
30+
return DateTime.UtcNow.ToString("O").Split('T')[1].Replace(".", " ");
31+
}
32+
2333
[Route("about")]
2434
public IActionResult About()
2535
{
-12 Bytes
Binary file not shown.

InertiaNetCore.Demo/Frontend/Vue/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
},
1111
"dependencies": {
1212
"prettier": "^3.3.3",
13+
"tailwind-merge": "^2.5.2",
1314
"vue": "^3.4.37"
1415
},
1516
"devDependencies": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script setup lang="ts">
2+
3+
import { twMerge } from 'tailwind-merge';
4+
5+
withDefaults(defineProps<{
6+
loading?: boolean,
7+
visible?: boolean,
8+
}>(), {
9+
loading: true,
10+
visible: true,
11+
})
12+
</script>
13+
14+
<template>
15+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
16+
class="text-gray-400 dark:text-gray-600 fill-blue-600 dark:fill-blue-300"
17+
:class="[{
18+
'hidden': !visible,
19+
'animate-spin': loading,
20+
}, twMerge('size-6', $attrs.class as any)]">
21+
<path fill="currentColor" fill-rule="evenodd" d="M12 19a7 7 0 1 0 0-14a7 7 0 0 0 0 14m0 3c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10" clip-rule="evenodd" opacity="0.2" />
22+
<path fill="currentFill" d="M2 12C2 6.477 6.477 2 12 2v3a7 7 0 0 0-7 7z" />
23+
</svg>
24+
</template>

InertiaNetCore.Demo/Frontend/Vue/src/layout/AppLayout.vue

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<script setup lang="ts">
2-
import { Link, usePage } from '@inertiajs/vue3';
2+
import { Link, router, usePage } from '@inertiajs/vue3';
33
import { ref } from 'vue';
44
import ThemeSwitcher from '@/components/ThemeSwitcher.vue';
5+
import Spinner from '@/components/Spinner.vue';
6+
import { globalState, initGlobalListeners } from '@/utils/globalState';
57
68
const showMenu = ref(true);
79
const showInertiaProps = ref(true);
810
11+
initGlobalListeners();
12+
913
const page = usePage<{
1014
auth: {
1115
token: string;
@@ -24,6 +28,8 @@ const page = usePage<{
2428
</button>
2529
<Link class="rounded transition-colors duration-300" href="/">InertiaNetCore</Link>
2630

31+
<Spinner :visible="globalState.routerWaiting"/>
32+
2733
<i class="flex-1"></i>
2834
<ThemeSwitcher />
2935
<button class="hover:bg-gray-200 dark:hover:bg-gray-800 p-2 rounded transition-colors duration-300 text-xl" @click="showInertiaProps = !showInertiaProps">

InertiaNetCore.Demo/Frontend/Vue/src/pages/PageIndex.vue

+24-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script setup lang="ts">
22
import { router, usePage } from '@inertiajs/vue3';
33
import { ref, unref } from 'vue';
4+
import { globalState } from '@/utils/globalState';
5+
import Spinner from '@/components/Spinner.vue';
46
57
const page = usePage<{
68
nowDirect: string;
@@ -10,7 +12,7 @@ const page = usePage<{
1012
}>();
1113
1214
const lastProps = ref(unref(page.props));
13-
15+
const loadingNowLazyAsync = ref(false);
1416
function reloadRouter(opts?: any) {
1517
lastProps.value = unref(page.props);
1618
router.reload(opts);
@@ -25,13 +27,18 @@ function reloadRouter(opts?: any) {
2527
<pre class="text-xs bg-gray-100 dark:bg-gray-900 p-3 mb-10 rounded-md">
2628
public IActionResult Index()
2729
{
28-
var now = DateTime.UtcNow.ToString("O").Split('T')[1].Replace(".", " ");
30+
var now = GetNow();
2931
return Inertia.Render("pages/PageIndex", new InertiaProps
3032
{
3133
["NowDirect"] = now,
3234
["NowFunc"] = () => now,
3335
["NowAlways"] = Inertia.Always(() => now),
3436
["NowLazy"] = Inertia.Lazy(() => now),
37+
["NowLazyAsync"] = Inertia.Lazy(async () =>
38+
{
39+
await Task.Delay(2000);
40+
return now;
41+
}),
3542
});
3643
}</pre
3744
>
@@ -46,7 +53,7 @@ public IActionResult Index()
4653
</tr>
4754
<tr>
4855
<td class="font-bold pr-4">NowFunc</td>
49-
<td class="font-mono" style="width: 200px;">{{ page.props.nowFunc }}</td>
56+
<td class="font-mono" style="width: 200px">{{ page.props.nowFunc }}</td>
5057
<td class="font-bold pl-4" :class="lastProps.nowFunc == page.props.nowFunc ? '' : 'text-green-600'">({{ lastProps.nowFunc == page.props.nowFunc ? 'same' : 'changed' }})</td>
5158
</tr>
5259
<tr>
@@ -55,9 +62,14 @@ public IActionResult Index()
5562
<td class="font-bold pl-4" :class="lastProps.nowAlways == page.props.nowAlways ? '' : 'text-green-600'">({{ lastProps.nowAlways == page.props.nowAlways ? 'same' : 'changed' }})</td>
5663
</tr>
5764
<tr>
58-
<td class="font-bold pr-4">NowLazy</td>
59-
<td class="font-mono" style="width: 200px">{{ page.props.nowLazy }}</td>
60-
<td class="font-bold pl-4" :class="lastProps.nowLazy == page.props.nowLazy ? '' : 'text-green-600'">({{ lastProps.nowLazy == page.props.nowLazy ? 'same' : 'changed' }})</td>
65+
<td class="font-bold pr-4">NowLazy</td>
66+
<td class="font-mono" style="width: 200px">{{ page.props.nowLazy }}</td>
67+
<td class="font-bold pl-4" :class="lastProps.nowLazy == page.props.nowLazy ? '' : 'text-green-600'">({{ lastProps.nowLazy == page.props.nowLazy ? 'same' : 'changed' }})</td>
68+
</tr>
69+
<tr>
70+
<td class="font-bold pr-4">NowLazyAsync</td>
71+
<td class="font-mono" style="width: 200px">{{ page.props.nowLazyAsync }}</td>
72+
<td class="font-bold pl-4" :class="lastProps.nowLazyAsync == page.props.nowLazyAsync ? '' : 'text-green-600'">({{ lastProps.nowLazyAsync == page.props.nowLazyAsync ? 'same' : 'changed' }})</td>
6173
</tr>
6274
</tbody>
6375
</table>
@@ -68,6 +80,12 @@ public IActionResult Index()
6880
<button @click="reloadRouter({ only: ['nowFunc'] })" class="bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-700 hover:opacity-90 border px-4 rounded py-1">Reload router (only NowFunc)</button>
6981
<button @click="reloadRouter({ only: ['nowAlways'] })" class="bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-700 hover:opacity-90 border px-4 rounded py-1">Reload router (only NowAlways)</button>
7082
<button @click="reloadRouter({ only: ['nowLazy'] })" class="bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-700 hover:opacity-90 border px-4 rounded py-1">Reload router (only NowLazy)</button>
83+
<button
84+
@click="reloadRouter({ only: ['nowLazyAsync'], onBefore: () => loadingNowLazyAsync = true, onFinish: () => loadingNowLazyAsync = false })"
85+
class="bg-gray-100 dark:bg-gray-800 border-gray-300 dark:border-gray-700 hover:opacity-90 border px-4 rounded py-1 flex items-center gap-2">
86+
<Spinner :visible="loadingNowLazyAsync" class="size-4" />
87+
Reload router (only NowLazyAsync)
88+
</button>
7189
</div>
7290
</div>
7391
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { router } from '@inertiajs/vue3';
2+
import { reactive } from 'vue';
3+
4+
export const globalState = reactive({
5+
routerWaiting: false,
6+
})
7+
8+
export const initGlobalListeners = () => {
9+
router.on('before', () => {
10+
console.log('router before');
11+
globalState.routerWaiting = true;
12+
});
13+
14+
router.on('finish', () => {
15+
console.log('router finish');
16+
globalState.routerWaiting = false;
17+
});
18+
}

InertiaNetCore/Inertia.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ public static class Inertia
3131

3232
public static void Share(InertiaProps data) => _factory.Share(data);
3333

34-
public static LazyProp Lazy(Func<object?> callback) => _factory.Lazy(callback);
35-
public static AlwaysProp Always(Func<object?> callback) => _factory.Always(callback);
34+
public static LazyProp<T> Lazy<T>(Func<T?> callback) => _factory.Lazy(callback);
35+
public static LazyProp<T> Lazy<T>(Func<Task<T?>> callback) => _factory.Lazy(callback);
36+
37+
public static AlwaysProp<T> Always<T>(Func<T?> callback) => _factory.Always(callback);
38+
public static AlwaysProp<T> Always<T>(Func<Task<T?>> callback) => _factory.Always(callback);
3639
}

InertiaNetCore/Models/InertiaProps.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using InertiaNetCore.Utils;
2-
using Microsoft.AspNetCore.Http.HttpResults;
32

43
namespace InertiaNetCore.Models;
54

65
public class InertiaProps : Dictionary<string, object?>
76
{
8-
internal InertiaProps ToProcessedProps(List<string>? partials)
7+
internal async Task<InertiaProps> ToProcessedProps(List<string>? partials)
98
{
109
var props = new InertiaProps();
1110

@@ -14,18 +13,18 @@ internal InertiaProps ToProcessedProps(List<string>? partials)
1413

1514
foreach (var (key, value) in this)
1615
{
17-
if(partials is null && value is LazyProp)
16+
if(partials is null && value is ILazyProp)
1817
continue;
1918

20-
if(partials is not null && value is not AlwaysProp && !partials.Contains(key, StringComparer.InvariantCultureIgnoreCase))
19+
if(partials is not null && value is not IAlwaysProp && !partials.Contains(key, StringComparer.InvariantCultureIgnoreCase))
2120
continue;
2221

2322
props.Add(key, value switch
2423
{
24+
Func<Task<object?>> f => await f.Invoke(),
2525
Func<object?> f => f.Invoke(),
2626
Delegate d => d.DynamicInvoke(),
27-
LazyProp l => l.Invoke(),
28-
AlwaysProp l => l.Invoke(),
27+
IInvokableProp l => await l.InvokeToObject(),
2928
_ => value
3029
});
3130
}

InertiaNetCore/Response.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public async Task ExecuteResultAsync(ActionContext context)
2020
Component = component,
2121
Version = version,
2222
Url = context.HttpContext.RequestedUri(),
23-
Props = GetFinalProps(context),
23+
Props = await GetFinalProps(context),
2424
};
2525

2626
if (!context.HttpContext.IsInertiaRequest())
@@ -49,14 +49,15 @@ public async Task ExecuteResultAsync(ActionContext context)
4949
}
5050
}
5151

52-
private InertiaProps GetFinalProps(ActionContext context)
52+
private async Task<InertiaProps> GetFinalProps(ActionContext context)
5353
{
5454
var partials = context.IsInertiaPartialComponent(component) ? context.GetPartialData() : null;
5555
var shared = context.HttpContext.Features.Get<InertiaSharedProps>();
5656
var errors = GetErrors(context);
57+
58+
var finalProps = await props.ToProcessedProps(partials);
5759

58-
return props
59-
.ToProcessedProps(partials)
60+
return finalProps
6061
.Merge(shared?.GetData())
6162
.AddTimeStamp()
6263
.AddErrors(errors);

InertiaNetCore/ResponseFactory.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace InertiaNetCore;
1111

1212
[SuppressMessage("Performance", "CA1822:Mark members as static")]
13+
[SuppressMessage("ReSharper", "MemberCanBeMadeStatic.Global")]
1314
internal class ResponseFactory(IHttpContextAccessor contextAccessor, SsrGateway ssrGateway, IOptions<InertiaOptions> options)
1415
{
1516
private object? _version;
@@ -92,6 +93,8 @@ public void Share(InertiaProps data)
9293
context.Features.Set(sharedData);
9394
}
9495

95-
public LazyProp Lazy(Func<object?> callback) => new(callback);
96-
public AlwaysProp Always(Func<object?> callback) => new(callback);
96+
public LazyProp<T> Lazy<T>(Func<T?> callback) => new(callback);
97+
public LazyProp<T> Lazy<T>(Func<Task<T?>> callback) => new(callback);
98+
public AlwaysProp<T> Always<T>(Func<T?> callback) => new(callback);
99+
public AlwaysProp<T> Always<T>(Func<Task<T?>> callback) => new(callback);
97100
}

InertiaNetCore/Utils/AlwaysProp.cs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
namespace InertiaNetCore.Utils;
22

3+
internal interface IAlwaysProp;
4+
35
/// <summary>
46
/// ALWAYS included on standard visits <br/>
57
/// ALWAYS included on partial reloads <br/>
68
/// ALWAYS evaluated when needed
79
/// </summary>
8-
public class AlwaysProp(Func<object?> callback)
10+
public class AlwaysProp<T> : InvokableProp<T>, IAlwaysProp
911
{
10-
public object? Invoke() => callback.Invoke();
11-
}
12+
public AlwaysProp(Func<T?> callback) : base(callback)
13+
{
14+
}
15+
16+
public AlwaysProp(Func<Task<T?>> callbackAsync) : base(callbackAsync)
17+
{
18+
}
19+
}

InertiaNetCore/Utils/InvokableProp.cs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
namespace InertiaNetCore.Utils;
2+
3+
internal interface IInvokableProp
4+
{
5+
internal Task<object?> InvokeToObject();
6+
}
7+
8+
public abstract class InvokableProp<T>: IInvokableProp
9+
{
10+
private readonly Func<T?>? _callback;
11+
private readonly Func<Task<T?>>? _callbackAsync;
12+
13+
protected InvokableProp(Func<T?> callback)
14+
{
15+
_callback = callback;
16+
}
17+
18+
protected InvokableProp(Func<Task<T?>> callbackAsync)
19+
{
20+
_callbackAsync = callbackAsync;
21+
}
22+
23+
public async Task<T?> Invoke()
24+
{
25+
if (_callback is not null)
26+
return _callback.Invoke();
27+
28+
if (_callbackAsync is not null)
29+
return await _callbackAsync.Invoke();
30+
31+
return default;
32+
}
33+
34+
public async Task<object?> InvokeToObject()
35+
{
36+
return await Invoke();
37+
}
38+
}

InertiaNetCore/Utils/LazyProp.cs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
namespace InertiaNetCore.Utils;
22

3+
internal interface ILazyProp;
4+
35
/// <summary>
46
/// NEVER included on standard visits <br/>
57
/// OPTIONALLY included on partial reloads (you should call <c>router.reload({ only: ["propName"] })</c>) <br/>
68
/// ONLY evaluated when needed
79
/// </summary>
8-
public class LazyProp(Func<object?> callback)
10+
public class LazyProp<T> : InvokableProp<T>, ILazyProp
911
{
10-
public object? Invoke() => callback.Invoke();
11-
}
12+
public LazyProp(Func<T?> callback) : base(callback)
13+
{
14+
}
15+
16+
public LazyProp(Func<Task<T?>> callbackAsync) : base(callbackAsync)
17+
{
18+
}
19+
}

0 commit comments

Comments
 (0)