Activate aurelia canDeactivate() life-cycle hook manually - aurelia

I have two types of routes in my application, routes to aurelia app itself and some external routes. I'm using href property to specify external routes. in example below Task is a such external route.
{
"name": "dashboard",
"route": "dashboard",
"moduleId": "../dashboard/dashboard",
"settings": {
"icon": "chart-bar",
"helpPageId": "_30000"
},
"title": "routes:root.dashboard",
"nav": 25
},
{
"name": "tasks",
"settings": {
"icon": "check-square",
"helpPageId": "_40000"
},
"title": "routes:root.tasks",
"nav": 30,
"href": "Task"
}
When a user have made some changes in an aurelia route I am using canDeactivate hook to display a custom dialog box to the user, asking to discard changes or save changes.
canDeactivate(): Promise<boolean> {
//helper method having dialog box logic
return this.helperMethod.canDiscardChanges(this.modelTracker.isChanged(this.model), this.deactivateSaveCallback.bind(this));
}
But the problem is when I navigate to an external route this hook is not activated. This is understandable because aurelia router is not responsible for that. What I tried to do was bind a method with beforeunload event, and then activate the canDeactivate manually.
async activate(params: any): Promise<void> {
// some logic
window.addEventListener("beforeunload", this.onDomWindowBeforenload);
}
onDomWindowBeforeunload: (event: BeforeUnloadEvent) => void = event => {
this.canDeactivate();
}
async deactivate(): Promise<void> {
window.removeEventListener("beforeunload", this.onDomWindowBeforeunload);
}
This shows the intended dialog box, but does not wait for user to do anything, it is same as previous, just goes to the specified route after showing the dialog. Any help is appericiated.

Related

How to prevent Vue make API call on back button click and instead take data from Vuex?

I'm newbie to Vue 3. I trying to do my first app with Vue 3 + Vuex + Vue route.
Step-by-step:
On page load --> API call --> received data set to Vuex store.
Render items on a page from store.
When user clicks on item on homepage, then a new page loading with the data from store (no second API call).
Propblem: when user clicks on browser back button then homepage is rendered again but it makes an API call for the items but I do not need them as they're in store.
How to fix that so if browser back button clicked then no second API call for the same data?
Main component
methods: {
...mapActions('characters', ['getCharacters']),
},
mounted() {
this.getCharacters();
},
Store action which makes the request:
actions: {
async getCharacters({ state, commit }, page = state.currentPage) {
try {
const res = await fetch(`${BASE_URL}character/?page=${page}`);
if (res.ok) {
const { info, results } = await res.json();
commit('setCharacters', { list: results });
}
} catch (e) {
console.log(e);
}
},
};
You can check if characters data exists before invoke getCharacters()
computed: {
...mapState('characters', ['characters']),
},
methods: {
...mapActions('characters', ['getCharacters']),
},
mounted() {
if (!this.characters) {
this.getCharacters();
}
},

how to convert Nuxt js (pwa) project to apk?

