This seems to be a very awkward problem:
I am accessing my modal.service.ts using the following code:
this.modalService.add('test');
My modal.service.ts looks like the following:
import { Injectable } from '#angular/core';
#Injectable({ providedIn: 'root' })
export class ModalService {
private modals: any[] = [];
add(modal: any) {
// add modal to array of active modals
this.modals.push(modal);
}
remove(id: string) {
// remove modal from array of active modals
this.modals = this.modals.filter(x => x.id !== id);
}
open(id: string) {
// open modal specified by id
const modal = this.modals.find(x => x.id === id);
console.log(this.modals)
console.log(this.modals[0])
//modal.open();
}
close(id: string) {
// close modal specified by id
const modal = this.modals.find(x => x.id === id);
modal.close();
}
}
Why does console.log(this.modals[0]) give me undefined when this.modals gives me an output of the array with 'test' being at pos 0?
This is the console output:
It is a problem with (or feature of) browser's console. It shows that 0th element of this.modals is "test" because at the moment of inspecting it is. But at the moment of executing it is empty.
{
const anArray = [];
console.log(anArray);
console.log(anArray[0]);
anArray.push("foo");
}
// Browser's output:
// >>> [] <-- But expanded it will show [0: "foo"].
// >>> undefined
{
const anArray = [];
anArray.push("foo");
console.log(anArray);
console.log(anArray[0]);
}
// Browser's output:
// >>> ["foo"] <-- See. Already pre-filled on display.
// >>> foo
So what you actually have is a race condition. Your this.modals is filled after open is called.
Related
I'm reading about Navigation in Jetpack Compose, and found this example I don't understand.
From the docs:
By using the saveState and restoreState flags, the state and back stack of that item is correctly saved and restored as you swap between bottom navigation items.
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.FriendsList.route) { FriendsList(navController) }
}
}
Specifically, I don't understand how the back stack can be saved if clicking an item in the bottom bar pops the navigation stack to the root.
I would imagine a journey like:
User moves to /FriendsList/Friend(A)/Friend(B)/Friend(C)
User clicks Profile button, resetting the navigation stack to /Profile
User clicks FriendsList button.
Based on the explanation, I would expect the navigation stack to be re-set to /FriendsList/FriendA/FriendB/FriendC, even though the onClick listener seems to set the stack to /FriendsList?
I really don't understand how this can happen, how does the navigation controller link the route to the entire navigation sub-stack? Is item.route changing state containing the full route to /FriendsList/Friend(A)/Friend(B)/Friend(C), or is something else going on? Or do I understand the example wrong?
I suspect maybe the underlying mechanism is that FriendsList contains a nested navigation graph, since the example doesn't actually show any Friend route definitions. The state of this entire nested graph is contained somehow, i.e., something like /FriendsList{FriendA/FriendB/FriendC}, and a move to /FriendsList will unpack this navigation stack. Is that kind of how it works?
The code in the example you gave has only 1 navHost and 1 navController so it is not possible to save the FriendList back stack seperated from the rest back stack.
To achive this behavior you need to declare nested navHosts. Each of them with his own navController and thats it. All the rest is working automatically.
Here is an example
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
Scaffold(
bottomBar = {
BottomNavigation {
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(
navController,
startDestination = Screen.ProfileNavHost.route,
Modifier.padding(innerPadding)
) {
composable(Screen.ProfileNavHost.route) {
val profileNavController = rememberNavController()
NavHost(
navController = profileNavController,
startDestination = Screen.ProfileRoot.route
) {
composable(Screen.ProfileRoot.route) {
ProfileRoot(navController)
}
composable(Screen.ProfileScreen.route) {
ProfileScreen(navController)
}
}
}
composable(Screen.FriendsListNavHost.route) {
val friendsListNavController = rememberNavController()
NavHost(
navController = friendsListNavController,
startDestination = Screen.FriendsListRoot.route
) {
composable(Screen.FriendsListRoot.route) {
FriendsList(navController)
}
composable(Screen.FriendsListFriend.route) {
FriendsListFriend(navController)
}
}
}
}
}
I want to push input to an array as an object, then display them to the DOM as list items but the function I wrote is incomplete.
addTodo is linked to a button.
let todo = [];
function addTodo(e) {
if (e === undefined) {
return console.log("Please enter a task you want added to the list")
}
let task = document.getElementById("input").value;
let newTodo = {id: todo.length, errand: task}
todo.push(newTodo);
display(todo);
todo.shift();
console.log(todo)
e.preventDefault();
}
/* Display() is use to iterate through any array passed in and display each value inside a list item.*/
const display = function (e) {
e.map(function (item) {
let li = document.createElement("li");
let todoItem = document.createTextNode(item.errand);
li.appendChild(todoItem);
list.appendChild(li);
});
};
display(todo);
The current output:
-Go to store
-Go to store
-Drop off mail
The expected output:
-Go to store
-Drop off mail
I have a map component that contains some clickable overlays on the map. Users can click and unclick the overlays on the map to select them and when they do so the app loads some data based on the overlays that are currently selected.
The current structure is as follows:
User clicks the map which executes a function passed to the map as a prop, which takes the current value of the neighborhoods and either adds or removes them from the query string.
The function executes a history.push()
I use a useEffect checking the value of the query param neighborhood and send a request to the backend to fetch the listings if the values have changed.
My issue is that when the user clicks on the map, the function executes but the value pushed to the params is never updated, causing the logic to fail the next time the user clicks on the map.
Relevant Snippets of Code are as follows:
history/param variables:
const { region, state, neighborhood, transactiontype } = useParams();
const location = useLocation();
const { pathname, search } = location;
Function that is passed down to the child map component:
const updateChildComponentHandler = (dataFromChild, addorRemove) => {
SetLastSetter("map");
SetNeighborhoodTypeSelected("custom");
// if a new neighborhood is added, just taking the existing string and adding &`neighborhood`
if (addorRemove === "add") {
let newGroup = ""
if (neighborhood !== "any") {
newGroup = `${neighborhood}&${dataFromChild}`;
SetMapChangeType("add");
}
// if no neighborhood was initially selected, just replacing the "any" with the proper neighborhood
if (neighborhood === "any") {
SetMapChangeType("add");
newGroup = `${dataFromChild}`
}
// pushing the new parameter string
const newPath = pathname.replace(neighborhood, newGroup);
history.push(`${newPath}${search}`);
}
// same concept as above, just removing the neighborhood from the string if it is removed from the map
if (addorRemove === "remove") {
let newGroup;
if (neighborhood !== dataFromChild) {
newGroup = neighborhood.replace(`&${dataFromChild}`, "")
SetMapChangeType("delete");
}
if (neighborhood === dataFromChild) {
newGroup = "any";
SetMapChangeType("add");
}
if (neighborhood.split("&")[0] === dataFromChild && neighborhood !== dataFromChild) {
newGroup = neighborhood.replace(`${dataFromChild}&`, "")
SetMapChangeType("delete");
}
const newPath = pathname.replace(neighborhood, newGroup);
const newerPath = `${newPath}${search}`;
history.push(newerPath);
deleteListings(dataFromChild);
}
SetUpdateMap(true);
}
UseEffect Logic:
useEffect(() => {
const func = async () => {
let neighborhoodParams;
if (neighborhood !== "any") {
neighborhoodParams = neighborhood.replace("%20", " ").split("&")
}
if (neighborhood === "any") {
neighborhoodParams = [];
neighborhoodParams[0] = "any";
}
const neighborhoods = neighborhood.split("%20").join("_");
SetNeighborhoodParams(neighborhoodParams);
SetNeighborhoodParamString(neighborhoods);
if (mapChangeType === "add") {
if (neighborhoodParams.length > 0) {
if (!requestMulti) {
await fetchProperties(transactiontype, neighborhoodParams[neighborhoodParams.length - 1].split(" ").join("_"), filteredRegion, state, filters, "single")
}
if (requestMulti) {
await fetchProperties(transactiontype, neighborhoods, filteredRegion, state, filters, "multi")
SetRequestMulti(false);
}
}
}
}
func();
}, [neighborhood]);
The issue I am experiencing is that when the map initially loads, the neighborhood is set to "any". The first time a user clicks an overlay for a neighborhood, the correct data is sent from the map and the map/data requests update and the URL parameter up top shows the new neighborhood. However, the second time a user clicks, the value of { neighborhood } is not updated and is still set to "any", so the function just replaces the value of { neighborhood } rather than adding it on as per above. I previously coded this with class components and am trying to convert it to hooks, but it seems like there is some dissonance that is causing the map component not to have access to the updated history variable. I am new to react/hooks, and appreciate if anyone could lend some advice.
Thanks!
I am learning Rust. I am creating a desktop app which read thousand/million rows of data in csv file and then transfer them one by one using tauri event.
Result: Rust has no problem read through the file (under 5 seconds). On the frontend side, my React app seems unable to keep up with events. On the screen, the altitude value is updated intermittently.
How to handle this situation is React? or What did I do wrong?
React side:
// App.js
import { listen } from '#tauri-apps/api/event';
import { useEffect, useCallback, useState } from 'react';
import { invoke } from '#tauri-apps/api/tauri'
const App = () => {
const [altitude, setAltitude] = useState("0");
useEffect(() => {
listen('rust-event', myCallback)
}, [])
const myCallback = useCallback((e) => {
console.log(e);
setAltitude(e.payload);
},[])
const handleClick = async () => {
invoke('my_custom_command').catch(error => console.log(error));
};
return (
<div>
<button onClick={handleClick}>Click Me To Start Fetching!</button>
<span>{altitude}</span>
</div>
);
}
export default App;
Tauri side:
// main.rs
use arrow::csv;
use arrow::datatypes::{DataType, Field, Schema};
use std::fs::File;
use std::sync::Arc;
use arrow::array::{StringArray, ArrayRef};
#[tauri::command]
async fn my_custom_command(window: tauri::Window) {
let schema = Schema::new(vec![
Field::new("altitude", DataType::Utf8, false)
]);
// Open file
let file = File::open("src/data.csv").unwrap();
// Get csv Reader using schema
let mut csv = csv::Reader::new(file, Arc::new(schema), true, None, 1, None, None);
// Loop through each row
while let Some(m) = csv.next() {
let n = m.unwrap();
// Get reference of array of a column
let col: &ArrayRef = n.column(0);
// Cast the reference of array to array of string
let col = col.as_any().downcast_ref::<StringArray>().unwrap();
// Get value from the array using index
let v = col.value(0);
println!("{}", col.value(0));
// Send each value through an event
window
.emit("rust-event", v)
.expect("failed to emit");
}
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("failed to run app");
}
I personally do not suggest what you are doing as you can get a stack overflow.
The best would be to emit them in batch, you can fill a local buffer, when there are X elements (or the end is reached), emit the event, and clear the buffer.
Example
#[tauri::command]
async fn my_custom_command(window: tauri::Window) {
// Your code
// [...]
// send 20 elements in the Vec (array)
let should_trigger_at = 20;
// local buffer
let local_buffer: Vec<String> = Vec::new();
// Loop through each row
while let Some(m) = csv.next() {
let n = m.unwrap();
// Get reference of array of a column
let col: &ArrayRef = n.column(0);
// Cast the reference of array to array of string
let col = col.as_any().downcast_ref::<StringArray>().unwrap();
// Get value from the array using index
let v = col.value(0);
println!("{}", col.value(0));
// add the value in the buffer
local_buffer.push(col.value(0));
if local_buffer.len() == should_trigger_at {
// Send each value through an event
window
.emit("rust-event", local_buffer)
.expect("failed to emit");
// reset local buffer
local_buffer = Vec::new();
}
}
// if buffer not empty, lets emit the values
if local_buffer.len() > 0 {
window
.emit("rust-event", local_buffer)
.expect("failed to emit");
}
// [...]
// Your code
}
Please note; doing this will send an Array of String to the Webview instead of a String.
Well, I guess Rust is too fast :) React is unable to handle the speed.
I slow down the event emit with settimeout rust lib and I am happy with it for now.
// Before emit an event, delay it 100 microsecond;
set_timeout(Duration::from_micros(100)).await;
window
.emit("rust-event", v)
.expect("failed to emit");
i have angular 7 component which is tied to a model and there is an array inside that model, the array was populated from a service. and it's populated.
the problem is i can't map over the array although it has elements there.
when i console it it shows the array has element. then i tried to console typeOf(array) it always gives object although it is an array !!.
i tried using this soluation but it didn't help either.
any help please?
export class FooModel {
foo : Foo
bars: Bar[];
}
export class SomeComponent implements OnInit {
model: FooModel;
constructor(private service: ProjectService) {
this.model = new FooModel();
this.model.bars = [];
}
ngOnInit() {
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
});
Console.log(this.model); // the model has data at this point
const arr = this.model.bars.map(a=> {
// never comes here
return a;
});
console.log(arr); // nothing is displayed here
// this works why ??
const arr2 = [1,2,3].map(s=> {
return s;
}
console.log(arr2); // it displays [1,2,3]
}
}
As the request is asynchronous, you might need to place the logic within the subscribe,
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
const arr = this.model.bars.map(a=> {
// never comes here
return a;
});
console.log(arr);
});
subscription is asynchronous so while it is still working the next line operation in the execution stack will be performed in this case the map you have after the subscription meanwhile it is still being populated in the background. You can try mapping in another life cycle hook say viewChecked hopefully it works. #cheers
Please look at the comments
export class FooModel {
foo : Foo
bars: Bar[];
}
export class SomeComponent implements OnInit {
model: FooModel;
constructor(private service: ProjectService) {
this.model = new FooModel();
this.model.bars = [];
}
ngOnInit() {
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
});
// the following starts to execute even before the model is populated above.
const arr = this.model.bars.map(a=> {
// never comes here because this.model.bars is empty at here and the length is 0 and nothing inside map executes
return a;
});
console.log(arr); // nothing is displayed here because it has nothing inside
// this works why ?? because you are using on an array which has some items.
const arr2 = [1,2,3].map(s=> {
return s;
}
console.log(arr2); // it displays [1,2,3]
}
}
So as Sajeetharan suggested, you have keep it inside subscribe()