I am getting response from server, but, I have to display last 5 indexes of data. So, I tried to do with some logic like if indexes is greater than 4, Then do filter. ReturnsData has the response.
But, The data is not persistent always, Some times that data is only 1 or 2 indexes, So, my logic getting wrong and UI showing empty, Even I tried to set else condition in react native, But, nothing working.
someAction = () => {
let lastFiveYearsData = [];
if (ReturnsData.length > 4) {
lastFiveYearsData = ReturnsData.sort((a, b) => a.year - b.year);
filteredArray = lastFiveYearsData.slice(Math.max(lastFiveYearsData.length - 5, 1));
} else {
this.filteredArray = ReturnsData;
}
filteredArray.map((item, index) => {
//do some work here
}
}
console.log ('filteredArray', filteredArray)
filteredArray showing undefined
Even I tried with set state which is not working and showing undefined filteredArray this is global defined.
How to fix this?
In your else condition you are assigning the data to the this.filteredArray instead of filteredArray.
Related
I just want to ask how to properly conditional testing? I have this code here
cy.get('[data-slug="add-to-any"] > .plugin-title > strong').then(($slug) => {
if (expect($slug).not.to.exist){
//It will passed
}else if (expect($slug).to.exist){
cy.get('#deactivate-add-to-any').should('not.exist')
}
I assert the element to not.to.exist, but it gives me this error
Expected to find element: [data-slug="add-to-any"] > .plugin-title > strong, but never found it.
I am really lost what assertions I need to use.
The ideal way (if it works in your scenario) is to shift the last selector inside the .then()
cy.get('[data-slug="add-to-any"] > .plugin-title')
.then(($pluginTitle) => {
const $slug = $pluginTitle.find('strong'); // this finds with jQuery
// which won't fail the test
// if not found
if ($slug.length === 0) { // not found
} else {
cy.get('#deactivate-add-to-any').should('not.exist')
}
})
It's not 100% fool-proof, if $slug is loaded asynchronously (say via fetch) it won't be there immediately and the test might pass when in fact the $slug turns up 100 ms after the test runs.
You need to understand the way the app works to really be sure.
Cypress docs show this pattern, using <body> as the "stable" element (always present after page load).
cy.get('body').then($body => {
const slug = $body.find('[data-slug="add-to-any"] > .plugin-title > strong')
if ($slug.length) {
...
It's less than ideal because the page might have <body> but still be fetching elements inside it.
Best practice IMO is to try the immediate parent element of the conditional one. If that is also conditional, move up the element tree until you find an element that is stable/present at that point in you test.
Or add a guard condition that waits for page fetch to complete. A cy.intercept() is useful for that, or even just this
cy.get('[data-slug="add-to-any"] > .plugin-title')
.should('be.visible') // retries until .plugin-title is showing
.then(($pluginTitle) => {
const $slug = $pluginTitle.find('strong')
if ($slug.length === 0) {
...
Simple example
cy.get("body").then($body => {
if ($body.find('[data-slug="add-to-any"] > .plugin-title').length > 0) {
cy.get('[data-slug="add-to-any"] > .plugin-title').then($title => {
if ($title.is(':visible')){
//you get here only if it EXISTS and is VISIBLE
}
});
} else {
//you get here if the it DOESN'T EXIST
cy.get('#deactivate-add-to-any').should('not.exist')
}
});
When writing custom functions to be used in spreadsheet cells, the default behavior for a sheet is to recalculate on edits, i.e. adding column or rows will cause a custom function to update.
This is a problem if the custom function calls a paid API and uses credits, the user will consuming API credits automatically.
I couldn't figure out a way to prevent this, so I decided to use the UserCache to cache the results for an arbitrary 25 minutes, and serve it back to the user should they happen to repeat the same function call. It's definitely not bulletproof but it's better than nothing I suppose. Apparently the cache can hold 10mb, but is this the right approach? Could I be doing something smarter?
var _ROOT = {
cache : CacheService.getUserCache(),
cacheDefaultTime: 1500,
// Step 1 -- Construct a unique name for function call storage using the
// function name and arguments passed to the function
// example: function getPaidApi(1,2,3) becomes "getPaidApi123"
stringifyFunctionArguments : function(functionName,argumentsPassed) {
var argstring = ''
for (var i = 0; i < argumentsPassed.length; i++) {
argstring += argumentsPassed[i]
}
return functionName+argstring
},
//Step 2 -- when a user calls a function that uses a paid api, we want to
//cache the results for 25 minutes
addToCache : function (encoded, returnedValues) {
var values = {
returnValues : returnedValues
}
Logger.log(encoded)
this.cache.put(encoded, JSON.stringify(values), this.cacheDefaultTime)
}
//Step 3 -- if the user repeats the exact same function call with the same
//arguments, we give them the cached result
//this way, we don't consume API credits as easily.
checkCache : function(encoded) {
var cached = this.cache.get(encoded);
try {
cached = JSON.parse(cached)
return cached.returnValues
} catch (e) {
return false;
}
}
}
Google Sheets already caches the values of custom functions, and will only run them again when either a) the inputs to the function have changed or b) the spreadsheet is being opened after being closed for a long time. I'm not able to replicate the recalculation you mentioned when adding and removing columns. Here's a simple example function I used to test that:
function rng() {
return Math.random();
}
Your approach of using an additional cache for expensive queries looks fine in general. I'd recommend using the DocumentCache instead of the UserCache, since all users of the document can and should see the same cell values.
I'd also recommend a more robust encoding of function signatures, since your current implementation is able to distinguish between the arguments [1, 2] and [12]. You could stringify the inputs and then base64 encode it for compactness:
function encode(functionName, argumentsPassed) {
var data = [functionName].concat(argumentsPassed);
var json = JSON.stringify(data);
return Utilities.base64Encode(json);
}
I have a mobile app that receives a push notification, when it does, I know there is a row that that needs to be updated. I have tried two methods for retrieving just that one:
var row = data_source.get(<id>);
row.dirty = true;
data_source.sync();
This tries to fire an UPDATE, which I could shoe-horn into doing what I want, but it's conceptually wrong.
The other option that I have tried:
data_source.read( { id : <id> } );
Which fires off a READ request, with the ID in options.data. When I try to hook this up, kendo complains about not getting an array back, and when I make the response into an array, it doesn't seem to work either.
How am I supposed to do this? GET it outside the context of the DataSource, and set the relevant parts, and then set the dirty bit to false?
Kendo doesn't do single row read out of the box. If its not feasible to do full read(), you have to do what you proposed. Try following :
1) make your server side read method to accept requests with or without id, without to read all items, with to read your one row.
2) use parameter map together with isPushNotificaton flag to control the read request params
parameterMap: function(data, type) {
if (type == "read") {
if (isPushNotificaton)
return { id : yourId };
else
return { id : 0}; // get all
}
}
3) use requestEnd to decide how to deal with read result
requestEnd: function(e) {
var type = e.type;
if (e.type == 'read') {
// examine the response(by count/add additional flags into response object)
var isFullReadResponse = ...;
if (!isFullReadResponse) {
// DIY
e.preventDefault();
UpdateSingleRow(response, datasource.data());
}
}
}
4) Implement UpdateSingleRow method as you proposed - .."and set the relevant parts, and then set the dirty bit to false", (Refresh a single Kendo grid row)
So, I took some code from this Microsoft provided Example which allows me to use the jquery validate unobtrusive library to parse validation error message returned from my server and display them in the UI. They have a video demonstrating this. So, here is the piece of Javascript code I'm using:
$.validator.addMethod("failure", function () { return false; });
$.validator.unobtrusive.adapters.addBool("failure");
$.validator.unobtrusive.revalidate = function (form, validationResult) {
$.removeData(form[0], 'validator');
var serverValidationErrors = [];
for (var property in validationResult) {
//var elementId = property.toLowerCase();
var item = form.find('#' + property);
if (item.length < 1) { item = form.find('#' + property.replace('.', '_')); }
serverValidationErrors.push(item);
item.attr('data-val-failure', validationResult[property].join(', '));
jQuery.validator.unobtrusive.parseElement(item[0]);
}
form.valid();
$.removeData(form[0], 'validator');
$.each(serverValidationErrors, function () {
this.removeAttr('data-val-failure');
jQuery.validator.unobtrusive.parseElement(this[0]);
});
};
So then after a AJAX form post in the handle error function I would do something like this:
$.validator.unobtrusive.revalidate(form, { 'PhysicalAddress.CityName': ['You must select a valid city'] });
Where PhysicalAddress.CityName is the name of my viewmodel property and html input field. So, it knows to put the validation message next to the correct html element.
This works 1 time. Then when they hit submit again and my code calls the unobtrusive.revalidate method again.. it doesnt work. It only shows the validation message one time then after that the validation message disappears for good.
Does anyone have any idea as to why this might be happening?.. I stepped through the revalidate method and no errors were thrown and everything seems like it should work.. but the unobtrusive library for some reason is not re-binding the validation error message.
Thanks
Probably this behavior depends on a known problem of the jQuery validation plugin: dynamically adding new validation rules for elements works just once! Further attempts are rejected because the plugin think they are a duplicated attempt to define the already defined rules.
This is the reason why the $.validator.unobtrusive.parse doesn't work when you add newly created content (when for instance you add a new row to a collection of items). There is a patch for the $.validator.unobtrusive.parse that you might try to apply also to the revalidate function....but it is better to rewrite it from scratch in a different way. The revalidate function usse the validation plugin just to place at the right place all validation errors, then it tries to reset the state of the validation plugin. However, deleting the validator object from the form is not enough to cancel all job done since there is another object contained in the form.data('unobtrusiveValidation'), where form is a variable containing the form being validated...This data are not reset by the revalidate function...and CANNOT be reset since resetting them would cause the cancellation of ALL client side validation rules.
Maybe this problem has been solved in the last version of the validation plugin, so try to update to the last version with nuget.
If this doesn't solve your issue I can pass you an analogous function implemented in a completely different way(it mimics what the server does on the server side to show server side errors). It will be contained in the upcoming version of the Mvc Controls toolkit. However, if you give me a couple of days (I will be very busy for 2 days) I can extract it from there with its dependencies so you can use it. Let me know if you are interested.
Below the code I promised. It expects an array whose elements are:
{
id:id of the element in error
errors:array of strings errors associated to the element
}
It accepts several errors for each element but just display di first one for each element
id is different from the name because . [ ] an other special char are replaced by _
You can transform name into id on the sever with
htmlName.Replace('$', '_').Replace('.', '_').Replace('[', '_').Replace(']', '_');
or on the client in javascript with:
name.replace(/[\$\[\]\.]/g, '_');
function remoteErrors(jForm, errors) {
//////////
function inner_ServerErrors(elements) {
var ToApply = function () {
for (var i = 0; i < elements.length; i++) {
var currElement = elements[i];
var currDom = $('#' + currElement.id);
if (currDom.length == 0) continue;
var currForm = currDom.parents('form').first();
if (currForm.length == 0) continue;
if (!currDom.hasClass('input-validation-error'))
currDom.addClass('input-validation-error');
var currDisplay = $(currForm).find("[data-valmsg-for='" + currElement.name + "']");
if (currDisplay.length > 0) {
currDisplay.removeClass("field-validation-valid").addClass("field-validation-error");
replace = $.parseJSON(currDisplay.attr("data-valmsg-replace")) !== false;
if (replace) {
currDisplay.empty();
$(currElement.errors[0]).appendTo(currDisplay);
}
}
}
};
setTimeout(ToApply, 0);
}
/////////
jForm.find('.input-validation-error').removeClass('input-validation-error');
jForm.find('.field-validation-error').removeClass('field-validation-error').addClass('field-validation-valid');
var container = jForm.find("[data-valmsg-summary=true]");
list = container.find("ul");
list.empty();
if (errors.length > 0) {
$.each(errors, function (i, ival) {
$.each(ival.errors, function (j, jval) {
$("<li />").html(jval).appendTo(list);
});
});
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
inner_ServerErrors(errors);
setTimeout(function () { jForm.find('span.input-validation-error[data-element-type]').removeClass('input-validation-error') }, 0);
}
else {
container.addClass("validation-summary-valid").removeClass("validation-summary-errors");
}
}
function clearErrors(jForm) {
remoteErrors(jForm, []);
}
I'm trying to make two Ajax calls to get data to populate different bits of a web page, and as you'll already know, only the second happens.
So I thought I'd do this:
callAjax1('a'); callAjax2('b');
function callAjax1(data) {
ajax(data);
}
function callAjax2(data) {
ajax(data);
}
function ajax(data) {
// calls XMLHttpRequestObject etc
}
The idea was that instead of calling ajax() twice, now, I'd have two independent instances of ajax that would run independently.
It works .. but only if I put in an alert at the top of ajax() to let me know I've arrived.
So I'm thinking that alert gives the first request time to finish before the second is called. Therefore, I've not managed to separate them properly into separate instances. Is that not possible?
What am I missing?
All the best
J
UPDATE:
I'm thinking this, do I stand a chance?
tParams = new Array (2); // we intend to call ajax twice
tParams[0] = new Array('ajaxGetDataController.php', 'PROJECT', 'id');
tParams[1] = new Array('ajaxGetFileController.php', 'FILE', 'projectId');
<select name='projectSelector' onchange=\"saveData(tParams, this.value);\">\n";
// gets called, twice
function saveData(pParams, pData) // pParams are: PageToRun, Table, Field
{
if (XMLHttpRequestObject)
{
tPage = pParams[0][0]+'?table='+pParams[0][1]+'&pField='+pParams[0][2]+'&pData='+pData;
XMLHttpRequestObject.open('GET', tPage);\n
XMLHttpRequestObject.onreadystatechange = callAjax(pParams, pData);
XMLHttpRequestObject.send(null);
}
}
function callAjax(pParams, pData)
{
if (XMLHttpRequestObject.readyState == 4 && XMLHttpRequestObject.status == 200)
{
var tReceived = XMLHttpRequestObject.responseXML;
options = tReceived.getElementsByTagName('option'); // fields and their values stored in simplest XML as options
popForm(options, pParams[0][1]); // goes off to use the DOM to populate the onscreen form
pParams.shift(); // cuts off pParams[0] and moves all elements up one
if (pParams.length>0)
{
saveData(pParams, pData);
}
}
}
I would create a ready state variable for the AJAX function:
function ajax(data) {
readyState = false;
// calls XMLHttpRequestObject etc
}
And then check for the ready state before executing the second call:
function callAjax2(data) {
if(readyState == true) {
ajax(data);
readyState = true;
}
}
And make sure to change the readyState back to false after the AJAX calls have executed. This will ensure the first AJAX call has finished executing before the second one tries to fire.