I am working on a nuxt project and I want to add it to Google Play, but it requires an apk output
so is there any solution to get the apk file from Nuxt?
I've already tried using android studio but it was unsuccessful
manifest.json:
{
"name": "my nuxt app",
"short_name": "my lovely nuxt app",
"description": "pwa to apk",
"icons": [
{
"src": "/logo.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/300.png",
"sizes": "384x384",
"type": "image/jpg"
},{
"src": "/512.jpg",
"sizes": "512x512",
"type": "image/jpg"
}
],
"start_url": "/?standalone=true",
"display": "standalone",
"background_color": "#222",
"theme_color": "#222",
"lang": "fa",
"prefer_related_applications": true
}
and I get this error when I want to install it:
for security your phone is set to block installation
TWA are a thing as you can read here: https://www.ateamsoftsolutions.com/what-are-pwa-and-twa/
Meanwhile, this is not the same as having an .apk which is something totally different from the Web platform as you can see here: https://fileinfo.com/extension/apk (none of the extensions are ones used on the Web)
This is a totally different bundle language and ecosystem. Hence, you cannot port a PWA into a Google Play app.
You'll need to learn ways to make a mobile app with either Capacitor (Quasar) can help or similar solutions.
Or use React Native, Flutter or even vanilla Kotlin (the latter being the closest one to the machine).
In addition to kissu's comment, I always use Nuxt.js for regular websites but Ionic/Vue with Capacitor for mobile apps, it works great, same ecosystem and a great UI components and CLI from Ionic. This is just a suggestion for something that works and it has a minimum learning curve.
after so many searches and thanks to #kissu for give me a hint about twa i found the solution:
1.first of all u need a service worker for your nuxt project and put it in the static folder
example:
/static/sw.js
and inside of sw.js:
const options = {"workboxURL":"https://cdn.jsdelivr.net/npm/workbox-cdn#5.1.4/workbox/workbox-sw.js","importScripts":[],"config":{"debug":false},"cacheOptions":{"cacheId":"online-actor-prod","directoryIndex":"/","revision":"c35hcbL1ctml"},"clientsClaim":true,"skipWaiting":true,"cleanupOutdatedCaches":true,"offlineAnalytics":false,"preCaching":[{"revision":"c35hcbL1ctml","url":"/"}],"runtimeCaching":[{"urlPattern":"/_nuxt/","handler":"CacheFirst","method":"GET","strategyPlugins":[]},{"urlPattern":"/","handler":"NetworkFirst","method":"GET","strategyPlugins":[]}],"offlinePage":null,"pagesURLPattern":"/","offlineStrategy":"NetworkFirst"}
importScripts(...[options.workboxURL, ...options.importScripts])
initWorkbox(workbox, options)
workboxExtensions(workbox, options)
precacheAssets(workbox, options)
cachingExtensions(workbox, options)
runtimeCaching(workbox, options)
offlinePage(workbox, options)
routingExtensions(workbox, options)
function getProp(obj, prop) {
return prop.split('.').reduce((p, c) => p[c], obj)
}
function initWorkbox(workbox, options) {
if (options.config) {
// Set workbox config
workbox.setConfig(options.config)
}
if (options.cacheNames) {
// Set workbox cache names
workbox.core.setCacheNameDetails(options.cacheNames)
}
if (options.clientsClaim) {
// Start controlling any existing clients as soon as it activates
workbox.core.clientsClaim()
}
if (options.skipWaiting) {
workbox.core.skipWaiting()
}
if (options.cleanupOutdatedCaches) {
workbox.precaching.cleanupOutdatedCaches()
}
if (options.offlineAnalytics) {
// Enable offline Google Analytics tracking
workbox.googleAnalytics.initialize()
}
}
function precacheAssets(workbox, options) {
if (options.preCaching.length) {
workbox.precaching.precacheAndRoute(options.preCaching, options.cacheOptions)
}
}
function runtimeCaching(workbox, options) {
const requestInterceptor = {
requestWillFetch({ request }) {
if (request.cache === 'only-if-cached' && request.mode === 'no-cors') {
return new Request(request.url, { ...request, cache: 'default', mode: 'no-cors' })
}
return request
},
fetchDidFail(ctx) {
ctx.error.message =
'[workbox] Network request for ' + ctx.request.url + ' threw an error: ' + ctx.error.message
console.error(ctx.error, 'Details:', ctx)
},
handlerDidError(ctx) {
ctx.error.message =
`[workbox] Network handler threw an error: ` + ctx.error.message
console.error(ctx.error, 'Details:', ctx)
return null
}
}
for (const entry of options.runtimeCaching) {
const urlPattern = new RegExp(entry.urlPattern)
const method = entry.method || 'GET'
const plugins = (entry.strategyPlugins || [])
.map(p => new (getProp(workbox, p.use))(...p.config))
plugins.unshift(requestInterceptor)
const strategyOptions = { ...entry.strategyOptions, plugins }
const strategy = new workbox.strategies[entry.handler](strategyOptions)
workbox.routing.registerRoute(urlPattern, strategy, method)
}
}
function offlinePage(workbox, options) {
if (options.offlinePage) {
// Register router handler for offlinePage
workbox.routing.registerRoute(new RegExp(options.pagesURLPattern), ({ request, event }) => {
const strategy = new workbox.strategies[options.offlineStrategy]
return strategy
.handle({ request, event })
.catch(() => caches.match(options.offlinePage))
})
}
}
function workboxExtensions(workbox, options) {
}
function cachingExtensions(workbox, options) {
}
function routingExtensions(workbox, options) {
}
2.you also need a manifest , for that put this code in your nuxt.config.js:
export default{
pwa: {
manifest: {
name: 'example name',
short_name: 'example',
lang: 'fa',
theme_color: '#222',
background_color: '#222',
start_url: `/`,
prefer_related_applications: true,
},
icon: {
fileName: 'logo.png'
},
},
}
3.now everything is ready to create your apk, now you can search for pwa to apk in google And use sites that offer these services:
ive already tried these sites and all working well:
gonative.io
or
pwabuilder.com

