backbone view in router lost after creation - variables

When I try to associate my router's public variable this.currentView to a newly created view, the view gets lost, the public variable is null instead of containing the newly created view.
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
});
});
});
alert(this.currentView); //null

The fetch() calls you do are firing asynchronous AJAX requests, meaning the code in your done handlers are not going to be executed untill the server calls return. Once you've executed user.fetch() the browser will fire off a request and then continue running your program and alert this.currentView without waiting for the requests to finish.
The sequence of events is basically going to be
call user.fetch()
alert this.currentView
call watchListsCollection.fetch()
call loggedUser.fetch()
set the value of self.currentView
You will not be able to see the value of your currentView before the last server request have completed.
If you change your code to
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
alertCurrentView();
});
});
});
function alertCurrentView() {
alert(this.currentView);
}
You should see the correct value displayed. Now, depending on what you intend to use your this.currentView for that might or might not let you fix whatever issue you have, but there's no way you're not going to have to wait for all the requests to complete before it's available. If you need to do something with it straight away you should create your UserView immediately and move the fetch() calls into that view's initialize().

fetch() is asynchronous, but you check your variable right after you've started your task. Probably these tasks, as they supposed to be just reads, should be run in parallel. And forget making a copy of this, try _.bind instead according to the Airbnb styleguide: https://github.com/airbnb/javascript
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
Promise.all(tasks).then(_.bind(function() {
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}, this));
or using ES6 generators:
function* () {
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
yield Promise.all(tasks);
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}

Related

useInfiniteScroll utility of Vueuse is fetching same items again

Here is a reproducable stackblitz -
https://stackblitz.com/edit/nuxt-starter-jlzzah?file=components/users.vue
What's wrong? -
My code fetches 15 items, and with the bottom scroll event it should fetch another 15 different items but it just fetches same items again.
I've followed this bottom video for this implementation, it's okay in the video but not okay in my stackblitz code:
https://www.youtube.com/watch?v=WRnoQdIU-uE&t=3s&ab_channel=JohnKomarnicki
The only difference with this video is that he's using axios while i use useFetch of nuxt 3.
It's not really a cache issue. useFetch is "freezing" the API URL, the changes you make to the string directly will not be reliably reflected. If you want to add parameters to your API URL, use the query option of useFetch. This option is reactive, so you can use refs and the query will update with the refs. Alternatively, you can use the provided refresh() method
const limit = ref(10)
const skip = ref(20)
const { data: users, refresh: refreshUsers } = await useFetch(
'https://dummyjson.com/users',
{
query:{
limit,
skip
}
}
);
//use the data object directly to access the result
console.log(users.value)
//if you want to update users with different params later, simply change the ref and the query will update
limit.value = 23
//use refresh to manually refresh the query
refreshUsers()
This results in a first API call http://127.0.0.1:8000/api/tasks?limit=10&skip=20 and then a second with the updated values http://127.0.0.1:8000/api/tasks?limit=23&skip=20
You can leave the cache alone, as it is just a workaround, and will not work reliably.
[Updated] The useFetch() documentation is now updated as described below.
The query option is not well documented yet, as discussed in this nuxt issue. I've created a pull request on nuxt/framework to have it reflected in the documentation. Please see a full explanation below:
Using the query option, you can add search parameters to your query. This option is extended from unjs/ohmyfetch and is using ufo to create the URL. Objects are automatically stringified.
const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
query: { param1, param2: 'value2' }
})
This results in https://api.nuxtjs.dev/mountains?param1=value1&param2=value2
Nuxt3's useFetch uses caching by default. Use initialCache: false option to disable it:
const getUsers = async (limit, skip) => {
const { data: users } = await useFetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`,
{
initialCache: false,
}
);
//returning fetched value
return users.value.users;
};
But you probably should use plain $fetch instead of useFetch in this scenario to avoid caching:
const getUsers = async (limit, skip) => {
const { users } = await $fetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`
);
//returning fetched value
return users;
};

Events with Cytoscapejs compound-drag-and-drop extension

