react-virtualized - Is it possible to infinite scroll groups of list? - infinite-scroll

After reading through the documentation, I am not sure if react-virtualized support this use case.
Let's say I have some data grouped by date.
[
{
date: 'Monday',
data: {
item1: "item1",
item2: "item2",
item3: "item3",
// and more ..
}
},
{
date: "Tuesday",
data: {
item1: "item1",
item2: "item2",
item3: "item3",
// and more ..
}
}
]
Is it possible to use react-virtualized with InfiniteLoader and List to render something like this:
<div>
<div>
<div class="day">Monday</div>
<div>item1</div>
<div>item2</div>
<div>item3</div>
<!-- and more... -->
</div>
<div>
<div class="day">Tuesday</div>
<div>item1</div>
<div>item2</div>
<div>item3</div>
<!-- and more... -->
</div>
</div>
or does the markup has to be flat?

The markup doesn't have to be flat, so long as there's only 1 root node per row.
For example, check out this demo (source here). Each react-virtualized row in this tree view contains 1-to-many children, depending on whether it's been expanded.

Related

Ionic - HTML list with parents and child elements

I have data in array where every element represents parent object, and every parent object has items array, whose elements are children. I have to generate on HTML parents list + nested children list under every parent. Every child element is clickable (navigates to another page). Something like on image. Any help here? I can use *ngFor to iterate first level elements (parents) and use ion-list and ion-item but how to nest children?
You can basically do a nested *ngFor.
Example data:
var categories = [
{
"name": "Category 1",
"items": [
"item1"
"item2"
]
}
];
Example template:
<div *ngFor="let category of categories">
<h2>{{category.name}}</h2>
<ion-list>
<ion-item *ngFor="let item of category.items">{{item}}</ion-item>
</ion-list>
</div>
At the top-level you iterate your categories, and for every single category, you iterate its items.

Angular 4 Create Dynamic formArray inside array using reactive forms

Here, we are creating dynamically form array's inside array.
Below is the sample structure of expected result given below.
{
"optionsRadios": null,
"Package_Title": null,
"HotelData": [
{
"Htitle": "",
"HDescription": "",
"hotelStar": "",
"RoomData": [
{
"Hotel_Room_Type": ""
},
{
"Hotel_Room_Type": ""
}
]
}
]
}
I want to create HotelData Dynamically, within that HotelData array i want to create RoomData array fields also dynamically.
I created HotelData fields by the following codes:
export class AddPackageComponent implements OnInit {
ngOnInit() {
this.invoiceForm = this._formBuild.group({
Package_Title: [],
HotelData: this._formBuild.array([this.addRows()])
})
}
addRows() {
return this._formBuild.group({
Htitle: [''],
HDescription: [''],
hotelStar: ['']
});
}
addHotel() {
const control: FormArray = this.invoiceForm.get(`HotelData`) as FormArray;
control.push(this.addRows());
}
}
You are on the right track, we just need to add some more code...
addRows need the form array RoomData, and here we also initially push an empty form group of room. If you don't want that, modify it.
addRows() {
let group = this._formBuild.group({
...
RoomData: this._formBuild.array([])
});
// push formgroup to array initially
this.addRoom(group.controls.RoomData)
return group;
}
addRoom looks like this:
addRoom(hotel:any) {
let group = this._formBuild.group({
Hotel_Room_Type: ['']
})
hotel.push(group)
}
addRoom is also the method we are calling from template when we want to add a new room to a hotel. Remember to pass the current hotel as parameter from template.
As for adding a new hotel, your addHotel stays the way you have it now.
Then over to your template, the relevant part should look something like this:
<div formArrayName="HotelData">
<div *ngFor="let hotel of invoiceForm.get('HotelData').controls; let i = index" [formGroupName]="i" >
<!-- form controls here -->
<button (click)="addRoom(hotel.get('RoomData'))">Add Room</button>
<div formArrayName="RoomData">
<div *ngFor="let room of hotel.get('RoomData').controls; let j = index" [formGroupName]="j">
<!-- form controls here -->
</div>
</div>
</div>
</div>
Finally, here's a Demo: http://plnkr.co/edit/7tcLcnzALew3oKGenjwK?p=preview

$ionicPosition.offset and $ionicPosition.position causing "TypeError: Cannot read property 'getBoundingClientRect' of undefined"

