How to differentiate between web elements that contain the same class - python-3.x

I want to make a list of elements that contain all the elements from these web elements:
<div class="league-item-block" id="LeagueId_(here there is a different id for every web element)" data-bind="css: { 'Hidden': $data.IsHidden() }, attr: { id: league.getDomId() }"></div>
and I use the syntax:
list = driver.find_elements_by_class_name("league-item-block")
The problem is that also have the web elements:
<div class="league-item-block Hidden" id="LeagueId_(here there is a different id for every web element)" data-bind="css: { 'Hidden': $data.IsHidden() }, attr: { id: league.getDomId() }"></div>
The list that is created contains these elements, and I do not know how to avoid them.

This can solve your problem -- CSS Selector:
.league-item-block:not(.Hidden)
Code:
list = driver.find_elements_by_css_selector(".league-item-block:not(.Hidden)")

Related

Avoiding rerendering of my custom items in a Flat List

I created a custom item for my Flat List which shows the name and the image ( retrieved by his url) of a certain product. The user can filter this products by their names, and the new filtered data is passed again to the Flat List. The problem is that whenever I filter the data, Flat List re-renders every custom item, so the Image components inside have to download the pic again, causing a flickering effect. This is an approximate example of what I mean:
function test(){
initialData=[
{
text: 'product A',
image: 'https://picsum.photos/200/300',
id: 1
},
{
text: 'product B',
image: 'https://picsum.photos/200/300',
id: 2
},
{
text: 'product C',
image: 'https://picsum.photos/200/300',
id: 3
},
]
const [filteredData, setFilteredData] = useState(initialData);
const handlePress = () =>(
setFilteredData(filterData(data)); // Filter data by some parameters
)
const renderItem = (props) => {
return(
<View>
<Image>{props.text}</Image>
<Text source={{uri: props.image}}></Text>
</View>
)
}
return(
<>
<Button onPress={() => handlePress()}></Button>
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>
</>
)
}
first must to say in order to avoiding unnecessary rendering data you are be able to use useMemo and useCallback methods (in functional programming) for memorizing your variables and your functions (check hooks React hooks documents). some procedures can help you for improving your code:
Don't put the static data inside your function, for example put initialData outside of your test function or memorize it by useMemo.
Don't use inline function inside components and as far as possible memorize them.
You can create independent component for your FlatList item and memorize it by memo then use them or memorize renderItem inside your test function.
For use image in your app (specifically when you have large numbers of image) use react-native-fast-image that help you avoiding to re-rendering images by caching them.

A way to render multiple root elements on VueJS with v-for directive

