I'm transitioning from SQLite to Firebase and in doing so, I have a lot of pre-existing data that I need to set into different views.
Initial view: An alphabetical list (no duplicates), showing letters for only those resources that exist below it (meaning if there is no resource that starts with X, X should not show up at all).
Secondary view: Once you tap on a letter, it expands accordion style to reveal resources (no duplicates) that start with that letter.
My Firebase query is right in that I'm getting the correct data, but how do I use it? Ideally what I'd like to do is get all the topics, drop them in a TreeSet to eliminate duplicates and sort them automatically, then do the same thing but just getting the first letter. I cant modify a set, or an array, or a list from an inner class, but I can't access the same if I instantiate from inside either (and it doesn't matter because it gets called fresh for every single object so it's always a new set).
Is there a best practice I'm missing?
mDatabase = FirebaseDatabase.getInstance();
mRootRef = mDatabase.getReference();
mQuoteRef = mRootRef.child("quotes");
Query topicQuery = mQuoteRef.orderByChild("Topic");
topicQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
TreeSet<String> topicList = new TreeSet<>(); <--obv not right
TreeSet<String> firstLetterList = new TreeSet<>();
GetQuoteInfo quote = dataSnapshot.getValue(GetQuoteInfo.class);
if (quote.getTopic() != null) {
topicList.add(quote.getTopic());
firstLetterList.add(quote.getTopic().substring(0, 1));
}
}
}
magicalListThatContainsAllOfMyFirebaseDataThatICanNowManipulate;
When you retrieve data from a Firebase database, you retrieve it as a Map, and not as a Set. So in order to solve your problem, change the way in which you retrieve the data. Because Firebase is a NoSQL database, better said a JSON database, everything is structured as key and value. So, change the Set with a Map and your problem will be solved.
Hope it helps.
Related
I am trying to create a list view using Recycler View and display a list. Lets say what I am trying to display is like a typical chat screen - image, message, sender_name etc.
So all this data is stored in Firebase Realtime Database. I am also using ViewModel and would like to use DiffUtil for efficiency - since messages can be deleted, edited, starred, new ones added etc.. Due to DiffUtil, I am using a ListAdapter.
The issue I am facing is that the ListAdapter needs a List and I use a ChildEventListener.
How do I now observe changes from Firebase using LiveData and then update my list so that I can pass back into my DiffUtil? So if I add a new message, I'd like that to be added to my RecyclerView and I'd like to do that using DiffUtil since the list can also be updated.
What I found through research was that I might need to use Room for this purpose and observe the query for changes - so observe something like a getAllMessages() method which would return the complete list and then I can use that to pass into my DiffUtil. That sounds like an overkill to me - the implementation of Room.
Any pointers or suggestions on how I can achieve my need?
Here is the structure of my db:
if you dare to start programming an app, you won't be able to get along with Room soon. if you take this step, SQL knowledge is necessary as well...so don't be shy and just dare to do it
GGK
So I figured this out by doing the following in my ViewModel.
private val _message = MutableLiveData<List<TextListModel>>()
val message: LiveData<List<TextListModel>>
get() = _message
//...
private val eventListener =
object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
Timber.e("Error!! ${databaseError.toException()}")
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
val listOfMessages : MutableList<TextListModel> = mutableListOf()
for(msg in dataSnapshot.children) {
val item = msg.getValue(TextListModel::class.java)
listOfMessages.add(item!!)
}
_message.value = listOfMessages
}
}
In my fragment I observe message and perform adapter.submitList(newList) when there is an update.
This downloading of data using ValueEventListener is concerning though since it pulls the data from under the node. I am checking on how to use ChildEventListener for my purposes.
EDIT
Seems like Value events don't download the full tree over! Value events work with the data in the local state and only downloads the delta.
Some more information in this Youtube Video from 2016!
I've created a lookup with two columns, first one containing and integer which works just fine but the second one has a long name and this is where the problem arises. Users should horizontally scroll in order to check the entire string and even in that case, the column's width is not big enough to display the whole data.
I've found this :
Adjusting column width on form control lookup
But i don't understand exactly where and what to add.
I am not sure but maybe I have to add the fact that this lookup is used on a menu item which points to an SSRS report, in the parameters section.
Update 1:
I got it working with a lookup form called like this :
Args args;
FormRun formRun;
;
args = new Args();
args.name(formstr(LookupOMOperatingUnit));
args.caller(_control);
formRun = classfactory.formRunClass(args);
formRun.init();
_control.performFormLookup(formRun);
and in the init method of this form i added:
public void init()
{
super();
element.selectMode(OMOperatingUnit_OMOperatingUnitNumber);
}
meaning the field i really need.
I am not sure i understand the mechanism completely but it seems it knows how to return this exact field to the DialogField from where it really started.
In order to make it look like a lookup, i have kept the style of the Design as Auto but changed the WindowType to Popup, HideToolBar to Yes and Frame to Border.
Probably the best route is do a custom lookup and change the extended data type of the key field to reflect that. In this way the change is reflected in all places. See form FiscalCalendarYearLookup and EDT FiscalYearName as an example of that.
If you only need to change a single place, the easy option is to override performFormLookup on the calling form. You should also override the DisplayLength property of the extended data type of the long field.
public void performFormLookup(FormRun _form, FormStringControl _formControl)
{
FormGridControl grid = _form.control(_form.controlId('grid'));
grid.autoSizeColumns(false);
super(_form,_formControl);
}
This will not help you unless you have a form, which may not be the case in this report scenario.
Starting in AX 2009 the kernel by default auto-updates the control sizes based on actual record content. This was a cause of much frustration as the sizes was small when there was no records and these sizes were saved! Also the performance of the auto-update was initially bad in some situations. As an afterthought the grid control autoSizeColumns method was provided but it was unfortunately never exposed as a property.
you can extends the sysTableLookup class and override the buildFromGridDesign method to set the grid control width.
protected void buildFormGridDesign(FormBuildGridControl _formBuildGridControl)
{
if (gridWidth > 0)
{
_formBuildGridControl.allowEdit(true);
_formBuildGridControl.showRowLabels(false);
_formBuildGridControl.widthMode(2);
_formBuildGridControl.width(gridWidth);
}
else
{
super(_formBuildGridControl);
}
}
I am developing an application for generating estimates on products such as cars.
So, when a car make an model is selected, I need to present various options to the user (options may be in different groups like Wheels, Seating Upholstery, Trunk Accessories)
Depending upon the group, the customer may pick one or more options in that group; if a certain option is selected - some other options may get disabled; not all options apply to every make an model
So, there are several rules to be defined for different groups to indicate what combination is allowed and what is not allowed?
How should I go about designing the database for this and is there a pattern that I can leverage as I develop this application?
I solved a similar requirement with the following structure, rewritten in your terms above:
Parts
Groups
Car
With the following notes:
Parts are standalone in their own right, each with a part number.
A car template is standalone in its own right.
Parts can be added to a option group, and a number of options groups belongs to a car.
An option group cannot exist without a car.
A group can depend on another group
I need to protect against circular references
I started out playing with my model by writing the test case before i wrote the class code (Test Driven Development), which gave me code (in C#) as:
var dieselEngine = new Sku("diesel 2_litre,",1000);
var petrolEngine2 = new Sku("petrol_2_litre",800);
var petrolEngine25 = new Sku("petrol_25_litre",900);
var petrolTurbo = new Sku("petrol_turbo",2000);
var dieselTurbo = new Sku("diesel_turbo",2000);
var car = new Car("myCar");
car.AddGroup("Engines");
car.AddSkuToGroup("Engines", diselEngine);
car.AddSkuToGroup("Engines", petrolEngine2);
car.AddSkuToGroup("Engines", petrolEngine25);
car.AddGroup("Turbos");
car.AddSkuToGroup("Turbos", petrolTurbo);
car.AddSkuToGroup("Turbos", dieselTurbo);
car.SetRequirement(diselEngine, dieselTurbo);
car.SetRequirement(petrolTurbo, petrolEngine2);
car.SetRequirement(petrolTurbo, petrolEngine25);
I add the dependency option on the groups, rather than on the Sku, since a part may exist across multiple cars but may have different dependencies for each specific car.
I have to put everything through the root car object, which will check and enforce all my business rules (such as checking for and protecting against circular references).
Should all access via the car object feel clunky, you could always have the car.AddGroup function return a group to make the code make more sense to read:
var engines = car.AddGroup("Engines");
engines.AddSkuToGroup(diselEngine);
engines.AddSkuToGroup(petrolEngine2);
engines.AddSkuToGroup(petrolEngine25);
But do not forget the business rules can only be enforced by the car, since the car has visibility of all the components. So we always chain up via the root:
class ConfigurableProduct
{
List<Group> groups = new List<Group>();
Group NewGroup(string name)
{
var group = new Group(this, name);
this.groups.Add(group);
return group;
}
bool ContainsSku(string skuId)
{
foreach (var group in this.Groups)
{
if (group.ContainsSku(skuId))
return true;
}
return false;
}
}
class Group
{
Group(ConfigurableProduct parent, string name)
{
this.parent = parent;
this.name = name;
}
string name;
List<string> skuIds = new List<string>();
ConfigurableProduct parent;
void AddSkuToGroup(string skuId)
{
// enforce invariants via parent, call functions as reuqired
if (this.parent.containsSku(skuId))
throw new Exception("SKU already exists in this configurable template, cannot exist twice");
// do other things, like check circular references etc, all via this.parent
}
bool ContainsSku(string toFind)
{
foreach (var skuId in this.skuIds)
{
if (skuId == toFind)
return true;
}
return false;
}
}
For the actual database storage i would worry about persistence last, it could be a text file, MSSQL database, MySQL database, MongoDB, there are so many options.
I find it always useful to concentrate on how i want to use the code in my application, and not the specifics of how the database needs to be used, as the storage can abstracted via a repository interface that returns a class (a plain old POCO class, but in this case we have started to flesh out with business logic to protect against invalid states).
For the front end, you may want to push this all down via JSON to something like angular or knockout that can render the options available dynamically, and show or hide different elements depending on the dependencies between groups.
Working fronted example
I am not sure what front end binding you are using (or if you will only be using razor, in that case you will need to store state on the server and refresh each selection), but I have provided an example using Knockoutjs here: http://jsfiddle.net/g18c/5jt9bwsv/1/ with working dependencies and dynamic javascript object builder.
Loops through provided JSON products by group
Creates calculated fields that change depending on a target dependency
Binds to a view via knockout
The selected SKUs could then simply be passed up to the server, any business rules can also be implemented in the front end javascript.
Of course anything data that is sent from the client to the server would need to be validated by building up the product graph on the server and checking that the provided SKUs are valid (i.e. you wouldn't want to allow a Diesel Turbo to be selected for a Petrol Engine).
Im working on a programming project, I want to store some objects in a list but, but I cant get rid of the duplicates.
This is my object
nd = nodeAddress16=0x10,0x03, nodeAddress64=0x00,0x13,0xa2,0x00,0x40,0x6f,0x8d,0xfc, rssi=-47, nodeIdentifier=
[0x10,0x03]
The code is inside a thread, so the code is looping.
private void handleAtCommandResponse(XBeeResponse response) {
//TODO Implement your code here, to handle particular AT command responses, if desired.
System.out.println("NetworkNode: Received AT Response:"+((AtCommandResponse)response).getCommand());
if (response.getApiId() == ApiId.AT_RESPONSE) {
NodeDiscover nd = NodeDiscover.parse((AtCommandResponse)response);
System.out.println("Node discover response is: " + nd);
nodeDiscoverList.add(nd); //add to list, but gives duplicates of nd.
//add to list if not already in it
//if already in list replace that object with the new object
//duplicate objects are not allowed ==> only one object in the list can contain a specific address.
// Only addresses are static values, other values may change over time.
}
else {
log.debug("Ignoring unexpected response: " + response);
}
}
Without understanding the rest of the system to help determine why handleAtCommandResponse is being invoked multiple times with the same response, note that you can use a Set implementation like HashSet instead of a List to keep from storing duplicate objects. You may need NodeDiscover to implement hashCode() if it does not already.
A Setdoes not allow duplicate objects. If you call put() with the same object (rather, an object whose hashCode() is equal to that of another object in the set) twice, the first instance is replaced by the second.
I am new to vaadin and have a databinding problem. I have posted allready in the vaadin forum, but no answer up to now.
if you answer here, I will of course reward it anyway.
https://vaadin.com/forum/-/message_boards/view_message/1057226
thanks in advance.
greets,
Andreas
Additional information: I tried allready to iterate over the items in the container, after pressing a save button. After deleting all original elements in the model collection, and adding copies from the container, the GUI breaks. Some other GUI elements do not respond anymore.
I have personally never used ListSelect, but I found this from the API docs:
This is a simple list select without, for instance, support for new items, lazyloading, and other advanced features.
I'd recommend BeanItemContainer. You can use it like this:
// Create a list of Strings
List<String> strings = new ArrayList<String>();
strings.add("Hello");
// Create a BeanItemContainer and include strings list
final BeanItemContainer<String> container = new BeanItemContainer<String>(strings);
container.addBean("World");
// Create a ListSelect and make BeanItemContainer its data container
ListSelect select = new ListSelect("", container);
// Create a button that adds "!" to the list
Button button = new Button("Add to list", new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
container.addBean("!");
}
}
// Add the components to a layout
myLayout.addComponent(button);
myLayout.addComponent(select);
The downside (or benefit, it depends :) of this is that you can't add duplicate entries to a BeanItemContainer. In the example above the exclamation mark gets only added once.
You can get a Collection of Strings by calling:
Collection<String> strings = container.getItemIds();
If you need to support duplicate entries, take a look at IndexedContainer. With IndexedContainer you can add a String property by calling myIndexedContainer.addContainerProperty("caption", String.class, ""); and give each Item a unique itemId (or let the container generate the id's automatically).
Im not sure I understand your problem but I belive that it might be that you haven't told the controller to repaint. You do this be setting the datasource like this after the save event has occured.
listSelect.setContainerDataSource(listSelect.getContainerDataSource());