Ionic4 navController back with params (callback) - ionic4

Just looking for best practice for sending data from a child route, back up to its parent. For example, if I have a list view and I want to add an item which is complex enough to merit its own page, how do I get that added item back to the list view? Now obviously I could just re-fetch the entire list but that seems kind of silly and not efficient. I found that I can also pass a “callback function” in the ActivatedRoute params when router navigates the AddPage, but that also felt wrong (though I like it better than the first option). It’d be great if I could pass the data back through the nav.back() method, but what is the recommended way to do what I need to do?

pass in a callback when transitioning(it is not working in ionic4)
// callback...
myCallbackFunction = function(_params) {
return new Promise((resolve, reject) => {
resolve();
});
}
// push page...
this.navController.navigateForward(["add-product",{"callback": myCallbackFunction}]);
in the AddProductPage
this.activatedRoute.params.subscribe((data: any) => {
this.callback = data.callback;
console.log("callback "+this.callback)
});
error:-i m getting data in string form(here i m getting callback data in string format)
output:-
callback "function(_params) {
return new Promise((resolve, reject) => {
resolve();
});
"
this.callback(param).then(()=>{
this.navController.pop();
});

I do what Pradeep said, and its working:
Put navCtrl in your constructor parameter:
private navCtrl: NavController,
And somewhere in your code when you want to go back:
let navigationExtras: NavigationExtras = {
state: {
param: "1",
}
};
this.navCtrl.navigateBack(['previouspage'], navigationExtras)
In your previous page constructor:
constructor(
........
private router: Router,
){
if (this.router.getCurrentNavigation().extras.state && this.router.getCurrentNavigation().extras.state.param) {
let param = this.router.getCurrentNavigation().extras.state.param
}
}

Related

nextTick() not triggering DOM update

I'm creating a messaging app and I'm having some trouble with scrolling to the bottom of an ion-content element when a new message is added to an array. I'm using the scrollToBottom() method that comes with ion-content, and I'm using the Composition API in Vue 3.
Consider this snippet:
setup(props) {
const replyContent = ref("")
const messages = ref([])
// References to ion-content in the template
const ionContent = ref(null)
const reply = async () => {
const message = await replyToThread(props.threadId, replyContent.value).then((message) => message)
messages.value.push(message)
nextTick(() => {
console.log("DOM updated!")
if (ionContent.value) {
ionContent.value.$el.scrollToBottom()
}
})
}
return { replyContent, messages, ionContent, reply }
}
replyToThread() performs an API call and returns the new message, and nextTick() should ensure me that the DOM has been updated so that I can have my way with it. The console does successfully log "DOM updated!", but no scrolling to the bottom happens.
But, and somehow this works every time nextTick() doesn't, when I replace the nextTick() code block with the following, it works flawlessly:
setTimeout(() => {
if (ionContent.value) {
ionContent.value.$el.scrollToBottom()
}
}, 200)
I have to set the timeout at around 200 ms, otherwise it doesn't work. But relying on this when something fancy like nextTick() should do the trick feels quite dirty. Does anyone know why this is happening?
That is because nextTick() only guarantees that the actual DOM has been updated: it doesn't mean that the browser has actually finished the layout of the page. That is the reason why you need an arbitrary timeout to ensure the scrolling works, because after 200ms the browser is likely to be done laying things out based on the updated DOM.
To fix this you will probably need to rely on window.requestAnimationFrame:
nextTick(() => {
window.requestAnimationFrame(() => {
if (ionContent.value) {
ionContent.value.$el.scrollToBottom()
}
});
});
If this feels like too much nesting for you, you can create a method that returns a promise based on rAF:
methods: {
rAF: function() {
return new Promise(r => window.requestAnimationFrame(r));
}
}
Then it's a matter of ensuring promises returned by both nextTick() and rAF() are resolved before scrolling:
await nextTick();
await this.rAF();
if (ionContent.value) {
ionContent.value.$el.scrollToBottom();
}

Vue.js component not loading/rendering data when called via URL or F5

