I'm using the semantic-ui modal to allow users to insert data. It has an onApprove callback which allows you to return false to keep the modal open if there are any problems. My data is inserted into a DB, which returns false if there's any error. What's the best way of keeping the modal open if there's an error during this async operation?
Here's my code (coffeescript):
$('#verification-modal')
.modal('setting', {
detachable: false,
onApprove: validateVerificationForm
closable: false
})
validateVerificationForm = () ->
formData = $('.form').serializeArray()
formatted = format($formData);
ID_Details.insert(formatted, (errs, id) ->
if errs
false
else
true
Obviously the anonymous function is returning true/false into the context of the function. What's the best way to return it to the modal?
You can use a local reactive variable:
var data = new ReactiveDict();
Template.modalTemplate.created = function() {
data.set('isError', false);
};
Template.modalTemplate.helpers({
isError: function() {
return data.get('isError');
},
});
var yourMethodWithAsync = function() {
...
async(..., function(error) {
if(error) {
data.set('isError', true);
}
...
});
};
Related
I'm using a Facebook login and I'm showing progress loading for the user until I get a response back from Facebook for authentication.
But I used to hide the progress bar like this.progress = false but this variable is undefined inside the window function.
My code :
initFacebook() {
this.progress=true
window.fbAsyncInit = function() {
window.FB.init({
appId: "MY-APP-ID", //You will need to change this
cookie: true, // This is important, it's not enabled by default
version: "v2.6",
status: false,
});
window.FB.login(function(response) {
if (response.status === 'connected'){
window.FB.api('/me?fields=id,name,email', function(response) {
console.log( response) // it will not be null ;)
})
} else {
console.log("User cancelled login or did not fully authorize.")
}
},
{scope: 'public_profile,email'}
);
this.progress = false
console.warn(this.progress)
};
},
I'm unable to set this.progress = false after getting all responses from Facebook.
I get an error while I console.log(this.progress) variable.
Error :
Login.vue?7463:175 undefined
How can I set this.progress variable to false once the authentication checks are complete?
Try converting all function() calls into arrow function calls () =>
The problem is that a function() will break the global vue scope. So vue this is not available within a function() call, but it is available within an arrow function () => {}
In a block scope (function() { syntax), this is bound to the nested scope and not vue's this instance. If you want to keep vues this inside of a function, use an arrow function (ES6) or you can have const that = this and defer the global this to that in a regular function() { if you prefer it this way.
Try using this code converted with arrow functions and see if it works:
initFacebook() {
this.progress=true
window.fbAsyncInit = () => {
window.FB.init({
appId: "MY-APP-ID", //You will need to change this
cookie: true, // This is important, it's not enabled by default
version: "v2.6",
status: false,
});
window.FB.login((response) => {
if (response.status === 'connected'){
window.FB.api('/me?fields=id,name,email', (response) => {
console.log( response) // it will not be null ;)
})
} else {
console.log("User cancelled login or did not fully authorize.")
}
},
{scope: 'public_profile,email'});
this.progress = false
console.warn(this.progress)
};
},
I know this because I just had the same problem :-) see here:
Nuxt plugin cannot access Vue's 'this' instance in function blocks
I have a AngularJS web application, I'm trying to upload a file to a server and while the upload is complete, I have to update ng-grid with the last uploaded file's entry. The following is my grid html,
<div class="gridholder" data-ng-grid="viewmodel.gridOptions">
</div>
The following is my controller logic.
vm.gridOptions = {
data: 'gridData',
enableColumnResize: true,
enablePaging: true,
columnDefs: [
{ field: 'FileName', displayName: 'File Name', width: 250 }
{ field: 'UploadedDate', displayName: 'Uploaded Date'}
],
multiSelect: false,
enableSorting: true,
showFooter: true,
};
The requirement is that I show the progress of file upload and the entire application to be responsive when upload is in progress, I have achieved this but my ng-grid not is updating in a particular scenario.
If I remain in the same page until the file is uploaded and the response comes, the grid is refreshing but when I move to another page of my application and come back to the file upload page, and the response comes after, my grid is not getting refreshed.
This is my file upload js code,
var data = new FormData();
data.append('file', file);
var xhrRequest = Factory.uploadFileRequest('UploadFile');
xhrRequest.upload.addEventListener("progress", progressHandler, false);
xhrRequest.onreadystatechange = function (e) {
};
xhrRequest.onload = function (e) {
if (JSON.parse(e.currentTarget.responseText).Success == true) {
$timeout(function () {
$scope.LoadGrid();
//showing success message here
}, 2000);
}
else
{
//showing error message here
}
};
xhrRequest.onerror = function (e) {
//showing error message here
};
xhrRequest.send(data);
$scope.LoadGrid = function () {
Factory.callGet("Files").then(function (d) {
$scope.gridData = d.data;
}
$scope.totalItems = $scope.gridData.length;
}, function error(err) {
//Error Message
});
}
gridData is my data-ng-grid value. I'm calling my LoadGrid method inside a $timeout already but still the grid is not refreshing with latest data. Any help would be much appreciated.
Possible Problem
You implemented upload logic inside the controller. When you switch to another view, angularjs destroys your controller and therefore no one listens on file upload response.
Possible solution:
1) Use a service (or Factory) kind of singleton to manage upload process there.
For example MyService.upload(data).then(function (response) {/**/});
2) By default MyService.upload(data) returns a promise on a regular basis but also stores the result inside the Service, for example, upload_results:
app.service('MyService',['$q',function ($q) {
var self = this;
var upload_results = [];
self.upload = function (_data) {
return // <YOUR_PROMISE>
.then(function (response) {
upload_results.push({
id: new Date().getTime(),
data: response.data
})
}
, function (error) {
console.error(error);
return $q.reject(error);
});
};
self.getResults() = function(){
return upload_results;
}
self.resetResults() = function(){
upload_results = [];
}
}
When you initialize the controller on start or go back to the previous controller, you ask the service if it has something for you:
var results = MyService.getResults();
if(results.length > 0){
$scope.gridData = results[0].data; // or use timestamp to manage it
MyService.resetResults();
}
Hope it will give you some insight,
In a JS component, I have several methods that respond to, for example, click events, and then fire off ajax requests. In order to prevent the same ajax request firing off several times before the first one finishes (in case of multiple clicks on the same button), I usually set a flag. So I have a variable in my component called working (which initially is false). When something is clicked I set it to true, and when the ajax request is done I set it back to false. If working === true I block any ajax requests.
The problem is, if working === true, ALL actions are blocked in the component, so no two things can be clicked at the same time. So a user can't click "save" until his "like" click right before is done.
In the code example, respondToClickB would be blocked until respondToClickA is resolved.
How can I handle this problem better?
Thank you in advance!
export default {
data: function() {
return {
working: false
}
},
methods: {
respondToClickA: function() {
let self = this;
if(!self.working)
{
self.working = true;
axios.get('/ajax')
.then(function(response){
self.working = false;
});
}
},
respondToClickB: function() {
let self = this;
if(!self.working)
{
self.working = true;
axios.get('/ajax')
.then(function(response){
self.working = false;
});
}
}
}
}
A nice usage case for Set: make self.working to be a Set object, and add/remove values to it.
Set is like an array, but it doesn't have order.
export default {
data: function() {
return {
working: new Set()
}
},
methods: {
respondToClickA: function() {
let self = this;
if(!self.working.has('a'))
{
self.working.add('a')
axios.get('/ajax')
.then(function(response){
self.working.delete('a');
});
}
},
respondToClickB: function() {
let self = this;
if(!self.working.has('b'))
{
self.working.add('b');
axios.get('/ajax')
.then(function(response){
self.working.delete('b');
});
}
}
}
}
In previous questions I have seen that a nice way to wait for the url to change is to use:
browser.wait( function() {
return browser.getCurrentUrl().then(function(url) {
return /myURL/.test(url);
});
}, 10000, "url has not changed");`
But I am trying to have a method that I can pass myURL as a variable (in case I need to use it with other sites) and is not working.
I am trying this in my Page Object file:
this.waitUrl = function(myUrl) {
browser.wait( function(myUrl) {
return browser.getCurrentUrl().then(function(url, myUrl) {
return myUrl.test(url);
});
}, 10000, "url has not changed");
};
Any ideas if this is even possible and how to do it if so?
Update (July 2016): with Protractor 4.0.0 you can solve it with urlIs and urlContains built-in Expected Conditions.
Original answer:
Don't pass myUrl inside the then function, it is available from the page object function scope:
browser.wait(function() {
return browser.getCurrentUrl().then(function(url) {
return myUrl.test(url);
});
}, 10000, "url has not changed");
I would though define it as an Expected Condition:
function waitUrl (myUrl) {
return function () {
return browser.getCurrentUrl().then(function(url) {
return myUrl.test(url);
});
}
}
So that you can then use it this way:
browser.wait(waitUrl(/my\.url/), 5000);
For those that want an example for Protractor 4.0.0 through 5.3.0
You can use "ExpectedConditions" like so...
var expectedCondition = protractor.ExpectedConditions;
// Waits for the URL to contain 'login page'.
browser.wait(expectedCondition.urlContains('app/pages/login'), 5000);
If you want to validate this with an e2e test.
it('should go to login page', function() {
loginPage.login();
const EC = protractor.ExpectedConditions;
browser.wait(EC.urlContains('app/pages/login'), 5000).then(function(result) {
expect(result).toEqual(true);
});
});
This one is wired.
This fires from a grid toolbar button click:
// fires when the client hits the add attachment button.
onAddAttachmentClick: function () {
var uploadAttachmentsWindow = new Nipendo.ProformaInvoice.Attachment.UploadWindow({
invoice: this.invoice,
maxFileSizeInMB: this.maxFileSizeInMB
});
uploadAttachmentsWindow.on('uploadcomplete', function (win, message) {
if (message.msg !== 'success') {
return;
}
win.close();
var store = this.getStore();
store.setBaseParam('useCache', false);
store.load();
this.fireEvent(
'attachmentuploaded',
this.invoice.ProformaInvoiceNumber,
this.invoice.VendorSiteID,
this.invoice.CustomerSiteID);
}, this);
uploadAttachmentsWindow.show();
} // eo onAddAttachmentClick
This is what happens on the uploadcomplete event:
this.uploadBtn.on('click', function () {
var form = this.uploadForm.getForm();
if (!form.isValid()) {
return;
}
form.submit({
url: 'XXX.ashx',
waitMsg: Nipendo.Localization.UploadingAttachment,
scope: this,
success: function (form, action) {
this.fireEvent('uploadcomplete', this, {
msg: 'success',
response: action.response
});
},
failure: function (form, action) {
switch (action.failureType) {
case Ext.form.Action.CLIENT_INVALID:
this.fireEvent('uploadcomplete', this, {
msg: 'Form fields may not be submitted with invalid values'
});
break;
case Ext.form.Action.CONNECT_FAILURE:
this.fireEvent('uploadcomplete', this, {
msg: 'Ajax communication failed'
});
break;
case Ext.form.Action.SERVER_INVALID:
Ext.Msg.alert(action.result.title, action.result.message);
this.fireEvent('uploadcomplete', this, {
msg: action.result.message
});
break;
}
}
});
}, this);
On IE 8 I am getting this error in the debugger:
I have no idea what object is missing... from my check they are all defined.
Any idea anyone?
Notice that I have an event firing from a listener (I am suspecting it to be the root of the problem).
It is hard to see but the error occuers in ext-all.js in the fire method.
I have found the answer in : https://stackoverflow.com/a/3584887/395890
Problem was I was listing to events is 2 different windows, that is not possible in Ext.
What I have done to solv it was to call the opner window from the pop up window to notify about changes.