RxDataSources - How to add a custom empty cell when there's no data - rxdatasources

struct MyViewModel {
var items: Observable<String>
//....
}
// In view controller
viewModel.items.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: MyCell.self)) { index, model, cell in
//...
}
.disposed(by: disposeBag)
If I have a another cell called EmptyCell, and I want to display this cell if the items is empty. How could I achieve this.

The RxDataSources data source should consist of any piece of state or data you want to display in your cells. For this reason you might actually want to have an enumeration for your SectionItem and not a simple string.
enum CellType {
case empty
case regular(String)
}
typealias Section = SectionModel<String, CellType>
Then, when binding your "CellType" Observable, you can relatively easily use the configureCell Cell Factory to define which cell you would want to dequeue for each case.
e.g.
dataSource.configureCell = { _, _, _, cellType in
switch cellType {
case .empty: /// Dequeue empty cell
case .regular(let string): // Dequeue regular cell and set string
}
}

Related

Is there a way to make a scrollable calendar

I have this calendar I'm working on in jetpack compose. I have gotten some help with placement in rows and stuff like that using weight. The problem is though that I'm trying to make an item of a month in the calendar and place that in a LazyRow but i think it has to do with that lazy rows are scrollable so the weight function for the box with the text in it makes it so that no text shows up if the code looks like this...
fun CalendarRowItem(calendarSize: Int, initWeekday: Int, textColor: Color, clickedColor: Color){
var dayCounter: Int = 1
var week: Int = 1
var _initWeekday = initWeekday
Column() {
while(dayCounter <= calendarSize){
Row(modifier = Modifier.fillMaxWidth().padding(5.dp)) {
if(initWeekday > 0){
repeat(initWeekday){
Spacer(modifier = Modifier.weight(1f))
}
}
for (i in week..(7 - _initWeekday)){
if(dayCounter <= 31){
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(10.dp)
.background(clickedColor, CircleShape)
.clickable { }
) {
Text(text = dayCounter++.toString(), color = textColor )
}
}else{
Spacer(modifier = Modifier.weight(1f))
}
_initWeekday = 0
}
}
}
}
}
but without the weight i cant place the dates correctly plus i have no clue how i would make it so that only one item is visible in the screen at once?
I think using HorizontalPager instead of LazyRow may suit better in this case: it has pagination, as you probably don't need your scroll to stop in between the month, and won't have such a problem out of the box, as same modifier as I suggest you below in applied by the component.
Below is a general answer to the problem, as you still may face it in other cases.
That indeed happens because the parent is horizontally scrollable.
In such case Modifier.fillMaxWidth, as well as Modifier.weight with fill parameter set to true (it's default value) has no effect, as parent width constraint is equal to infinity.
You need to restrict parent width. In this case Modifier.fillParentMaxWidth() can be used on the container: it'll make the view width be equal to the LazyRow width - the part of scroll view which takes exactly "one screen".
As this modifier is defined on LazyItemScope, you have two options to apply it:
Define CalendarRowItem on the scope, in this case CalendarRowItem will only be available to use from lazy view item.
fun LazyItemScope.CalendarRowItem(/* ... */) {
// ...
Column(Modifier.fillParentMaxWidth()) {
// ...
}
}
Add a modifier parameter for CalendarRowItem and pass Modifier.fillParentMaxWidth from item scope:
fun CalendarRowItem(/* ... */, modifier: Modifier) {
// ...
Column(modifier) {
// ...
}
}
items(yourItems) { item ->
CalendarRowItem(
/* ... */,
modifier = Modifier.fillParentMaxWidth()
)
}

Is there any way to add a template doc at top of an another google document using google scripting?

I am trying to add a template doc into an existing google doc.The template is being added, but next time when i am trying to add the template again the template is appending at the bottom of the existing google doc but i want to insert the template at the top.
You can do this by getting the Body of one document and appending its child Elements to the current document.
function addtemplate() {
var thisDoc = DocumentApp.getActiveDocument();
var thisBody = thisDoc.getBody();
var templateDoc = DocumentApp.openById(''); //Pass in id of doc to be used as a template.
var templateBody = templateDoc.getBody();
for(var i=0; i<templateBody.getNumChildren();i++){ //run through the elements of the template doc's Body.
switch (templateBody.getChild(i).getType()) { //Deal with the various types of Elements we will encounter and append.
case DocumentApp.ElementType.PARAGRAPH:
thisBody.appendParagraph(templateBody.getChild(i).copy());
break;
case DocumentApp.ElementType.LIST_ITEM:
thisBody.appendListItem(templateBody.getChild(i).copy());
break;
case DocumentApp.ElementType.TABLE:
thisBody.appendTable(templateBody.getChild(i).copy());
break;
}
}
return thisDoc;
}
It sounds like your goal is to choose the position of the document where the content is added?
One option is to add the template at your current cursor location.
In the example below I have two functions. The first function creates a menu in Google Docs when I open the document (usually a few seconds delay).
The second function attempts to identify the position of my cursor. If it's successful it will insert the date at my cursor position.
Since I've created a menu item I don't have to go to the script editor to trigger this function.
function onOpen() {
// Add a menu item in Google Docs.
DocumentApp.getUi().createMenu('Insert Menu')
.addItem('Insert Current Date', 'insertCurrentDate')
.addToUi();
}
function insertCurrentDate() {
var cursor = DocumentApp.getActiveDocument().getCursor();
if (cursor) {
// Attempt to insert text at the cursor position. If insertion returns null,
// then the cursor's containing element doesn't allow text insertions.
var date = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd");
var element = cursor.insertText(date);
if (element) {
element.setBold(true);
} else {
DocumentApp.getUi().alert('Document does not allow inserted text at this location.');
}
} else {
DocumentApp.getUi().alert('Cannot find a cursor in the document.');
}
}
It's also possible that you want to clear the previous template before pasting the new one in? You can do that with the clear() function and then run the rest of your code.
body.clear()

Is Qml able to receive a list of models and let listview to choose which one to use?

I have got one listview in qml now known as listview1. For each element in listview1, it contains another listview used to show some of its variables. Now I want to make it possible for all such sub-listviews able to add items to them. And also adding item to one of them will not affect all the other listviews in other elements of listview1.
What is the best way to solve such problem? Thanks
Adapted from https://stackoverflow.com/a/55604525/1493608 (same thing but with a GridView instead of a ListView) :
Give each sub-ListView a UUID.
Connect each sub-ListView to a listview1 signal that you will use to broadcast to sub-ListView delegates what you will want to do.
In this signal, put your UUID.
In sub-ListView receivers, put a filter on the UUID and do what you want to do only if it is the right one.
ListView {
id: listview1
signal broadcaster(string uuid, var other_args)
delegate: ListView {
// Provided like this or as a role from the corresponding list element
property string uuid: ""
signal sendToOtherLV(string uuid, var other_args)
function lv_receiver(broadcasted_uuid, other_args) {
if (this.uuid === broadcasted_uuid) {
// Do what you have to do with other_args.
}
}
Component.onCompleted: {
/*
* Custom way to generate the UUID and store it so
* the other ListViews will be able to retrieve it.
*/
this.uuid = generateAndStoreUUID()
// Connecting the sub-ListView to your "sub-ListView LAN".
this.sendToOtherLV.connect(listview1.broadcaster)
listview1.broadcaster.connect(this.lv_receiver)
}
Component.onDestruction: {
// Disconnecting the sub-ListView from your "sub-ListView LAN".
this.sendToOtherLV.disconnect(listview1.broadcaster)
listview1.broadcaster.disconnect(this.lv_receiver)
}
// Here is an example of what you might have to do
function foo() {
// ...
var otherlv_receiver_uuid
var signal_args
// Retreive the UUID of the ListView that you want to send something to.
// Set the other arguments you want to send through the signal.
this.sendToOtherLV(otherlv_receiver_uuid, signal_args)
// ...
}
// Other sub-ListView-related stuff
}
// Other listview1-related stuff
}

Angular2 unit test Click() with keypress

I am trying to test a sorting function for a table. The table enables sorting on a primaray and secondary value. The secondary sorting value is set by holding down the shift key.
Code for setting the sort order:
private sortClick(event: any, column: DataGridColumn): void {
if (!column.sortable) {
return;
}
if (event.shiftKey) { // When the shift key is pressed, secondary sorting is being set.
if (column.headerText === this.primarySorting.headerText) {
return;
}
this.secondarySortingColumn.setSortingProperty(column.headerText, column.name);
} else {
this.primarySorting.setSortingProperty(column.headerText, column.name);
this.secondarySortingColumn.clear();
}
this.sortData(this);
}
Now I'm writing the unit testst for the function.
The primary sorting is done using the following:
let firstColumn: NodeListOf<HTMLElement> = <NodeListOf<HTMLElement>>compiled.querySelectorAll("#header0");
firstColumn.item(0).click();
How can I call the .click() function with shift key pressed in the unit test
You can use the dispaatchEvent method to invoke the click event and supply other event information as follows:
var event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'shiftKey': true
});
let firstColumn: NodeListOf<HTMLElement> = <NodeListOf<HTMLElement>>compiled.querySelectorAll("#header0");
firstColumn.item(0).dispatchEvent(event);
Note the shiftKey member is set to true in the mouseEventInit dictionary parameter to the MouseEvent constructor which allows us to specify that the shiftKey is pressed.

Adding a list inside a GWT List Box

I have added an List box and and i have an list of value that needs to be populated in the list box. The only option i could find to add values to the list box is listbox.addItem... where i have to iterate the list of values i have and need to add it one by one. Is there any other way where i can add the entire list in one single call.??
private final List<Operation> numberComparisons = Arrays.asList(Operation.EQUAL, Operation.GREATER_THAN, Operation.GREATER_THAN_OR_EQUAL, Operation.LESS_THAN, Operation.LESS_THAN_OR_EQUAL, Operation.FILLED_IN, Operation.EMPTY);
now i have to add this number comparisons list into
ListBox conditionDropDown = new ListBox();
conditionDropDown.addItem(numberComparisons);
..... how can i do this...???
Maybe try this:
class MyListBox extends ListBox {
public void addAsList(List<Operation> list) {
for (Operation operation : Operation.values()) {
addItem(operation.toString());
}
}
and finally:
MyListBox conditionDropDown = new MyListBox();
conditionDropDown.addAsList(numberComparisons);
Have a look at the ValueListBox widget; you'll then use a Renderer to "generate" the String representation of your Operation (used to display them to the user in the list).