@vue-modeler/model 1.0.7-beta.1 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What is @vue-modeler/model
|
|
4
4
|
|
|
5
|
-
A state management library based on models for [
|
|
5
|
+
A state management library based on models for [Vue.js](https://vuejs.org/). The extremely simple API serves single purpose — creating models. It preserves types, supports OOP, DRY, and SOLID principles.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@ A state management library based on models for [VUE.js.](VUE.js.) The extremely
|
|
|
34
34
|
Less code, fewer issues.
|
|
35
35
|
|
|
36
36
|
#### **Use Classes and Interfaces**
|
|
37
|
-
Both
|
|
37
|
+
Both Vuex and Pinia employ custom approaches for managing stores and reusing common code. I wanted to use standard classes, interfaces, getters, and protected properties. That's why my models are built upon classes and support OOP patterns.
|
|
38
38
|
|
|
39
39
|
#### **Reuse Business Logic Across Projects**
|
|
40
40
|
Models are classes where dependencies are injected via constructors, so model files don't contain direct imports of external dependencies. Models can easily be extracted into separate modules and reused in other projects or with different UI libraries.
|
|
@@ -51,4 +51,192 @@ Models are classes where dependencies are injected via constructors, so model fi
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
-
This library promises to simplify state management in [Vue.js](
|
|
54
|
+
This library promises to simplify state management in [Vue.js](https://vuejs.org/) applications while promoting clean, scalable, and maintainable code.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
First, [install @vue-modeler/dc](https://github.com/vue-modeler/dc?tab=readme-ov-file#installation)
|
|
59
|
+
|
|
60
|
+
Then install @vue-modeler/model:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm add @vue-modeler/model
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage Examples
|
|
67
|
+
|
|
68
|
+
### 1. Define Proto model
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { ProtoModel, action } from '@vue-modeler/model'
|
|
72
|
+
|
|
73
|
+
export interface Api {
|
|
74
|
+
fetchUser: () => Promise<UserDto>
|
|
75
|
+
patch: (dto: PatchDto) => Promise<void>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface UserDto {
|
|
79
|
+
name: string
|
|
80
|
+
email: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface PatchDto {
|
|
84
|
+
name?: string
|
|
85
|
+
email?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Define your model class extending ProtoModel
|
|
89
|
+
class User extends ProtoModel {
|
|
90
|
+
|
|
91
|
+
// Regular properties. It will be reactive after creation model
|
|
92
|
+
protected _name = ''
|
|
93
|
+
protected _email = ''
|
|
94
|
+
|
|
95
|
+
get name(): string {
|
|
96
|
+
return this._name
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get email(): string {
|
|
100
|
+
return this._email
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
constructor(
|
|
104
|
+
private api: Api
|
|
105
|
+
) {
|
|
106
|
+
super()
|
|
107
|
+
|
|
108
|
+
this.action(this.init).exec()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Actions (async methods with @action decorator)
|
|
112
|
+
@action async init() {
|
|
113
|
+
if (this.action(this.patch).isPending) {
|
|
114
|
+
throw new Error('action execution conflict')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const userDto = await this.api.fetchUser()
|
|
118
|
+
this._name = userDto.name
|
|
119
|
+
this._email = userDto.email
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@action async patch(dto: PatchDto): Promise<void> {
|
|
123
|
+
if (this.action(this.init).isPending) {
|
|
124
|
+
throw new Error('action execution conflict')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await this.api.patch(dto)
|
|
128
|
+
if (dto.name) this._name = dto.name
|
|
129
|
+
if (dto.email) this._email = dto.email
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 2. Create API
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
export const fetchUser = async (): Promise<UserDto> => {
|
|
138
|
+
const response = await fetch('/api/users/me')
|
|
139
|
+
return response.json()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const patchUser = async (dto: PatchDto): Promise<void> => {
|
|
143
|
+
await fetch('/api/users/me', {
|
|
144
|
+
method: 'PATCH',
|
|
145
|
+
headers: { 'Content-Type': 'application/json' },
|
|
146
|
+
body: JSON.stringify(dto)
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 3. Compose all together and create model provider
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { model } from '@vue-modeler/model'
|
|
155
|
+
|
|
156
|
+
export const useUser = model(() => new User({
|
|
157
|
+
fetchUser,
|
|
158
|
+
patch: patchUser
|
|
159
|
+
}))
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 4. Using Models in Vue Components
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<template>
|
|
166
|
+
<div>
|
|
167
|
+
<div v-if="user.init.isPending">Loading...</div>
|
|
168
|
+
<div v-else-if="user.init.error">Error: {{ user.init.error.message }}</div>
|
|
169
|
+
<div v-else>
|
|
170
|
+
<h2>{{ user.name }}</h2>
|
|
171
|
+
<p>{{ user.email }}</p>
|
|
172
|
+
|
|
173
|
+
<form @submit.prevent="saveUser" class="user-form">
|
|
174
|
+
<div class="form-group">
|
|
175
|
+
<label for="name">Name:</label>
|
|
176
|
+
<input
|
|
177
|
+
id="name"
|
|
178
|
+
v-model="formData.name"
|
|
179
|
+
type="text"
|
|
180
|
+
:disabled="user.init.isPending || user.patch.isPending"
|
|
181
|
+
required
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="form-group">
|
|
186
|
+
<label for="email">Email:</label>
|
|
187
|
+
<input
|
|
188
|
+
id="email"
|
|
189
|
+
v-model="formData.email"
|
|
190
|
+
type="email"
|
|
191
|
+
:disabled="user.init.isPending || user.patch.isPending"
|
|
192
|
+
required
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="form-actions">
|
|
197
|
+
<button type="button"
|
|
198
|
+
:disabled="user.init.isPending || user.patch.isPending"
|
|
199
|
+
@click="user.init.exec()"
|
|
200
|
+
>
|
|
201
|
+
Reload User
|
|
202
|
+
</button>
|
|
203
|
+
|
|
204
|
+
<button type="submit" :disabled="user.init.isPending || user.patch.isPending">
|
|
205
|
+
{{ user.patch.isPending ? 'Saving...' : 'Save Changes' }}
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
</form>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</template>
|
|
212
|
+
|
|
213
|
+
<script setup lang="ts">
|
|
214
|
+
import { ref, watch } from 'vue'
|
|
215
|
+
import { useUser } from './user-model'
|
|
216
|
+
|
|
217
|
+
const user = useUser()
|
|
218
|
+
|
|
219
|
+
// Form data for editing
|
|
220
|
+
const formData = ref({
|
|
221
|
+
name: user.name,
|
|
222
|
+
email: user.email
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// Update form data when user data changes
|
|
226
|
+
watch(() => user.name, (newName) => {
|
|
227
|
+
formData.value.name = newName
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
watch(() => user.email, (newEmail) => {
|
|
231
|
+
formData.value.email = newEmail
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const saveUser = () => {
|
|
235
|
+
user.patch.exec({
|
|
236
|
+
name: formData.value.name,
|
|
237
|
+
email: formData.value.email
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
</script>
|
|
241
|
+
```
|
|
242
|
+
|