Response Handle - database

I am pulling data database,
My response class
sealed class Response<out T>{
object Loading: Response<Nothing>()
data class Success<out T>(val data: T): Response<T>()
data class Error(val message: String): Response<Nothing>()
}
Where should I handle the incoming data like this?
First approach
View Model
class DataViewModel (private val useCase: UseCase, ) : ViewModel() {
private val _dataState = mutableStateOf("")
val dataState = _dataState
val loading = mutableStateOf(false)
private val _eventFlow = MutableSharedFlow<UIEvent>()
val eventFlow = _eventFlow.asSharedFlow()
fun getData() {
viewModelScope.launch {
useCase.getData().collect { response ->
when(response){
is Response.Error -> {
_eventFlow.emit(UIEvent.ShowSnackBar(response.message))
loading.value=false
}
is Response.Loading -> loading.value=true
is Response.Success -> {
_dataState.value = response.data
loading.value=false
_eventFlow.emit(UIEvent.Success)
}
}
}
}
}
sealed class UIEvent {
data class ShowSnackBar(val message: String) : UIEvent()
object Success : UIEvent()
}
}
Screen
#Composable
fun DataScreen(dataViewModel: DataViewModel) {
val scaffoldState = rememberScaffoldState()
LaunchedEffect(key1 = true) {
dataViewModel.eventFlow.collectLatest { event ->
when (event) {
is DataViewModel.UIEvent.ShowSnackBar -> {
scaffoldState.snackbarHostState.showSnackbar(event.message)
}
is DataViewModel.UIEvent.Success -> {
"Do something"
}
}
}
}
Scaffold(
scaffoldState = scaffoldState,
content = { EditProfileContent(dataViewModel.dataState.value) }
)
}
Second Approach
View Model
class DataViewModel (private val useCase: UseCase, ) : ViewModel() {
private val _dataState = mutableStateOf<Response<Data>>(Response.Loading)
val dataState = _dataState
fun getData() {
viewModelScope.launch {
useCase.getData().collect { response ->
_dataState.value = response
}
}
}
}
Screen
#Composable
fun DataScreen(dataViewModel: DataViewModel) {
val scaffoldState = rememberScaffoldState()
when(val dataState = dataViewModel.dataState.value){
is Response.Error -> {
LaunchedEffect(key1 = true, block = {scaffoldState.snackbarHostState.showSnackbar(dataState.message)}) }
is Response.Loading -> { Loading() }
is Response.Success -> {
Scaffold(
scaffoldState = scaffoldState,
content = { EditProfileContent(dataState.data) }
)
// Do something else
}
}
}
The first approach seems much easier to me, especially when I'm pulling in more than one data, but when I search, the second approach is usually used in most places.
What's wrong with using the first approach? Which is the best practice?

Related

Missing properties in TypeScript

class RolloutStoreClass {
import { observable, action, makeAutoObservable } from "mobx";
public queue = observable<IDeploymentProject>([]);
public inProcess = observable<IDeploymentProject>([]);
public successfull = observable<IDeploymentProject>([]);
public failed = observable<IDeploymentProject>([]);
constructor() {
makeAutoObservable(this);
}
#action
private clearQueue(): void {
this.queue = [] ;
this.inProcess = [];
this.failed = [];
this.successfull = [];
}
}
export const RolloutStore = new RolloutStoreClass();
I get the issue on the clearQueue Function exactlly on this. queue
Error is:
In the "never []" type, the following properties of the "Observable Array " type are missing: "spliceWithArray, clear, replace, remove, toJSON".
You either need to make your queue (and other fields) regular array, all observability will still work.
Or use .clear() method inside clearQueue:
private clearQueue(): void {
this.queue.clear();
// ...
}
One more thing: when you use makeAutoObservable you don't need to explicitly mark action's observable's and so on, you can just drop all the decorators:
class Foo {
// Don't need explicit observable decorator
public queue: IDeploymentProject[] = [];
// ...
constructor() {
makeAutoObservable(this);
}
// You can remove action decorator
private clearQueue(): void {
this.queue = [] ;
this.inProcess = [];
this.failed = [];
this.successfull = [];
}
}