I am using cytoscapejs and the extension compound-drag-and-drop. My aim is to shoot an event to my database when I drag a node and drop it over another. I am taking the id (of my future child node) with cdndtapstart or cdndout (when it is inside a compound node) and event.target._private.data.id, and then, with cdnddrop I call dropSibling._private.data.id, and with this data I can do my ajax call.
The problem that I have is that those events are acumulating one over another, if I drag the node once is fine, the second time that I drag it, it will produce two ajax call, the third time will give me three calls. Is there a way to avoid this or should it be reported as a bug?
cy.on('cdndout', 'node', function(event, dropTarget, dropSibling){
let type = event.target._private.data.type;
let id = event.target._private.data.id;
let gId = event.target._private.data;
if(type == 'device'){
cy.on('cdnddrop', function(event, dropTarget, dropSibling){
var typeDe = dropSibling._private.data
if(typeDe == undefined){
$.notify({message: err},{type: 'danger'});
createGraph()
} else {
let uuid = event.target._private.data.id;
let gId = dropSibling._private.data.id;
setResourceToGroup(uuid, gId, cb)
.then(reso => getAllResources())
.then(allReso => createGraph())
.catch(err => {
$.notify({message: parseLog(err)},{type: 'danger'});
createGraph()
})
}
})
}
})
So with this code, the ajax call that I have in the function setResourceToGroup, will be executed as many times as I drag the node. I am guessing I am not handling the events properly and they are accumulating... any help with that?
Events in cytoscape.js are quite simple, you bind an event and that event fires when the conditions of the binded event are met. The problems start, when you call the same binding operation twice or more. Then your event works normally, but the second you call that bind again, you have 2 events on your hand. And they will fire both at the same time! So how do you solve that?
Every time you call cy.bind() (cy.bind() is the same as cy.on()), you have to evaluate if that line can be executet more than once. If yes, then you have to do this:
cy.unbind('cdnout');
cy.bind('cdndout', 'node', function(event, dropTarget, dropSibling){
let type = event.target._private.data.type;
let id = event.target._private.data.id;
let gId = event.target._private.data;
if(type == 'device'){
cy.unbind('cdnddrop);
cy.bind('cdnddrop', function(event, dropTarget, dropSibling){
var typeDe = dropSibling._private.data
if(typeDe == undefined){
$.notify({message: err},{type: 'danger'});
createGraph()
} else {
let uuid = event.target._private.data.id;
let gId = dropSibling._private.data.id;
setResourceToGroup(uuid, gId, cb)
.then(reso => getAllResources())
.then(allReso => createGraph())
.catch(err => {
$.notify({message: parseLog(err)},{type: 'danger'});
createGraph()
});
}
});
}
});
By unbinding the event first, you can only have one event firing at once :)

Redux/saga: How to fire an action (put) inside a callback without channels (use sagas as normal generator functions)

I'm looking for a way to fire an action from inside a callback. I know this is not possible by default, but I'm looking for a way around. Channels are a bad solution in my case (for so far I see it).
The library I use is react-native-ble-plx. In that library, there is a function to start scanning: startDeviceScan(UUIDs, options, listener).
To keep this clean, I want to disconnect the start/stop scan from the listener (hence channels are out of the question).
Before I had this solution:
const onScanChannel = () => eventChannel(emit => {
BleService.startDeviceScan(..., ..., (peripheral) => {
emit(peripheral);
}
);
return () => {BleService.stopScan();}
});
The problem is that this connects the channel with the start and the stop of the scan. Which causes you to connect a lot of sagas because you need to start and stop the scanning from application logic (cancel the channel, setup again, start a new saga to listen to the new channel, etc)
What I had in mind is using sagas as normal generator functions:
const startScanSaga = function* () {
BleService.scan(..., ..., (peripheral) => {
const generator = deviceFoundHandler(peripheral);
generator.next();
generator.next();
});
};
const deviceFoundHandler = function* (peripheral) {
yield put(actions.deviceFound(peripheral));
};
That way, the saga for listening to the found-device-actions can just keep running. But, although the put is executed correctly, no take ever receives the action, which suggests that put does not work without the saga-logic behind the scenes.
Does someone know more about this? Or does someone has an alternative approach to realize this?
I managed to fix the problem by using middleware.run(saga, ...args).
I needed to export the sagaMiddleWare: export const sagaMiddleware = createSagaMiddleware();
import {sagaMiddleware} from '.../file-where-sagaMiddleWare-is-exported';
const startScanSaga = function* () {
BleService.scan((peripheral) => {
sagaMiddleware.run(deviceFoundHandler, peripheral);
});
};
const deviceFoundHandler = function* (peripheral) {
yield put(actions.deviceFound(peripheral));
};
Works like a charm =)