I have a Vue.js SPA with some pages that display data from a backend. When I navigate the pages via the navbar, everything works fine, components and data are loaded.
When I'm on the page, e.g. localhost:8080/#/mypage and press F5, the data doesn't get loaded / rendered. Same goes for when I directly navigate to the page via the address bar.
The data gets loaded in this function:
async beforeMount() {
await this.initializeData();
}
I've tried to call the method in every lifecycle hook, i.e. created, beforeCreated, mounted etc...
In the mounted lifecycle hook I'm setting a boolean property to true, so that the table is only rendered when the component is loaded (done with v-if).
mounted() {
this.componentLoaded = true;
}
Not sure if this is important, but I've tried it with or without and it doesn't work.
I would really appreciate it if somebody knew whats happening here.
EDIT:
this.applications is a prop and contains multiple applications which contain instances. I want to add some variables from the backend to each application.
console.log(1) gets printed
console.log(2) does not
initializeData: function () {
let warn = 0;
console.log("1");
this.applications.forEach(async application => {
const instance = application.instances[0];
console.log("2");
let myData = null;
try {
const response = await instance.axios.get('url/myData');
myData = response.data;
} catch (err) {
}
let tmpCount = 0;
let tmpFulfilled = 0;
myData.forEach(ba => {
if(!ba.fulfilled){
warn++;
application.baAllFulfilled = false;
}else {
tmpFulfilled++;
}
tmpCount++;
})
console.log("3");
// Assign values
this.baTotalWarnings = warn;
application.baAnzahl = tmpCount;
application.baFulfilled = tmpFulfilled;
this.componentLoaded = true;
}
Try removing the async and await keywords from your beforeMount, and remove this.componentLoaded from mounted. Set it instead in the then block (or after await) in your initializeData method. I'm not sure Vue supports the async keyword in its lifecycle methods.
Something like this:
beforeMount() {
this.initializeData(); // start processing the method
}
methods: {
initializeData() {
callToBackend().then(() => {
this.componentLoaded = true // backend call ready, can now show the table
})
}
}

Which Lifecycle hook after axios get but before DOM render

I'm trying to render my DOM, dependent on some data I'm returning from an axios get. I can't seem to get the timing right. The get is in the created hook, but there is a delay between the get and actually receiving the data. Basically if there is info in seller_id then I need to show the cancel button, otherwise don't. Here is my code:
this is in my created hook
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
and then this is the logic to show or hide the button. I've tried created, mounted, beforeUpdate, and updated all with no luck. I've also tried $nextTick but I can't get the timing correct. This is what I have currently:
beforeUpdate: function () {
// this.$nextTick(function () {
function sellerIdNotBlank() {
var valid = this.seller_id == '';
return !valid;
}
if(sellerIdNotBlank()){
this.show_cancel_button = true;
}
// })
},
First, it is pointless to get your data from backend and try to sync with Vue.js lifecycle methods. It never works.
Also, you should avoid beforeUpdate lifecycle event. It is often a code smell. beforeUpdate is to be used only when you have some DOM manipulations done manually and you need to adjust them again before Vue.js attempt to re-render.
Further, show_cancel_button is a very good candidate for a computed property. Here is how component will look:
const componentOpts = {
data() {
return {
seller_id: '',
// ... some more fields
};
},
created() {
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
},
computed: {
show_cancel_button() {
return this.seller_id !== '';
}
}
}

Vue redirecting. My code is note redirecting to the page i want to

it is not redirecting to the page. What am I doing wrong here?
this method is getting all the data from the form and adding dynamic values to date and token.
methods:{
addToApi() {
console.log(this.data);
crypto.randomBytes(20,(err,buff)=>{
let token = buff.toString('hex');
let sub = this.data.sub;
let date = Date.now() + 3600000;
let newData = {
sub: this.data.sub,
token: token,
date: date
}
axios.post("http://localhost:3000/api/qrData",newData)
.then((res)=>{
//after sending the data to back end it should redirect to this route but it doesn't redirect
router.go('/');
})
.catch((err)=>{
console.log(err);
})
})
}
}
}
check Vue Router: Programing Navigation,
As the tutorial said:
router.go(n)
This method takes a single integer as parameter that indicates by
how many steps to go forwards or go backwards in the history stack,
similar to window.history.go(n).
For your use case, you should use router.push
The main problem is that you refer to this variable which in this certain case has different values over the scopes. The common solution is to create a new variable storing this as I did it in the first line of addToApi() method.
Getting to the point, variable router is undefined in the method's scope. You should use this.$router which refers to the global router object. And as this variable loses its value in then function (change of scope), you want to use vm variable I defined. So your code should finally look like this below:
As pointed in the comment, arrow functions do not carry this variable.
If you want to redirect to a particular route you should use $router.push method instead. (go method was used in Vue Router's older versions and renamed to push in 2.x)
methods: {
addToApi() {
crypto.randomBytes(20, (err, buff) => {
let token = buff.toString('hex');
let sub = this.data.sub;
let date = Date.now() + 3600000;
let newData = {
sub: this.data.sub,
token: token,
date: date
}
axios.post("http://localhost:3000/api/qrData", newData)
.then((res) => {
//after sending the data to back end it should redirect to this route but it doesn't redirect
this.$router.push('/');
})
.catch((err) => {
console.log(err);
})
})
}
}
}

Get item from AsyncStorage in React Native

I have a list of companies in React Native.
When I click on one of those companies I get the url of the API that is used for selected company. Then I store it to AsyncStorage and then I show the login screen. The function is as follows:
selectCompany(data_url, e) {
AsyncStorage.setItem("data_url", JSON.stringify(data_url), () => this.props.login());
}
Then on login page if I click on sign in button I go to the onLogin function, the function is as follows:
onLogin: function() {
fetch(data.url + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
},
And data.url comes from data.js file, and I try to get url from the data.js file as follows:
let data_url = AsyncStorage.getItem("data_url").then(json => JSON.parse(json));
module.exports = {
url: data_url,
.....
}
But it doesn't work. Any advice?
AsyncStorage is async, therefore data_url will not be defined until it's retrieved what its looking for, you would need to move the fetch into the promise thats returned from the get so it will run it once it's done getting the data. This might be one way you tackle it:
const data_url = () => AsyncStorage.getItem("data_url"); //change this into a function
module.exports = {
url: data_url,
.....
}
now inside your component...
onLogin: function() {
data.url().then((url) => {
fetch(JSON.parse(url) + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
});
},
AsyncStorage.getItem is a promise and needs to await for response rather than accessing direct and the function calling it should be defined as async. Here is an example to retrieve from AsyncStorage..
export async function getAccessKey(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
return accessToken;
}