Right now, I'm trying to make a website that shows recent news posts which is supplied my NodeJS API.
I've tried the following:
HTML
<div id="news" class="media" v-for="item in posts">
<div>
<h4 class="media-heading">{{item.title}}</h4>
<p>{{item.msg}}</p>
</div>
</div>
JavaScript
const news = new Vue({
el: '#news',
data: {
posts: [
{title: 'My First News post', msg: 'This is your fist news!'},
{title: 'Cakes are great food', msg: 'Yummy Yummy Yummy'},
{title: 'How to learnVueJS', msg: 'Start Learning!'},
]
}
})
Apparently, the above didn't work because Vue can't render multiple root elements.
I've looked up the VueJS's official manual and couldn't come up with a solution.
After googling a while, I've understood that it was impossible to render multiple root element, however, I yet to have been able to come up with a solution.
The simplest way I've found of adding multiple root elements is to add a single <div> wrapper element and make it disappear with some CSS magic for the purposes of rendering.
For this we can use the "display: contents" CSS property. The effect is that it makes the container disappear, making the child elements children of the element the next level up in the DOM.
Therefore, in your Vue component template you can have something like this:
<template>
<div style="display: contents"> <!-- my wrapper div is rendered invisible -->
<tr>...</tr>
<tr>...</tr>
<tr>...</tr>
</div>
</template>
I can now use my component without the browser messing up formatting because the wrapping <div> root element will be ignored by the browser for display purposes:
<table>
<my-component></my-component> <!-- the wrapping div will be ignored -->
</table>
Note however, that although this should work in most browsers, you may want to check here to make sure it can handle your target browser.
You can have multiple root elements (or components) using render functions
A simple example is having a component which renders multiple <li> elements:
<template>
<li>Item</li>
<li>Item2</li>
... etc
</template>
However the above will throw an error. To solve this error the above template can be converted to:
export default {
functional: true,
render(createElement) {
return [
createElement('li', 'Item'),
createElement('li', 'Item2'),
]
}
}
But again as you probably noticed this can get very tedious if for example you want to display 50 li items. So, eventually, to dynamically display elements you can do:
export default {
functional: true,
props: ['listItems'], //this is an array of `<li>` names (e.g. ['Item', 'Item2'])
render(createElement, { props }) {
return props.listItems.map(name => {
return createElement('li', name)
})
}
}
INFO in those examples i have used the property functional: true but it is not required of course to use "render functions". Please consider learning more about functional componentshere
Define a custom directive:
Vue.directive('fragments', {
inserted: function(el) {
const children = Array.from(el.children)
const parent = el.parentElement
children.forEach((item) => { parent.appendChild(item) })
parent.removeChild(el)
}
});
then you can use it in root element of a component
<div v-fragments>
<tr v-for="post in posts">...</tr>
</div>
The root element will not be rendered in DOM, which is especially effective when rendering table.
Vue requires that there be a single root node. However, try changing your html to this:
<div id="news" >
<div class="media" v-for="item in posts">
<h4 class="media-heading">{{item.title}}</h4>
<p>{{item.msg}}</p>
</div>
</div>
This change allows for a single root node id="news" and yet still allows for rendering the lists of recent posts.
In Vue 3, this is supported as you were trying:
In 3.x, components now can have multiple root nodes! However, this does require developers to explicitly define where attributes should be distributed.
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Multiple root elements are not supported by Vue (which caused by your v-for directive, beacause it may render more than 1 elements). And is also very simple to solve, just wrap your HTML into another Element will do.
For example:
<div id="app">
<!-- your HTML code -->
</div>
and the js:
var app = new Vue({
el: '#app', // it must be a single root!
// ...
})

How to filter items inside “ngFor” loop, based on object property string

