I am using firebase and polymer to build an app, and I have a data I got using the firebase-query element. I want to reorder the data so that it displays the new data first, so I thought I could use the sort attribute on template tag to sort it by the key in a descending order, but it's not working as expected.
<firebase-query
id="questionQuery"
path="/questions"
limit-to-last="15"
data="{{questions}}">
</firebase-query>
And the template looks like this method:
<template is="dom-repeat" items="[[questions]]" sort="_computeSort" as="question"><p>[[question.body]]</p></template>
and inside my element definition, I have this:
_computeSort: function(a, b) {
console.log(a.__firebaseKey__);
if (a.__firebaseKey__ == b.__firebaseKey__) {
return 0;
}
return a.__firebaseKey__ > b.__firebaseKey__ ? -1 : 1;
},
This is not working. The output of the log to console is just bunch of undefindes, so the problem should be there, but how can I access each question's key?
Okay, I got an anser
$key is the expression to access the key generated by the firebase, so just change the sorting function to this:
_computeSort: function(a, b) {
if (a.$key == b.$key) {
return 0;
}
return a.$key > b.$key ? -1 : 1;
},
I actually had just copied the code from here Polymer 1.0: Sorting dom-repeat and that's how I ended up with code like _firebasekey__.
Related
I am trying to merge storefront filtering and sorting in my custom theme collections page in Shopify.
Both things work, but the 'sort_by' parameters are overwriting the filtering ones when these are multiple.
i.e of how the URL should look once filtering with two parameters (sizes XXS and XL) and sorting by ascending price:
../collections/new-arrivals?filter.v.option.size=XXS&filter.v.option.size=XL&sort_by=price-ascending
But this is what happens when sorting:
../collections/new-arrivals?filter.v.option.size=XXS&sort_by=price-ascending
Second filtering parameter gets overwritten by the sorting one.
Pasting below my code for the JS piece that triggers the sorting behaviour.
// sortby
$(function() {
Shopify.queryParams = {};
if(location.search.length) {
for(var aKeyValue, i = 0, aCouples = location.search.substr(1).split('&'); i < aCouples.length; i++) {
aKeyValue = aCouples[i].split('=');
if (aKeyValue.length > 1) {
Shopify.queryParams[decodeURIComponent(aKeyValue[0])] = decodeURIComponent(aKeyValue[1]);
}
}
}
document.querySelector('.sort-by').addEventListener('change', function(e) {
var value = e.currentTarget.value;
Shopify.queryParams.sort_by = value;
location.search = new URLSearchParams(Shopify.queryParams).toString();
});
})
Has someone ever tried to achieve something like this?
I would appreciate any help.
Thanks in advance,
I have the following scenario:
if the element is present, i have to do one activity and if not present will do another activity.
cy.xpath("//div[text()= 'button').its('length').then(res=> {
if (res > 0) {
return 1;
}
else {
cy.log ("Element is not present")
}
}
)} '''
if element is present = Code is working fine,
if the element xpath is not present = it try to search the element xpath (//div[text()= 'button') and throwing the error as 'Timed out retrying: Expected to find element: undefined, but never found it.'
if element is not present, Is there any way, i can handle the code ,
When using xpath you can (sort of) make it conditional by wrapping the xpath selector with count().
cy.xpath("count(//div[text()= 'button'])") // ok with async content
.then(count => {
if (count) {
//return 1; // not useful, returns a "chainer"
// ...but you can perform the required test here, e.g
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
})
The shorter syntax using built-in jQuery might be
const exists = !!Cypress.$("div:contains('button')").length
if (exists) {
cy.xpath("//div[text()= 'button']").click()
} else {
cy.log('not found')
}
Note that this is a partial match to 'button', where-as the xpath syntax is an exact match.
Also note - using Cypress.$ by-passes retry-ability, so it should not be used where the text is asynchronous.
From docs
This is a great way to synchronously query for elements when debugging from Developer Tools.
The implication is that it's more for debugging after the page has loaded.
The best practice is to try to construct the test and the app's data in such a way that you know that the button is present.
You can do something like this. With Cypress.$, you can validate the presence of the element with the help of the length attribute and then do further actions.
cy.get('body').then($body => {
const ele = $body.find('selector');
if (Cypress.$(ele).length == 0) {
//Do Something element is not present
}
else if (Cypress.$(ele).length > 0) {
//Do Something when element is present
}
})
All I have a Kendo grid built using the fluent grid builder. In one of my client templates, I need to iterate over a collection that is being returned. Here is the suspect line:
c.Bound(x => x.EventFormats).Width(300).ClientTemplate("# if(EventFormats != undefined && EventFormats.length > 0){ for(var formatIndex=0; formatIndex < EventFormats.length; formatIndex++){ console.log(i) } } #").EditorTemplateName("Formats");
The problem is that when I introduce the for loop, all hell breaks loose and it loops hundreds of thousands of times (after 300k I gave up and killed the process).
If I do this:
c.Bound(x => x.EventFormats).Width(300).ClientTemplate("# if(EventFormats != undefined && EventFormats.length > 0){ console.log(EventFormats.length) } #").EditorTemplateName("Formats");
It correctly returns the number of items I'm expecting in the collection. Only 4 of the records that are being returned will have the collection to begin with, and the maximum number of items in any collection is 2 (although it could be more).
I'm not really sure why it is loosing it's mind when I introduce the for loop and any insight would be greatly appreciated.
Here is the JSON object that is getting produced:
[{
"Id":85,
"FormatTypeId":34,
"Name":"35mm",
"Created":"/Date(1447265241983)/",
"CreatedBy":"system",
"Modified":null,
"ModifiedBy":null,
"FormatType":null
},
{
"Id":83,
"FormatTypeId":34,
"Name":"16mm",
"Created":"/Date(1447265241737)/",
"CreatedBy":"system",
"Modified":"/Date(1453243258067)/",
"ModifiedBy":"system",
"FormatType":null
}]
I'm not entirely sure as to why it was looping 300k+ times, but I did come up with a workable solution.
My KendoGrid template now has this line:
c.Bound(x => x.Formats).Width(300).ClientTemplate("#=$.generateFormatList(Formats)#");
Then in my JS file, I just wrote the generateFormatList function like so:
$.generateFormatList = function (formats) {
var template = '';
if (formats != null) {
for (var i = 0; i < formats.length; i++) {
if (i !== 0) {
template += ", ";
}
template += formats[i].Name;
}
}
return template;
}
That works like a champ. Not sure why but apparently Kendo can't handle for loops inside the ClientTemplate all that well.
Had a tough time with this so thought I would ask for a better solution than mine and also be able to post an answer for those it might help.
Problem:
Working with KendoUI JSP Grid. Goal add server side pagination and sorting
I need to be able to have a GET request as follows:
URL Expected by Spring Data Rest
http://localhost:8080/api/accounts?page=1&size=20&sort=firstName,desc -> or asc
URL Presented by default from KendoUI
http://localhost:8080/api/accounts?take=20&skip=0&page=1&pageSize=20&sort%5B0%5D%5Bfield%5D=firstName&sort%5B0%5D%5Bdir%5D=asc
This is for Server Side sorting for a Spring Data Rest project. Currently the Sorting portion is showing up as an array. How do you change the way the sorting URL is formed?
After a fair amount of coding by Google came up with this as a solution. Please correct me if this is not correct.
Within KendoUI Grid you can modify a URL via the parameterMap. Below is how this will format the KendoUI URL to what Spring Data Rest is expecting.
<script>
function parameterMap(data, operation) {
if (operation == "read") {
if (data.sort && data.sort.length > 0) {
var p = {};
if(!data.sort && data.sort.length < 0) {
p = data.page(1);
} else {
p = data.page
}
var values = {};
values["page"] = p;
values["size"] = data.pageSize;
values["sort"] = data.sort[0]['field'] + ',' + data.sort[0]['dir'];
return values;
} else {
var values = {};
values["page"] = data.page;
values["size"] = data.pageSize;
return values;
}
}
}
</script>
The else portion will ensure that pagination will still work. Also setting values["page"] = p; within the sorting portion ensures that when you sort it takes you to the 1st page of the sort.
The problem I still have is if you load the page then navigate to a page then hit sort it takes you to page 1 of the sorted data. If you now navigate to another page in the sorted data set and then choose to sort again it will not take you to page one of the new sort.
I'm displaying a table with multiple rows and columns. I'm using a JQUERY plugin called uiTableFilter which uses a text field input and filters (shows/hides) the table rows based on the input you provide. All you do is specify a column you want to filter on, and it will display only rows that have the text field input in that column. Simple and works fine.
I want to add a SECOND text input field that will help me narrow the results down even further. So, for instance if I had a PETS table and one column was petType and one was petColor -- I could type in CAT into the first text field, to show ALL cats, and then in the 2nd text field, I could type black, and the resulting table would display only rows where BLACK CATS were found. Basically, a subset.
Here is the JQUERY I'm using:
$("#typeFilter").live('keyup', function() {
if ($(this).val().length > 2 || $(this).val().length == 0)
{
var newTable = $('#pets');
$.uiTableFilter( theTable, this.value, "petType" );
}
}) // end typefilter
$("#colorFilter").live('keyup', function() {
if ($(this).val().length > 2 || $(this).val().length == 0)
{
var newTable = $('#pets');
$.uiTableFilter( newTable, this.value, "petColor" );
}
}) // end colorfilter
Problem is, I can use one filter, and it will display the correct subset of table rows, but when I provide input for the other filter, it doesn't seem to recognize the visible table rows that are remaining from the previous column, but instead it appears that it does an entirely new filtering of the original table. If 10 rows are returned after applying one filter, the 2nd filter should only apply to THOSE 10 rows. I've tried LIVE and BIND, but not working.
Can anyone shed some light on where I'm going wrong? Thanks!
The uiTableFilter plugin doesn't support what you're trying to do. A quick look at the source reveals this:
elems.each(function(){
var elem = jQuery(this);
jQuery.uiTableFilter.has_words(getText(elem), words, false)
? matches(elem)
: noMatch(elem);
});
and that expands to (essentially) this:
elems.each(function(){
var elem = jQuery(this);
jQuery.uiTableFilter.has_words(getText(elem), words, false)
? elem.show()
: elem.hide();
});
So all it does is spin through all the rows, .show() those that match, and .hide() those that don't; uiTableSorter doesn't pay attention to the current shown/hidden state of the rows and there's no way to tell it to filter on multiple columns.
If you really need your desired functionality then you can modify the plugin's behavior (the code is pretty small and simple) or just write your own. Here's a stripped down and simplified version that supports multiple filters and is a more conventional jQuery plugin than uiTableFilter:
(function($) {
$.fn.multiFilter = function(filters) {
var $table = $(this);
return $table.find('tbody > tr').each(function() {
var tr = $(this);
// Make it an array to avoid special cases later.
if(!$.isArray(filters))
filters = [ filters ];
howMany = 0;
for(i = 0, f = filters[0]; i < filters.length; f = filters[++i]) {
var index = 0;
$table.find('thead > tr > th').each(function(i) {
if($(this).text() == f.column) {
index = i;
return false;
}
});
var text = tr.find('td:eq(' + index + ')').text();
if(text.toLowerCase().indexOf(f.word.toLowerCase()) != -1)
++howMany;
}
if(howMany == filters.length)
tr.show();
else
tr.hide();
});
};
})(jQuery);
I'll leave error handling and performance as an exercise for the reader, this is just an illustrative example and I wouldn't want to get in the way of your learning. You could wire it up something like this:
$('#type').keyup(function() {
$('#leeLooDallas').multiFilter({ column: 'petType', word: this.value });
});
$('#color').keyup(function() {
$('#leeLooDallas').multiFilter([
{ column: 'petType', word: $('#type').val() },
{ column: 'petColor', word: this.value }
]);
});
And here's a live example (which assumes that you're going to enter something in "type" before "color"): http://jsfiddle.net/ambiguous/hdFDt/1/