jaspr_getx_distil 1.0.0
jaspr_getx_distil: ^1.0.0 copied to clipboard
A distilled reactive state-management and hybrid scoped-DI engine for Jaspr (GetX DX, Component tree).
// jaspr_getx_distil 최소 SPA example.
//
// * 앱 전체 수명 서비스는 루트 `BindingComponent(eager: true)`에 둔다 (immortal `GetxService`).
// * 화면 수명 Controller는 각 라우트의 `BindingComponent` 스코프에 둔다.
// * ShellRoute = feature 스코프: shell Binding은 자식 라우트 이동에도 유지된다.
//
// 실행: `cd example && jaspr serve`
import 'package:jaspr/dom.dart';
import 'package:jaspr/server.dart';
import 'package:jaspr_router/jaspr_router.dart';
import 'package:jaspr_getx_distil/jaspr_getx_distil.dart';
void main() {
Jaspr.initializeApp();
runApp(Document(title: 'jaspr_getx_distil example', body: const App()));
}
// ─── 앱 전체 수명 = immortal GetxService ────────────────────────────────────
/// 유저 세션 같은 앱 수명 서비스. 루트 eager 스코프에서 immortal로 등록된다.
class SessionService extends GetxService {
final Rx<String> displayName = Rx<String>('guest');
int closeCount = 0;
@override
void onClose() {
closeCount++;
super.onClose();
}
}
// ─── 화면 수명 = scoped Controller ──────────────────────────────────────────
/// `/` 화면 컨트롤러. BindingComponent가 unmount되면 onClose 후 registry 제거.
class HomeController extends GetxController {
final Rx<int> count = Rx<int>(0);
/// Rx 값을 바꾸고 `update()`로 GetBuilder 리스너를 깨운다.
void increment() {
count.value++;
update();
}
}
/// `/other` 화면 컨트롤러.
class AboutController extends GetxController {
final Rx<String> message = Rx<String>('about');
}
// ─── 화면 (GetView) ─────────────────────────────────────────────────────────
class HomePage extends GetView<HomeController> {
const HomePage({super.key});
@override
Component build(BuildContext context) {
final controller = this.controller;
return div([
h1([.text('jaspr_getx_distil example')]),
// Obx는 동기 build가 읽은 Rx만 추적한다. leaf만 감싼다.
Obx(() => p([.text('count: ${controller.count.value}')])),
button(onClick: () => controller.increment(), [.text('+1')]),
// GetxController.update()를 구독하는 명령형 빌더. increment()가
// update()를 호출하면 이 리스너가 rebuild된다.
GetBuilder<HomeController>(
builder: (c) => span([.text('update() → ${c.count.value}')]),
),
// 전역 서비스는 라우트 이동 후에도 find 가능.
nav([
Link(to: '/about', child: span([.text('Go to about')])),
]),
]);
}
}
class AboutPage extends GetView<AboutController> {
const AboutPage({super.key});
@override
Component build(BuildContext context) {
final controller = this.controller;
return div([
h1([.text('About')]),
Obx(() => p([.text('message: ${controller.message.value}')])),
nav([
Link(to: '/', child: span([.text('Go home')])),
]),
]);
}
}
// ─── 앱 / 라우트 ────────────────────────────────────────────────────────────
/// ShellRoute = feature 스코프. 자식 `/` `/about` 이동에도 유지되는 기능
/// 서비스. eager BindingComponent 위에서 immortal이 된다.
class ShellBinding extends GetxService {
final Rx<int> pings = Rx<int>(0);
}
class App extends StatelessComponent {
const App({super.key});
@override
Component build(BuildContext context) {
return BindingComponent(
key: Key('root-services'),
eager: true,
bindings: [
// GetxService는 eager BindingComponent 위에서 immortal이 된다. 프로세스
// 전역 static이므로 SSR에선 요청 시작 시 Get.reset()으로 재설정한다.
Bind<SessionService>(() => SessionService()),
],
child: Router(
routes: [
// ShellRoute.builder는 (context, state, child)를 받아 shell을 감싼다.
// 이 BindingComponent는 자식 라우트가 바뀌어도 unmount되지 않는다.
ShellRoute(
builder: (_, _, child) => BindingComponent(
key: Key('shell'),
eager: true,
bindings: [Bind<ShellBinding>(() => ShellBinding())],
child: child,
),
routes: [
Route(
path: '/',
builder: (_, _) => BindingComponent(
key: Key('home'),
bindings: [Bind<HomeController>(() => HomeController())],
child: const HomePage(),
),
),
Route(
path: '/about',
builder: (_, _) => BindingComponent(
key: Key('about'),
bindings: [Bind<AboutController>(() => AboutController())],
child: const AboutPage(),
),
),
],
),
],
),
);
}
}