I need to filter items inside an ngFor loop, by changing the category in a drop-down list. Therefore, when a particular category is selected from the list, it should only list the items containing that same category.
HTML Template:
<select>
<option *ngFor="let model of models">{{model.category}}</option>
</select>
<ul class="models">
<li *ngFor="let model of models" (click)="gotoDetail(model)">
<img [src]="model.image"/>
{{model.name}},{{model.category}}
</li>
</ul>
Items Array:
export var MODELS: Model[] = [
{ id: 1,
name: 'Model 1',
image: 'img1',
category: 'Cat1',
},
{ id: 2,
name: 'Model 2',
image: 'img2',
category: 'Cat3',
},
{ id: 3,
name: 'Model 3',
image: 'img3',
category: 'Cat1',
},
{ id: 4,
name: 'Model 4',
image: 'img4',
category: 'Cat4',
},
...
];
Also, the drop-down list contains repeated category names. It is necessary for it to list only unique categories (strings).
I know that creating a custom pipe would be the right way to do this, but I don't know how to write one.
Plunker: http://plnkr.co/edit/tpl:2GZg5pLaPWKrsD2JRted?p=preview
Here is a sample pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'matchesCategory'
})
export class MathcesCategoryPipe implements PipeTransform {
transform(items: Array<any>, category: string): Array<any> {
return items.filter(item => item.category === category);
}
}
To use it:
<li *ngFor="let model; of models | matchesCategory:model.category" (click)="gotoDetail(model)">
===== for the plunkr example ====
You need your select changes to reflect in some variable
First define in your class a member:
selectedCategory: string;
then update your template:
<select (change)="selectedCategory = $event.target.value">
<option *ngFor="let model of models ">{{model.category}}</option>
</select>
last, use the pipe:
<li *ngFor="let model; of models | matchesCategory:selectedCategory" (click)="gotoDetail(model)">
==== comments after seeing the plunker ====
I noticed you used promise. Angular2 is more rxjs oriented. So the first thing I'd change is in your service, replace:
getModels(): Promise<Model[]> {
return Promise.resolve(MODELS);
}
to:
getModels(): Observable<Array<Model>> {
return Promise.resolve(MODELS);
}
and
getModels(id: number): Observable<Model> {
return getModels().map(models => models.find(model.id === id);
}
then in your ModelsComponent
models$: Observable<Array<Model>> = svc.getModels();
uniqueCategories$: Observable<Array<Model>> = this.models$
.map(models => models.map(model => model.category)
.map(categories => Array.from(new Set(categories)));
Your options will become:
<option *ngFor="let category; of uniqueCategories$ | async">{{model.category}}</option>
and your list:
<li *ngFor="let model; of models$ | async | matchesCategory:selectedCategory" (click)="gotoDetail(model)">
This is a very drafty solution since you have many duplicates and you keep querying the service. Take this as a starting point and query the service only once, then derive specific values from the result you got.
If you'd like to keep you code, just implement a UniqueValuesPipe, its transform will get a single parameter and filter it to return unique categories using the Array.from(new Set(...)). You will need though to map it to strings (categories) first.

Nested ListView or Nested Repeater

I am trying to created a nested repeater or a nested list view using WinJS 4.0, but I am unable to figure out how to bind the data source of the inner listview/repeater.
Here is a sample of what I am trying to do (note that the control could be Repeater, which I would prefer):
HTML:
<div id="myList" data-win-control="WinJS.UI.ListView">
<span data-win-bind="innerText: title"></span>
<div data-win-control="WinJS.UI.ListView">
<span data-win-bind="innerText: name"></span>
</div>
</div>
JS:
var myList = element.querySelector('#myList).winControl;
var myData = [
{
title: "line 1",
items: [
{name: "item 1.1"},
{name: "item 1.2"}
]
},
{
title: "line 2",
items: [
{name: "item 2.1"},
{name: "item 2.2"}
]
}
];
myList.data = new WinJS.Binding.List(myData);
When I try this, nothing renders for the inner list. I have attempted trying to use this answer Nested Repeaters Using Table Tags and this one WinJS: Nested ListViews but I still seem to have the same problem and was hoping it was a little less complicated (like KnockOut).
I know it is mentioned that WinJS doesn't support nested ListViews, but that seems to be a few years ago and I am hoping that is still not the issue.
Update
I was able to get the nested repeater to work correctly, thanks to Kraig's answer. Here is what my code looks like:
HTML:
<div id="myTemplate" data-win-control="WinJS.Binding.Template">
<div
<span>Bucket:</span><span data-win-bind="innerText: name"></span>
<span>Amount:</span><input type="text" data-win-bind="value: amount" />
<button class="removeBucket">X</button>
<div id="bucketItems" data-win-control="WinJS.UI.Repeater"
data-win-options="{template: select('#myTemplate')}"
data-win-bind="winControl.data: lineItems">
</div>
</div>
</div>
<div id="budgetBuckets" data-win-control="WinJS.UI.Repeater"
data-win-options="{data: Data.buckets,template: select('#myTemplate')}">
</div>
JS: (after the "use strict" statement)
WinJS.Namespace.define("Data", {
buckets: new WinJS.Binding.List([
{
name: "A",
amount: 5,
lineItems: new WinJS.Binding.List( [
{ name: 'test item1', amount: 50 },
{ name: 'test item2', amount: 25 }
]
)
}
])
})
*Note that this answers part of my question, however, I would really like to do this all after a repo call and set the repeater data source programmatically. I am going to keep working towards that and if I get it I will post that as the accepted answer.
The HTML Repeater control sample for Windows 8.1 has an example in scenario 6 with a nested Repeater, and in this case the Repeater is created through a Template control. That's a good place to start. (I discuss this sample in Chapter 7 of Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, starting on page 372, or 374 for the nested part.)
Should still work with WinJS 4, though I haven't tried it.
Ok, so I have to give much credit to Kraig because he got me on the correct path to getting this worked out and the referenced book Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition is amazing.
The original issue was a combination of not using templates correctly (using curly braces in the data-win-bind attribute), not structuring my HTML correctly and not setting the child lists as WinJS.Binding.List data source. Below is the final working code structure to created a nested repeater when binding the data from code only:
HTML:
This is the template for the child lists. It looks similar, but I plan on add more things so I wanted it separate instead of recursive as referenced in the book. Note that the inner div after the template control declaration was important for me.
<div id="bucketItemTemplate" data-win-control="WinJS.Binding.Template">
<div>
<span>Description:</span>
<span data-win-bind="innerText: description"></span>
<span>Amount:</span>
<input type="text" data-win-bind="value: amount" />
<button class="removeBucketItem">X</button>
</div>
</div>
This is the main repeater template for the lists. Note that the inner div after the template control declaration was important for me. Another key point was using the "winControl.data" property against the property name of the child lists.
<div id="bucketTemplate" data-win-control="WinJS.Binding.Template">
<div>
<span>Bucket:</span>
<span data-win-bind="innerText: bucket"></span>
<span>Amount:</span>
<input type="text" data-win-bind="value: amount" />
<button class="removeBucket">X</button>
<div id="bucketItems" data-win-control="WinJS.UI.Repeater"
data-win-options="{template: select('#bucketItemTemplate')}"
data-win-bind="winControl.data: lineItems">
</div>
</div>
</div>
This is the main control element for the nested repeater and it is pretty basic.
<div id="budgetBuckets" data-win-control="WinJS.UI.Repeater"
data-win-options="{template: select('#bucketTemplate')}">
</div>
JavaScript:
The JavaScript came down to a few simple steps:
Getting the winControl
var bucketsControl = element.querySelector('#budgetBuckets').winControl;
Looping through the elements and making the child lists into Binding Lists - the data here is made up but could have easily came from the repo:
var bucketsData = selectedBudget.buckets;
for (var i = 0; i < bucketsData.length; i++) {
bucketsData[i].lineItems =
new WinJS.Binding.List([{ description: i, amount: i * 10 }]);
}
Then finally converting the entire data into a Binding list and setting it to the "data" property of the winControl.
bucketsControl.data = new WinJS.Binding.List(bucketsData);
*Note that this is the entire JavaScript file, for clarity.
(function () {
"use strict";
var nav = WinJS.Navigation;
WinJS.UI.Pages.define("/pages/budget/budget.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
// TODO: Initialize the page here.
var bindableBuckets;
require(['repository'], function (repo) {
//we can setup our save button here
var appBar = document.getElementById('appBarBudget').winControl;
appBar.getCommandById('cmdSave').addEventListener('click', function () {
//do save work
}, false);
repo.getBudgets(nav.state.budgetSelectedIndex).done(function (selectedBudget) {
var budgetContainer = element.querySelector('#budgetContainer');
WinJS.Binding.processAll(budgetContainer, selectedBudget);
var bucketsControl = element.querySelector('#budgetBuckets').winControl;
var bucketsData = selectedBudget.buckets;
for (var i = 0; i < bucketsData.length; i++)
{
bucketsData[i].lineItems = new WinJS.Binding.List([{ description: i, amount: i * 10 }]);
}
bucketsControl.data = new WinJS.Binding.List(bucketsData);
});
});
WinJS.UI.processAll();
}
});
})();

How to short data form webservice with winjs?

I have problems with grouped ListView or short group. i get data from webservice already but i don't know how to binding data to html template user behind code.
HTML:
<div class="listLayoutTopHeaderTemplate" data-win-control="WinJS.Binding.Template">
<div class="listLayoutTopHeaderTemplateRoot">
<div data-win-bind="innerHTML: title"></div>
</div>
</div>
<div id="listView"
class="win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{
itemTemplate: select('.smallListIconTextTemplate'),
groupHeaderTemplate: select('.listLayoutTopHeaderTemplate'),
layout: { type: WinJS.UI.ListLayout, groupHeaderPosition: 'top'}}">
</div>
Winjs:
var myData = new WinJS.Binding.List([
{ title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "/images/fruits/60Banana.png" },
{ title: "Lavish Lemon Ice", text: "Sorbet", picture: "/images/fruits/60Lemon.png" },
]);
var grouped = myData.createGrouped(function (item) {
return item.title.toUpperCase().charAt(0);
}, function (item) {
return {
title: item.title.toUpperCase().charAt(0)
};
}, function (left, right) {
return left.charCodeAt(0) - right.charCodeAt(0);
});
listView.winControl.groupDataSource = grouped.groupDataSource;
You need to take whatever data you get from the web service, which is presumably JSON, and transfer that data into a WinJS.Binding.List that you can use as the ListView's data source. In the code you show, this is the myData variable, which you can initialize as empty (instead of using the shown data that's just from a sample). Iterating over your web service data, call the List's add method for each item. If the JSON you want to render already has an array, then parse that out and pass the array to the WinJS.Binding.List constructor.
Either way, you end up with a WinJS.Binding.List populated with the data from the service, which you can then give to the ListView for rendering.
There are fancier ways of working with data sources if you want to avoid obtaining the web service data and copying it all into an in-memory WinJS.Binding.List. For this I'll refer you to Chapter 7 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, specifically the section "Collection Control Data Sources" on page 380.

Resources