Type of the parameter must be a class annotated with #Entity or a collection/array of it./ while converting ArrayList<Float>

Error was made when I will try to save ArrayList<Float> as column in room database
#Entity(tableName = "Height")
class Height: Serializable {
#PrimaryKey(autoGenerate = true)
var id: Long = 0
#ColumnInfo(name = "data")
var data: ArrayList<Float>? = ArrayList()
}
#Dao
abstract class HeightDao : BaseDao<Height>() {
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insertAllHeight(listOfHeight: ArrayList<Float>)
I have written TypeConverter
#TypeConverter
fun fromArrayListOfFloats(list: ArrayList<Float>?): String {
return list?.joinToString(separator = ";") { it.toString() } ?: ""
}
#TypeConverter
fun toArrayListOfFloats(string: String?): ArrayList<Float> {
return ArrayList(string?.split(";")?.mapNotNull { it.toFloatOrNull() } ?: emptyList())
}
It works fine, when I am convering ArrayList<SomeEntityObject>, but in this case it makes an above error. Please, help!

How to do correct Repository factory?

i have some free days between projects so i decided to learn typescript.
Therefore i want to do a repository factory. The idea is simple, there is some API where i can access models crud actions. It is nice solution to have one generic repository for buisnes models. But still get the model class from CRUD methods.
What is the correct way to do it ? Can someone help me solve this? How to do it right ?
What i want to achive is:
var factory = new RepositoryFactory($resource, 'http://api.com');
var repo = factory.getRepository(User);
var user = repo.getAll();
I starded to do somethink like this:
IEntity:
'use strict';
export interface IEntity {
id: number;
}
IRepository:
'use strict';
import {IEntity} from "./IEntity";
export interface IRepository<T extends IEntity> {
getAll(params:Object): T[];
getById(id:number): T;
create(data:Object): T;
update(id:number, data:{id:number}): T;
remove(id:number): boolean;
}
RepositoryFactory
'use strict';
import {IEntity} from "./IEntity";
import {Repository} from "./Repository";
export default class RepositoryFactory {
protected $resource:any;
protected url:string;
constructor($resource:any, url:string) {
this.$resource = $resource;
this.url = url;
}
public getRepository<T extends IEntity>(model:T):Repository {
return new Repository(this.$resource, this.url, model)
}
}
`Repository`:
'use strict';
import {IRepository} from "./IRepository";
import {IEntity} from "./IEntity";
export default class Repository<T extends IEntity> implements IRepository<T> {
protected $resource:any;
protected resource:any;
protected url:string;
protected model:T;
constructor($resource:any, url:string, model:T) {
this.$resource = $resource;
this.url = url;
this.model = model;
this.resource = this.getResource(model.path);
}
getAll(params:Object):T[] {
let results = this.resource.query((typeof params === 'undefined' ? {} : params), this.onSuccess);
return this.returnPromise(results);
}
getById(id:number):T {
let model = this.resource.get({id: id}, this.onSuccess);
return this.returnPromise(model);
}
create(data:Object):T {
let model = new this.resource(data);
return model.$save().then(this.onSuccess);
}
update(id:number, data:Object):T {
data.id = id;
var model = new this.resource(data);
return model.$update().then(this.onSuccess);
}
remove(id:number):boolean {
var data = {id: id};
var model = new this.resource(data);
return model.$delete().then(this.onSuccess);
}
protected getResource(path:string) {
return this.$resource(this.url + path, {id: '#id'}, {
'update': {
method: 'PUT'
},
'get': {
method: 'GET'
},
'save': {
method: 'POST'
},
'query': {
method: 'GET'
},
'remove': {
method: 'DELETE'
},
'delete': {
method: 'DELETE'
}
});
}
protected onSuccess(response:any) {
if (this.checkPropertyExistence(response, 'data')) {
if (response.data instanceof Array) {
let results = response.data;
for (var key in results) {
if (results.hasOwnProperty(key)) {
results[key] = new this.model(results[key]);
}
}
return results;
} else {
return new this.model(response.data);
}
}
return response;
}
protected transformRequest(obj:Object) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
}
protected returnPromise(object:Object) {
return object.$promise.then(function (result) {
return result;
});
}
protected checkPropertyExistence(obj:Object, paths:string|string[]) {
for (var i = 0; i < paths.length; i++) {
if (!obj || !obj.hasOwnProperty(paths[i])) {
return false;
}
obj = obj[paths[i]];
}
return true;
}
}
User:
'use strict';
import {IEntity} from "./IEntity";
export default class User implements IEntity {
id:number;
name:string;
static _path:string = '/users';
constructor(id:number, name:string) {
this.id = id;
this.name = name;
}
static get path():string {
return this._path;
}
}
Hey guys i have menage to created working example for you.
The example shows how to create repository factory with typescript.
I have prepared this DEMO FIDDLE where you can press RUN on the right side, and the page with single button will appear. After clicking the button console.log will display getAll method results.
In example i have mocked data, simply to show that factory works. If anyone wants to improves it, feel free and welcome to do it!
How it works?
//create factory, you can do it in an abstract controller class
//and later extends controller by it so you can easy get access to repository
var factory = new RepositoryFactory();
//inject path for $resource (unused in example)
//and your model.entity namespace
var repo = factory.getRepository('/users', 'User');
//call repo method
var users = repo.getAll({});
When using angular create RepositoryFactory as a service. Thats all. You may also want to inject $resource to get the right data from API.
And here is a full example code:
interface IEntity {
id: number;
}
class Entity implements IEntity {
private _id:number;
private _name:string;
private _birth:Date;
constructor(parameters: {id:number, name:string, birth:Date}) {
this._id = parameters.id;
this._name = parameters.name;
this._birth = parameters.birth;
}
get id():number {
return this._id;
}
set id(value:number) {
this._id = value;
}
get name():string {
return this._name;
}
set name(value:string) {
this._name = value;
}
get birth():Date {
return this._birth;
}
set birth(value:Date) {
this._birth = value;
}
}
class RepositoryFactory {
public getRepository<T extends IEntity>(path:string, model:string):IRepository<T> {
return new Repository<T>(path, model)
}
}
interface IRepository<T extends IEntity> {
getAll(params:Object): T[];
getById(id:number): T;
create(data:Object): T;
update(id:number, data:{id:number}): T;
remove(id:number): boolean;
}
class Repository<T extends IEntity> implements IRepository<T> {
protected path:string;
protected model:string;
constructor(path:string, model:string) {
this.path = path;
this.model = model;
}
getAll(params:Object):T[] {
let results = [
{id:1, name: 'rafal', birth:new Date()},
{id:2, name: 'szymon', birth:new Date()},
{id:3, name: 'mateusz', birth:new Date()},
];
let entities= [];
for (var key in results) {
if (results.hasOwnProperty(key)) {
let entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(results[key]));
entities.push(entity);
}
}
return entities;
}
getById(id:number):T {
let object = {id:id, name: 'test', birth:new Date()};
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(object));
return entity;
}
create(data:Object):T {
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(data));
return entity;
}
update(id:number, data:Object):T {
var entity = Object.create(window[this.model].prototype);
entity.constructor.apply(entity, new Array(data));
return entity;
}
remove(id:number):boolean {
return true;
}
}
var factory = new RepositoryFactory();
var repo = factory.getRepository('/users', 'Entity');
var users = repo.getAll({});