It is so slow when a callback function be called repeatedly in react native app

It will have a callback function called repeatedly to when I start a download task. But It will be so slow that it can't give feedback when touch a button.
Fileio.downloadFile(downloadData[downloadFileIndex].uri, '1.jpg',this.progressFunc.bind(this)).then((DownloadResult)=> {
if (DownloadResult.statusCode == 200) {
let nextDownloadFileIndex = this.props.downloadFileIndex + 1;
this.props.dispatch(DOWNLOADED_A_FILE({
downloadFileIndex:nextDownloadFileIndex,
progressNum:0
}))
}
}).catch((error)=> {
console.log(error)
})
This is my code and the callback function are as be folllowed
progressFunc(DownloadBeginCallbackResult) {
let progressNum = DownloadBeginCallbackResult.bytesWritten / DownloadBeginCallbackResult.contentLength;
if(progressNum<=0.99){
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:progressNum,
jobId:this.props.jobId
}));
}else{
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:0,
jobId:this.props.jobId
}));
}
}
I mean I can't get feedback immediately when I touch button. I think that it is because I have a callback function called repeatedly. so js can't handle so many tasks;
It does sound like JS thread is busy doing the request and not able to communicate back to UI thread. One thing you can try is to wrap you on press handler in an InteractionManager.runAfterInteractions(() => ...)
See https://facebook.github.io/react-native/docs/interactionmanager.html

Wait until page is fully loaded with selenium and intern js

I am trying to create a UI automation test with intern js , but i m getting problem on waiting until the page is fully loaded. My code starts searching for element before the page is loaded. Can some one help me on this.
My code:
define([
'intern!object',
'intern/chai!assert',
'Automation/ConfigFiles/dataurl',
'Automation/pages/login/loginpage',
'intern/dojo/node!fs',
'intern/dojo/node!leadfoot/helpers/pollUntil'
], function (registerSuite, assert,dataurl, LoginPage,fs,pollUntil) {
registerSuite(function () {
var loginPage;
var values;
return {
setup: function () {
var data = fs.readFileSync(loginpage, 'utf8');
json=JSON.parse(data);
console.log('###########Setting Up Login Page Test##########')
this.remote
.get(require.toUrl(json.locator.URL))
.then(pollUntil(this.remote.findById('uname').isDisplayed(),6000)// here i want to wait until page is loaded
.waitForDeletedByClassName('loading').end().sleep(600000)// here i want to wait until loading component is disappered
values = json.values;
loginPage = new LoginPage(this.remote,json.locator);
},
'successful login': function () {
console.log('##############Login Success Test############')
return loginPage
.login(values.unamevalue,values.pwdvalue)
},
// …additional tests…
};
});
});
I m trying to use pollUntil . But I m not sure weather I should use it or not.
pollUntil is a good thing to use here, but it doesn't look like you're actually waiting for polling to finish. Your setup method needs to return the command chain that includes pollUntil so that Intern will know it needs to wait, something like:
var setupPromise = this.remote
.get(require.toUrl(json.locator.URL))...
values = json.values;
loginPage = new LoginPage(this.remote, json.locator);
return setupPromise;
Alternatively, you could pass your LoginPage class the chain:
var setupPromise = this.remote
.get(require.toUrl(json.locator.URL))...
values = json.values;
loginPage = new LoginPage(setupPromise, json.locator);
In this case Intern won't wait for setup to complete, but your LoginPage code will implicitly wait for the setupPromise to complete before doing anything else. While this will work, the intent isn't as clear as in the previous example (e.g., that Intern should wait for some setup process to complete before proceeding).