On gists
Vue.js design pattern
•
JS Patterns
Vue.js
pattern.js
Raw
#
// https://blog.logrocket.com/exploring-advanced-design-patterns-vue-js/
// + MORE ... builder, adapter more usable ...
// BUILDER
// FormBuilder.js
class FormBuilder {
constructor() {
this.formFields = [];
}
addTextField(name, label, placeholder = '') {
this.formFields.push({
type: 'text',
name,
label,
placeholder,
});
return this; // Return the builder instance for method chaining
}
addNumberField(...) {
// Add a number field
}
addSelectField(...) {
// Add a select field
}
build() {
return this.formFields;
}
}
export default FormBuilder;
// Form.vue
import FormBuilder from './helpers/FormBuilder';
export default {
data() {
return {
formFields: [],
formData: {}, // Add a data property to store the form data
};
},
created() {
/*
* Use the FormBuilder class and its methods
* to construct the `formFields` object array
* with different form fields.
*/
this.formFields = new FormBuilder()
.addTextField('name', 'Name')
.addNumberField(...)
.addSelectField(...)
.build();
},
methods: {
handleSubmit() {
// Log the form data when the form is submitted
console.log(this.formData);
},
},
};
// ADAPTER
// UserAdapter.js
class UserAdapter {
constructor(externalUserData) {
this.externalUserData = externalUserData;
}
adapt() {
/*
* Adapt the external user data to match
* the component's expected format.
*
* Considering the structure of the expected
* response, grab the right object array
* (`results`) and its only value.
*
* @link https://randomuser.me/api/
*/
const userData = this.externalUserData.results[0];
return {
name: `${userData.name.first} ${userData.name.last}`,
email: userData.email,
gender: userData.gender,
location: `${userData.location.city}, ${userData.location.country}`,
displayPic: userData.picture.large,
};
}
}
export default UserAdapter;
import UserAdapter from './UserAdapter';
export default {
data() {
return {
userAdapter: null,
user: null
};
},
created() {
/*
* Get user data from an external API
* with a different format.
*/
async created() {
try {
// Make an API call
const response = await fetch('...');
if (!response.ok) {
throw new Error('Failed to fetch external user data');
}
const externalUserData = await response.json();
/*
* Create an adapter to convert external weather data
* to the format expected by the component.
*/
this.userAdapter = new UserAdapter(externalUserData);
// Adapt the data to the component's expected format
this.user = this.userAdapter.adapt();
} catch (error) {
console.error('Error fetching external user data:', error);
this.errorMessage = 'Failed to load user data. Please try again later.';
}
},
}
};
// Composable pattern
// userPreferences.js
import { ref } from 'vue';
const theme = ref('light');
const language = ref('english');
const notifications = ref(true);
// Getter for theme
const getTheme = () => theme.value;
// Setter for theme
const setTheme = (newTheme) => {
theme.value = newTheme;
};
// Getter for language
const getLanguage = () => language.value;
// Setter for language
const setLanguage = (newLanguage) => {
language.value = newLanguage;
};
// Getter for notifications
const getNotificationsEnabled = () => notifications.value;
// Setter for notifications
const setNotificationsEnabled = (enabled) => {
notifications.value = enabled;
};
export {
getTheme,
setTheme,
getLanguage,
setLanguage,
getNotificationsEnabled,
setNotificationsEnabled,
};
// themeSelector.js
import { computed } from 'vue';
import { getTheme, setTheme } from '../utils/userPreferences';
export function useThemeSelection() {
const selectedTheme = computed({
get: () => getTheme(),
set: (newTheme) => setTheme(newTheme),
});
return {
selectedTheme,
};
}
// ThemeSelector.vue
<template>
<div>
<label for="theme-switch">Theme</label>
<select id="theme-switch" @change="handleChange" v-model="selectedTheme">
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { useThemeSelection } from '../composables/themeSelector';
export default defineComponent({
setup() {
const { selectedTheme } = useThemeSelection();
const handleChange = (event) => {
const newTheme = event.target.value;
selectedTheme.value = newTheme; // Set the new theme
};
return {
selectedTheme,
handleChange,
};
},
});
</script>
// AnyOther.vue
<template>
<div class="settings">
<div class="settings-row">
<ThemeSelector />
<div :class="selectedTheme">
Here's how the {{ selectedTheme }} theme looks like.
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { useThemeSelection } from './composables/themeSelector';
import ThemeSelector from './components/ThemeSelector';
export default defineComponent({
setup() {
const { selectedTheme } = useThemeSelection();
return {
selectedTheme,
};
},
components: {
ThemeSelector
},
});
</script>
<style>
.light {
background-color: #fff;
color: #000;
}
.dark {
background-color: #000;
color: #fff;
}
</style>