<!--ErrorBoundary.vue-->
<!-- https://vueschool.io/articles/vuejs-tutorials/what-is-a-vue-js-error-boundary-component/ -->
<script setup>
import { ref, computed } from 'vue'
// create an reactive container to store the potential error
const error = ref()
// using Vue's build in lifecycle hook
// listen for errors from components passed to the default slot
onErrorCaptured(err => {
// set the reactive error container to the thrown error
error.value = err
// return false to prevent error from bubbling further
// (this is optional, if you have a top level error reporter catching errors
// you probably don't want to do this.
// Alternatively you could report your errors in the boundary and prevent bubble
return false
})
// create a way to clear the error
function clearError() {
error.value = null
}
// provide the error and the clear error function to the slot
// for use in consuming component to display messaging
// and clear the error
const slotProps = computed(() => {
if (!error.value) return {}
return { error, clearError }
})
// if there's an error show the error slot, otherwise show the default slot
const slotName = computed(() => (error.value ? 'error' : 'default'))
</script>
<template>
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
<template>
<form
class="mt-8"
method="post"
:id="frmId"
v-ajax-on-before="() => $emit('onPreloader', true)"
v-ajax-on-success="() => $emit('onPreloader', false)"
v-ajax-on-error="() => $emit('onPreloader', false)"
v-nette-form
data-ajax-use-router
>
/* https://codepen.io/kevinpowell/full/vYvEdWG */
.cluster {
outline: 5px solid hotpink;
padding: 1rem;
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.flexible-grid {
outline: 5px solid hotpink;
padding: 1rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.flexible-grid > * {
flex: 1;
}
.auto-grid {
outline: 5px solid hotpink;
padding: 1rem;
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
}
.reel {
outline: 5px solid hotpink;
padding: 1rem;
display: grid;
gap: 1rem;
grid-auto-flow: column;
grid-auto-columns: 45%;
overflow-x: scroll;
scroll-snap-type: x mandatory;
scroll-padding: 1rem;
}
.reel > * {
scroll-snap-align: start;
}
.main-with-sidebar {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 1em;
max-width: 1200px;
margin-inline: auto;
}
.main-with-sidebar > :first-child {
flex-basis: 500px;
flex-grow: 9999;
}
.main-with-sidebar > :last-child {
flex-basis: 300px;
flex-grow: 1;
}
// https://medium.com/@hxu0407/master-these-8-promise-concurrency-control-techniques-to-significantly-improve-performance-5a1c199b6b3c
// 1. Promise.all: Execute in Parallel and Return Results Together
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // Output: [1, 2, 3]
});
// 2. Promise.allSettled: Execute in Parallel and Return All States
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
console.log(results);
/* Output:
[
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 'Error' },
{ status: 'fulfilled', value: 3 }
]
*/
})
// 3. Promise.race: Execute in Parallel and Return the Fastest
const promise1 = new Promise(resolve => setTimeout(() => resolve("Fast"), 100));
const promise2 = new Promise(resolve => setTimeout(() => resolve("Slow"), 500));
Promise.race([promise1, promise2])
.then(result => console.log(result)); // Output: "Fast" (whichever resolves first)
// 4. Promise.any (ES2021): Execute in Parallel and Return the First Fulfilled
const promise1 = Promise.reject("Error 1");
const promise2 = new Promise(resolve => setTimeout(() => resolve("Success"), 200));
const promise3 = Promise.reject("Error 2");
Promise.any([promise1, promise2, promise3])
.then(result => console.log(result)) // Output: "Success"
.catch(error => console.log(error.errors)); // If all reject
// 5. Custom Concurrency Control Function: Limit Max Concurrent Requests
async function limitConcurrency(tasks, limit) {
const results = [];
const running = new Set();
for (const task of tasks) {
const promise = task().then(result => {
running.delete(promise);
return result;
});
running.add(promise);
results.push(promise);
if (running.size >= limit) {
await Promise.race(running);
}
}
return Promise.all(results);
// https://medium.com/@hxu0407/9-smart-ways-to-replace-if-else-in-javascript-28f82ad6dcb9
// 1. Object Mapping Instead of if-else
function getPrice(user) {
if (user.type === 'vip') {
return 'VIP Price';
} else if (user.type === 'svip') {
return 'SVIP Price';
} else if (user.type === 'vvip') {
return 'VVIP Price';
} else {
return 'Regular Price';
}
}
// better
const priceStrategy = {
vip: () => 'VIP Price',
svip: () => 'SVIP Price',
vvip: () => 'VVIP Price',
default: () => 'Regular Price'
};
function getPrice(user) {
return (priceStrategy[user.type] || priceStrategy.default)();
}
// 2. Replace Multiple Conditions with Array.includes
if (status === 'failed' || status === 'error' || status === 'rejected') {
handleError();
}
// better
const errorStatus = ['failed', 'error', 'rejected'];
if (errorStatus.includes(status)) {
handleError();
}
// 3. Chained Ternary Operators
let message;
if (score >= 90) {
message = 'Excellent';
} else if (score >= 80) {
message = 'Good';
} else if (score >= 60) {
message = 'Pass';
} else {
message = 'Fail';
}
// better
const message =
score >= 90 ? 'Excellent' :
score >= 80 ? 'Good' :
score >= 60 ? 'Pass' :
'Fail';
// 4. Logical Operators && and ||
// Replacing a simple `if`
user.isAdmin && showAdminPanel();
// Setting default values
const name = user.name || 'unnamed';
// Nullish coalescing
const count = data?.users ?? [];
// 5. Switch-Case with Pattern Matching
const actions = new Map([
[/^vip/, handleVip],
[/^admin/, handleAdmin],
[/^user/, handleUser]
]);
/*or
const actions = [
[/^vip/, handleVip],
[/^admin/, handleAdmin],
[/^user/, handleUser]
];
*/
const handleRequest = (type) => {
const action = [...actions].find(([key]) => key.test(type));
return action ? action[1]() : handleDefault();
};
// 6. Using Proxy for Conditional Interception
const handler = {
get: (target, property) => {
return property in target ? target[property] : target.default;
}
};
const services = new Proxy({
admin: () => 'Admin Service',
user: () => 'User Service',
default: () => 'Default Service'
}, handler);
// 7. Functional Approach
// Composing conditions
const isAdult = age => age >= 18;
const hasPermission = role => ['admin', 'superuser'].includes(role);
const canAccess = user => isAdult(user.age) && hasPermission(user.role);
// Usage
users.filter(canAccess).forEach(grantAccess);
// 8. State Machine Pattern
const stateMachine = {
draft: {
publish: 'published',
delete: 'deleted'
},
published: {
unpublish: 'draft',
archive: 'archived'
},
archived: {
restore: 'draft'
}
};
const changeState = (currentState, action) =>
stateMachine[currentState]?.[action] || currentState;
// 9. Use Decorators to Handle Conditional Logic
function checkPermission(target, name, descriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
if (this.user?.hasPermission) {
return original.apply(this, args);
}
throw new Error('No permission');
};
return descriptor;
}
class Document {
@checkPermission
edit() {
// Edit the document
}
}
/*
onScopeDispose - Podobně jako onWatcherCleanup, ale obecnější.
Zavolá se, když je aktuální reaktivní efektový scope (effect scope) ukončen.
Je to užitečné pro čištění zdrojů v jakémkoliv reaktivním kontextu:
*/
// 1
import { onScopeDispose, effectScope } from 'vue'
// Vytvoření izolovaného scope
const scope = effectScope()
scope.run(() => {
// Kód uvnitř scope
onScopeDispose(() => {
// Tento kód se zavolá při scope.stop()
})
})
// Později můžete ukončit scope
scope.stop()
// 2)
const scope = effectScope()
scope.run(() => {
const state = reactive({ count: 0 })
const double = computed(() => state.count * 2)
watch(() => state.count, (count) => console.log(count))
})
// Později ukončí všechny reaktivní efekty
scope.stop()
// 1) cleanup when watch is ended
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const { response, cancel } = doAsyncWork(newId)
// `cancel` is called if `id` changes or the component unmounts
onWatcherCleanup(cancel)
})
// 2) better watch with desctruct
const { pause, resume, stop } = watch(source, (newVal, oldVal) => {
// Watch logic
});
// Pause watching
pause();
// Resume watching
resume();
// 3 watch logic, not only true/false ...
watch(reactiveObject, (newVal, oldVal) => {
// Watch logic
}, { deep: 2 });