How to useState in react native?

I am trying to use useState in React Native to store and display data but the array is displaying empty:
Here's my data which I am trying to store:
Array [
Object {
"text": "Dhananjay",
"time": 1610528730258,
},
Object {
"text": "Abhey",
"time": 1610529549681,
},
Object {
"text": "Himanshu",
"time": 1610529566017,
},
]
Below is my code:
const [list, setList] = useState([]);
useEffect(() => {
const items = firebase.database().ref("userName");
items.on("value", datasnap => {
//console.log(Object.values(datasnap.val()));
console.log(list);
setList([...list, {
id: Object.values(datasnap.val())
}
]);
console.log(list);
})
console.log(list);
}, []);
Firstly, useEffect in this instance will trigger your function anytime:
The functional component mounts, and
One of the dependencies specified in your array has changed between a subsequent re-render
Since you haven't specified any dependencies, the best way to check whether this is working is to instead render the contents of the list inside your component and observe whether the list has changed.
It is also worth noting that useState is asynchronous and you could consider using:
useEffect(() => { console.log(list)}, [list])
Alternatively, you could also try rendering the contents as well (although you've covered your bases above) with:
return (
<div>
{JSON.stringify(list)}
</div>
)
And see if the text appears in the component. If it doesn't chances are the on callback isn't firing here.

Rename page with Apollo & Vue router

I'd like to rename the title of the pages depending on the data received by Apollo.
For example, at the url myproject.com/media/title1, my GraphQL reply will be:
{
"data": {
"creation":
{
"id": "1",
"title": "Title1",
"creationDate": "2019-09-22 07:37:57 UTC",
"coverUrl": "linktomedia.com/image.jpg",
}
}
}
I want to get the title in data -> creation -> title as my title page name.
How can I do this ?
Thank you.
vue-apollo smart queries provide a result callback that's called when the query completes. You can complete any actions that depend on the data, including changing the title, inside the callback. Something like:
apollo: {
creation: {
query: YOUR_QUERY,
result: ({ data }) => {
document.title = data.creation.title
},
}
}

Nuxt refresh router view when router :id parameter changes

My Nuxt app loads a link and it's child view on the route http://127.0.0.1/user/:id. I put the API calls for the user in mounted hook of the router view.
If route id changes, mounted is not triggered anymore because the child view is already loaded. I ended up with solution - watch the $route.params.id and moved the API call from mounted to this watcher.
watch: {
$route() {
this.getRows()
}
}
Is there a better way to do this?
Solution 1
Force the reload when the route changes defining a :key for the <nuxt-child>, like this:
<nuxt-child :key="$route.fullPath"></nuxt-child>
Solution 2
Put the API call to load the user in a watch to the id coming from the URL instead to mounted, you can use immediate: true to call it in the fist load.
export default {
data() {
return {
user: null
}
},
asyncData({ params }) {
return {
id: params.id
}
},
watch: {
id: {
immediate: true,
handler(id) {
//Call the API to get the user using the id
}
}
}
}