Paging ListBox with ReactiveUI and Caliburn.Micro

I'm trying to implement a paging mechanism for a listbox using Caliburn.Micro.ReactiveUI with a call to EF using ".Skip(currentPage).Take(pageSize)". I'm new to ReactiveUI and Reactive in general. I'm sure this is supposed to be easy.
I've got a single "SearchParameters" class which I needs to be observed and the search function needs to execute when any of the properties on the SearchParameters object changes.
You can see from the commented-out code that I've tried to define the class as a ReactiveObject as well. The current implementation though is with CM's PropertyChangedBase. The individual properties are bound textboxes in my view using CM's conventions:
public class SearchParameters : PropertyChangedBase
{
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
if (value == _searchTerm) return;
_searchTerm = value;
NotifyOfPropertyChange(() => SearchTerm);
}
}
private int _pageSize;
public int PageSize
{
get { return _pageSize; }
set
{
if (value == _pageSize) return;
_pageSize = value;
NotifyOfPropertyChange(() => PageSize);
}
}
private int _skipCount;
public int SkipCount
{
get { return _skipCount; }
set
{
if (value == _skipCount) return;
_skipCount = value;
NotifyOfPropertyChange(() => SkipCount);
}
}
//private string _searchTerm;
//public string SearchTerm
//{
// get { return _searchTerm; }
// set { this.RaiseAndSetIfChanged(ref _searchTerm, value); }
//}
//private int _pageSize;
//public int PageSize
//{
// get { return _pageSize; }
// set { this.RaiseAndSetIfChanged(ref _pageSize, value); }
//}
//private int _skipCount;
//public int SkipCount
//{
// get { return _skipCount; }
// set { this.RaiseAndSetIfChanged(ref _skipCount, value); }
//}
}
"SearchService" has the following method which needs to execute when any one of SearchParameter's values change:
public async Task<SearchResult> SearchAsync(SearchParameters searchParameters)
{
return await Task.Run(() =>
{
var query = (from m in _hrEntities.Departments select m);
if (!String.IsNullOrEmpty(searchParameters.SearchTerm))
{
searchParameters.SearchTerm = searchParameters.SearchTerm.Trim();
query = query.Where(
x => x.Employee.LastName.Contains(searchParameters.SearchTerm) || x.Employee.FirstName.Contains(searchParameters.SearchTerm)).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize);
}
return new SearchResult
{
SearchTerm = searchParameters.SearchTerm,
Matches = new BindableCollection<DepartmentViewModel>(query.Select(x => new DepartmentViewModel{ Department = x }).Skip(searchParameters.SkipCount).Take(searchParameters.PageSize))
};
});
}
Here's how I've tried to wire all of this up in MainViewModel's ctor and where Rx gets hazy for me:
public class MainViewModel : ReactiveScreen
{
private SearchParameters _searchParameters;
public SearchParameters SearchParameters
{
get { return _searchParameters; }
set
{
if (value == _searchParameters) return;
_searchParameters = value;
NotifyOfPropertyChange(() => SearchParameters);
}
}
{
public void MainViewModel()
{
var searchService = new SearchService();
//default Skip and PageSize values
SearchParameters = new Services.SearchParameters { SkipCount = 0 , PageSize = 10};
var searchParameters = this.ObservableForProperty(x => x.SearchParameters)
.Value()
.Throttle(TimeSpan.FromSeconds(.3));
var searchResults = searchParameters.SelectMany(parameters => searchService.SearchAsync(parameters));
var latestMatches = searchParameters
.CombineLatest(searchResults,
(searchParameter, searchResult) =>
searchResult.SearchTerm != searchParameter.SearchTerm
? null
: searchResult.Matches)
.Where(matches => matches != null);
_departmentViewModels = latestMatches.ToProperty(this, x => x.DepartmentViewModels);
searchParameters.Subscribe(x => Debug.WriteLine(x));
}
}
In the above example the call to SearchAsync doesn't execute. It seems that changes to SearchParameter's properties aren't being observed.
Can anyone tell me what I'm doing wrong here?
Here's how I ended up doing this although I'd be interested in hearing other solutions if anyone has suggestions. I'm not sure if this is the best way but it works:
First, I defined a computed property in my SearchParameters class that returns a string and reevaluates anytime CurrentPage, SkipCount and PageSize are updated from the View:
public string ParameterString
{
get { return String.Format("SearchTerm={0}|SkipCount={1}|PageSize={2}", SearchTerm, SkipCount, PageSize); }
}
Next, in my MainViewModel ctor I simply observe the computed rather than attempting to react to SearchTerm, SkipCount and PageSize individually (which my original question was asking how to do):
var searchTerms = this
.ObservableForProperty(x => x.SearchParameters.ParameterString)
.Value()
.Throttle(TimeSpan.FromSeconds(.3));
var searchResults = searchTerms.SelectMany(parameters => SearchService.SearchAsync(parameters));
var latestMatches = searchTerms
.CombineLatest(searchResults,
(searchTerm, searchResult) =>
searchResult.SearchTerm != searchTerm
? null
: searchResult.Matches)
.Where(matches => matches != null);
Finally, in my SearchService I parse the parameter string to get the current values:
var parameters = searchParameters.Split('|');
var searchTerm = "";
var skipCount = 0;
var pageSize = 0;
foreach (var parameter in parameters)
{
if (parameter.Contains("SearchTerm="))
{searchTerm = parameter.Replace("SearchTerm=", "");}
else if (parameter.Contains("SkipCount="))
{ skipCount = Convert.ToInt32(parameter.Replace("SkipCount=", "")); }
else if (parameter.Contains("PageSize="))
{ pageSize = Convert.ToInt32(parameter.Replace("PageSize=", "")); }
}

Displaying a large list in a Pane in JavaFX

In my programme I've a large array of strings(say 1600) which I want to show as a CheckBox list. The array is actually the location of all the songs in one's PC, and thus can gradually be bigger. I don't wanna use ListView<String> as the CheckBox list is more efficient and above all visually better for my purpose. I'm currently doing the below :
private void listAll() {
songs = MediaManager.getAllSongs();
VBox vb = new VBox();
vb.setSpacing(5);
vb.getStyleClass().add("background");
if (songs != null) {
Service s = new Service() {
#Override
protected Task createTask() {
Task t = new Task() {
#Override
protected Object call() throws Exception {
for (String song : songs) {
addSong(song, vb);
c++;
updateMessage(c+" songs");
}
return null;
}
};
t.messageProperty().addListener((obs,o,n)->{
count.setText(n);
});
return t;
}
};
s.start();
ScrollPane sp = new ScrollPane(vb);
getChildren().add(sp);
}
}
private void addSong(String n, Pane p) {
String toAdd = "";
Media m = new Media(Paths.get(n).toUri().toString());
if (m.getMetadata().get("title") == null || !(m.getMetadata().get("title").equals(""))) {
toAdd = m.getSource().split("/")[m.getSource().split("/").length - 1].replace("%20", " ").replace(".mp3", "");
} else {
toAdd = ((String) m.getMetadata().get("title"));
}
SongBox s = new SongBox(toAdd);
s.setUserData(n);
p.getChildren().add(s);
}
class SongBox extends CheckBox {
public SongBox(String t) {
this();
setText(t);
}
public SongBox() {
super();
setOnAction((ActionEvent evt) -> {
if (isSelected()) {
if (!playNow.isVisible()) {
playNow.setVisible(true);
}
path = (String) getUserData();
selected.add((String) getUserData());
} else {
selected.remove((String) getUserData());
if (selected.size() == 0) {
playNow.setVisible(false);
}
}
});
}
}
First of all, that is not showing the complete array. Whenever I'm going back and returning to it, the number of songs get changed. Secondly, the whole UI is getting sluggish(sometimes also hanging my PC). Moreover, I can't cancel the Service when I've gone to the previous window, as it's always returning false. Anyone have a better approach?

Resources