I'm trying to get the position and offset of a dragged list item on release.
However, both $ionicPosition.offset and $ionicPosition.position both return "TypeError: Cannot read property 'getBoundingClientRect' of undefined.
I've included a rudimentary code snippet. Thanks in advance!
var data = {
"week": [{
"name": "Monday",
"activities": [{
"name": "Go running",
"shortname": "go_running"
}, {
"name": "Wash clothes",
"shortname": "wash_clothes"
}]
}, {
"name": "Tuesday",
"activities": [{
"name": "Holiday shopping",
"shortname": "holiday_shopping"
}, {
"name": "Clean bike",
"shortname": "clean_bike"
}]
}]
}
var app = angular.module('activityplan', ['ionic']);
app.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if (window.cordova && window.cordova.plugins.Keyboard) {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
// Don't remove this line unless you know what you are doing. It stops the viewport
// from snapping when text inputs are focused. Ionic handles this internally for
// a much nicer keyboard experience.
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
});
});
app.controller('PlanController', ['$scope','$ionicPosition', function($scope,$ionicPosition) {
$scope.days = data.week;
$scope.releaseTest = function(a) {
console.log($ionicPosition.offset(a));
console.log($ionicPosition.position(a));
}
$scope.moveActivity = function(activity, day, fromIndex, toIndex) {
day.activities.splice(fromIndex, 1);
day.activities.splice(toIndex, 0, activity);
};
}]);
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>Activity plan</title>
<link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
</head>
<body ng-app="activityplan">
<ion-content ng-controller="PlanController" style="width:360px;">
<div ng-repeat="day in days">
<div class="item item-divider item-dark">
<b>{{ day.name }}</b>
</div>
<ion-list ng-controller="PlanController" show-reorder="true" can-swipe="true">
<ion-item class="item-icon-right" ng-repeat="activity in day.activities track by $index" on-release="releaseTest(activity)">
{{ activity.name }}
<ion-option-button class="button-assertive icon ion-trash-a" ng-click=""></ion-option-button>
<ion-reorder-button class="ion-navicon" on-reorder="moveActivity(activity,day,$fromIndex,$toIndex)"></ion-reorder>
</ion-item>
</ion-list>
</div>
</ion-content>
</body>
</html>
You are doing it all wrong. Method offset(element) and position(element) of $ionicPosition expects DOM element to be part of parameter.
In your case you are passing the JavaScript object from mark-up thats why its failing.
position(element) :
Get the current coordinates of the element, relative to the offset parent. Read-only equivalent of jQuery’s position function.
Its fact that it is very poorly documented in ionic docs.
What I figured out after digging through source code that these methods are jQuery equivalent and jQuery always wraps the matched element inside array, therefore parameter should be an array with 0th index pointing to actual dom as shown below
var element = document.getElementById(id);
var offset = $ionicPosition.offset([element]);
/*it returns the offset of param[0]
where param is the parameter passed to this function
*/
/*note that we need to wrap the dom element inside array as these methods are inspired from jQuery
and jQuery wraps the matched element inside array
If you are using AngularJS's jqLite, please verify if actual DOM is in array 0th position or not
*/

onChange event doesn't change state in <select> React

As the title, I have an onChange event for the <select> element in React, but it doesn't fire when I change the option in the dropdown.
I set the initial state, update the state with this.setState() and test it with 2 onChange event, 1 for the text input, and the other for the <select>.
Only the state of the text input is updated. It means only the text input called the onChange event.
Can anyone help me figure out why this happens?
ServicesPage = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
var accountId = Meteor.userId();
return {
services: Services.find({ accountId: accountId }).fetch(),
sites: Sites.find({ accountId: accountId }).fetch()
}
},
getInitialState() {
return {
selected: "0",
test: 'Hello'
}
},
handleChangeLocation(e) {
this.setState({ selected: e.target.value });
},
handleChange(event) {
this.setState({ test: event.target.value});
console.log(this.state.test);
},
componentDidMount() {
$('select').material_select();
console.log(this.state.selected);
},
render() {
var sitesList = [];
for (var i = 0; i < this.data.sites.length; i++) {
sitesList.push(<option key={i} value={this.data.sites[i]._id}>{this.data.sites[i].name}</option>);
}
return (
<div>
<input type="text" value={this.state.test} onChange={this.handleChange} />
<h4>Services Page</h4>
<div className="row">
<div className="col s12 m6 l6">
<select value={this.state.selected} onChange={this.handleChangeLocation}>
<option value="0">Choose location</option>
{sitesList}
</select>
</div>
</div>
</div>
)
}
})
material_select inserts it's own DOM elements and hides the existing <select> hierarchy. The React elements don't know anything about this since they work only at the level of the virtual DOM. Therefore material_select undermines the connection between the React virtual DOM and the real DOM.
This will be the case for almost all JQuery plugins, so they are mostly incompatible with React. You probably want to use the material-ui React components instead.

Is it possible to hide sap.m.input "description" property value

I'm using the description field to hold an value that I don't want to be displayed, is it possible to set this property to visible:false or set to width to 0?
new sap.m.Input("idAltDistInput"+refDocID+sequenceID, {value:"{AltDistrictDesc}",
description: { path : 'AltDistrictID' }
:
visible : false doesn't seem to work.
Yes you can by adding StyleClass.
sap.m.Input("id",{
//Properties
}).addStyleClass("InputDescripTionHidden");
Add following css
.InputDescripTionHidden>span{
display:none
}
Your comment above suggests that you want to store some hidden value, for later use.
Rather than "hijack" (in the nicest sense of the word) another property, you should consider using Custom Data, which is designed for this sort of thing. Here's an example.
new sap.m.List({
mode: "SingleSelectMaster",
items: {
path: "/records",
template: new sap.m.InputListItem({
label: "District",
content: new sap.m.Input({
value: "{AltDistrictDesc}",
customData: [new sap.ui.core.CustomData({
key: "DistrictID",
value: "{AltDistrictID}"
})]
})
})
},
select: function(oEvent) {
var id = oEvent.getParameter("listItem")
.getContent()[0] // the Input control
.getCustomData()[0] // the only Custom Data
.getValue();
alert("Selected District ID : " + id);
}
})
.setModel(new sap.ui.model.json.JSONModel({
records: [{
AltDistrictID: "D1",
AltDistrictDesc: "District 1"
}, {
AltDistrictID: "D2",
AltDistrictDesc: "District 2"
}]
}))
.placeAt("content");
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-libs="sap.m" data-sap-ui-theme="sap_bluecrystal"></script>
<div class="sapUiBody" id="content"></div>
(Note that for simplicity, I'm just grabbing the first content and the first custom data within that content in the select listener. You will want to do this slightly more precisely in real code.)