SwiftUI Strange results with nested Foreach loops with LazyVGrid - multidimensional-array

From a lot of comments it is well known, that nested Foreach loops sometimes don't work as expected. However, up to now I didn't find a solution for the problem.
The code example should produce a vie with the content
ad ae af
bd be bf
However, only the first line is displayed.
The reason is quite clear formulated by the error messages:
LazyVGridLayout: the ID d is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID e is used by multiple child views, this will give undefined results!
LazyVGridLayout: the ID f is used by multiple child views, this will give undefined results!
Does somebody know how to solve the problem?
struct ContentView: View {
let rowData = ["a", "b"]
let colData = ["d", "e", "f"]
let colums = [GridItem(), GridItem(), GridItem()]
var body: some View {
LazyVGrid(columns: colums) {
ForEach(rowData, id: \.self) { rowItem in
ForEach(colData, id: \.self) {colItem in
Text(rowItem + colItem)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

The LazyVGrid layout is getting confused with the ID of the Views it is presenting.
So give it a unique id for each Text view. Like this:
Text(rowItem + colItem).id(rowItem + colItem)
Works for me.

Related

MVC minification seems to creating duplicate variable names

This question seems to be more or less a duplicate of this one, but that one received no answers and is over 2 years old so I don't know what the protocol is (I tried to find out).
Anyway, I've written an ASP.NET MVC5 web app and it all works fine in debug. After publishing to the server with the Release configuration I started seeing the error "Uncaught ReferenceError: Cannot access 'n' before initialization".
After many hours of trawling through the code I think I've isolated the issue. I have this small function (it's a Knockout view model, but that's irrelevant):
eventIndexViewModel = function (params) {
let self = this;
// Map values from params object to properties of self
for (const [key, value] of Object.entries(params)) {
self['_' + key] = value;
}
self.eventsView = ko.observable(self._eventsView);
self.calendarToggleButtonClass = ko.pureComputed(function () {
return self.eventsView() === "calendar" ? "active" : "";
});
self.tableToggleButtonClass = ko.pureComputed(function () {
return self.eventsView() === "table" ? "active" : "";
});
};
After being minified and published to the server, if I view the source in the dev tools console it looks like this:
eventIndexViewModel = function(n) {
let t = this;
for (const [n,i] of Object.entries(n))
t["_" + n] = i;
t.eventsView = ko.observable(t._eventsView);
t.calendarToggleButtonClass = ko.pureComputed(function() {
return t.eventsView() === "calendar" ? "active" : ""
});
t.tableToggleButtonClass = ko.pureComputed(function() {
return t.eventsView() === "table" ? "active" : ""
})
}
It is overkill to map the properties for the params object in this way in this particular instance, but I have much larger view models with many more properties in the same project and I want to keep them code consistent, so go with it.
Unless I'm misunderstanding something, the minified version has renamed both the params variable and the key variable in the for statement to n, and I think that is what is causing my error. Certainly, that line with the for statement is where the error is thrown.
Am I understanding the cause of this problem correctly? If so, is it a bug in the minification process? And either way, how can I get around it?

GraphQL Syntax for mapping array on mutation field

I'm looking for advice on syntax. In my query, I need my backend (Graphlite, Symfony) to get the child items. The way I get the regular item is as below, and works correctly. I'm not well versed enough in GraphQL / Apollo / VueJS / GraphQLite to know which technology owns the "{id: $itemId}" syntax, nor do I know what it is called, so I can't find any info about it.
insertItem: gql` mutation insertItem(
$itemId: Uuid!,
$childItemIds: [Uuid!]!
) {
insertItem(
item: {id: $itemId},
childItems: { id: { in : { $childItemIds} }
){
// ... stuff
}
}`
So, given that the {id: $itemId} correctly works for getting an item, I assume there is some graphQL syntax that would work to apply the childItemIds to get the childItems. Is there a name for this type of mapping? What would the syntax be?
As per discussion with #UjinT34, since this couldn't be done inline with GraphQL, I created an array of ItemInput before calling the mutation:
for (var i = 0, len = item.childItems.length; i < len; i++) {
childItems.push({ id : item.childItems[i].id } )
}
The original query then became:
insertItem:`` gql` mutation insertItem(
$itemId: Uuid!,
$childItemIds: [ItemInput!]!
) {
insertItem(
item: {id: $itemId},
childItems: $childItemIds
){
// ... stuff
}
}
Once I understood that the {id: $item} wasn't a magic GQL/GraphQLite syntax and was simply an inline javascript object being created, the solution became fairly simple.

App crashes when I attempt to have a dynamic value in spinner

I have the following code and was wondering why it crashes the app, I was hoping you could help me figure out what was going on.
The code below is not exact aside from the spinner code.
The idea is to have the spinner racePicker's array populated by FireStore document IDs, as shown below:
val db = FirebaseFirestore.getInstance()
val raceArray = ArrayList()
raceArray.add("Select race...")
db.collection("races").get().addOnSuccessListener {
DocumentSnapshot ->
for (document in DocumentSnapshot) {
raceArray.add(document.id)
Log.e("info", "raceArray contains values $raceArray")
}
This is a rough approximation. I may have set it .toString() or maybe used .addAll vs .add in the raceArray statement.
(I honestly don't remember exactly if this is how I coded it, but it's close enough to give an idea, I'm typing from memory at the moment).
My intention was to use it like so:
racPicker.onSelectedItemListener = object :
OnItemSelectedListener {
override fun onNothingSelected() {
}
override fun onItemSelected(parent: AdapterView<*>?, view:
View?, position: Int, id: Long) {
val selection = parent?.getItemAtPosition(position).toString()
when (selection) {
"$selection" -> raceArray.remove("Select race...").also{
statAllocator(selection) }
}
}
}
For some reason it crashes, but if I assign a literal such as "race name" -> fun, "2nd race name" -> fun, etc it works.
Would it be better to use
if (selectedItem.equals("$selection") {
// do stufd
}
instead? Or is it absolutely necessary to call each and every when case/statement as a literal string? I essentially am looking for a way to have the spinner's selected item (which is an array of document names generated from FireStore database) then "check for itself" and trigger the other functions.

How to export a table as google sheet in Google app maker using a button

I've looked extensively and tried to modify multiple sample sets of codes found on different posts in Stack Overflow as well as template documents in Google App Maker, but cannot for the life of me get an export and en email function to work.
UserRecords table:
This is the area where the data is collected and reviewed, the populated table:
These are the data fields I am working with:
This is what the exported Sheet looks like when I go through the motions and do an export through the Deployment tab:
Lastly, this is the email page that I've built based on tutorials and examples I've seen:
What I've learned so far (based on the circles I'm going round in):
Emails seem mostly straight forward, but I don't need to send a message, just an attachment with a subject, similar to using the code:
function sendEmail_(to, subject, body) {
var emailObj = {
to: to,
subject: subject,
htmlBody: body,
noReply: true
};
MailApp.sendEmail(emailObj);
}
Not sure how to change the "body" to the exported document
To straight up export and view the Sheet from a button click, the closest I've found to a solution is in Document Sample but the references in the code speak to components on the page only. I'm not sure how to modify this to use the table, and also what to change to get it as a sheet instead of a doc.
This may seem trivial to some but I'm a beginner and am struggling to wrap my head around what I'm doing wrong. I've been looking at this for nearly a week. Any help will be greatly appreciated.
In it's simplest form you can do a Google sheet export with the following server script (this is based on a model called employees):
function exportEmployeeTable() {
//if only certain roles or individuals can perform this action include proper validation here
var query = app.models.Employees.newQuery();
var results = query.run();
var fields = app.metadata.models.Employees.fields;
var data = [];
var header = [];
for (var i in fields) {
header.push(fields[i].displayName);
}
data.push(header);
for (var j in results) {
var rows = [];
for (var k in fields) {
rows.push(results[j][fields[k].name]);
}
data.push(rows);
}
if (data.length > 1) {
var ss = SpreadsheetApp.create('Employee Export');
var sheet = ss.getActiveSheet();
sheet.getRange(1,1,data.length,header.length).setValues(data);
//here you could return the URL for your spreadsheet back to your client by setting up a successhandler and failure handler
return ss.getUrl();
} else {
throw new app.ManagedError('No Data to export!');
}
}

Flow error when using react-apollo Query component render prop

I have the following code:
import { Query } from 'react-apollo';
type Post = {
id: string
};
interface Data {
posts: Array<Post>;
}
class PostsQuery extends Query<Data> {}
When using the above as follows:
<PostsQuery query={POSTS_QUERY}>
{({ loading, data }) => {
...
{data.posts.map(...)}
...
}
</PostsQuery>
I get the following error from flow:
Error:(151, 27) Cannot get 'data.posts' because property 'posts' is missing in object type [1].
Any idea why?
I did use flow-typed to add apollo-client_v2.x.x.js to my project by the way
Solution to the problem
Continued from the answer explaining how to make a verifiable example and research the problem.
So it looks like this part of react-apollo isn't typed in such a way to make accessing the data contents straightforward. Okay, that's fine, we can take their recommendation on destructuring and check for empty data. At the same time, we can also add an id property to the Post type so flow stops complaining about that:
(Try - Scroll to bottom for relevant code)
type Post = {
id: string,
title: string;
};
...snip...
// Look ma, no errors
class Search extends React.Component<{}> {
render = () => (
<PostsQuery query={QUERY}>
{({ loading, error, data }) => {
if (error) {
return <p>Error</p>
}
if (loading) return <p>Loading</p>
const nonNullData = (data || {})
const dataWithAllPosts = {allPosts: [], ...nonNullData}
const {allPosts} = dataWithAllPosts
if (allPosts.length == 0) {
return <p>Empty response or something</p>
}
return (
<div>
{allPosts.map(post => {
return <div key={post.id}>{post.title}</div>;
})}
</div>
);
}}
</PostsQuery>
);
}
I'm not familiar with the react-apollo library, so I'm not sure how you want to handle that case where there are no posts. I just added a message as seen above. It's entirely possible that the case never occurs (again, you would know better than I do). If that's the case, you might want to skip some of the above steps and just assert the desired type with a cast through any.
How to make a reproducible example and research the problem
So the first thing we need to do while analyzing these types is to go lookup the typedefs in the flow-typed repo. I went ahead a copy-pasted the react-apollo typedefs into flow.org/try, modified them slightly (added an any somewhere, set gql to any), and was able to replicate your errors:
(Try - Scroll to the bottom for your code)
Referencing the relevant lines of the QueryRenderProps type, we can see why flow is throwing the error:
{
data: TData | {||} | void,
...
}
It looks like data can either be TData (probably what you want), an empty object, or undefined. Cross checking this with the typescript typings for react-apollo, we can see why the type is the way it is:
{
...
// we create an empty object to make checking for data
// easier for consumers (i.e. instead of data && data.user
// you can just check data.user) this also makes destructring
// easier (i.e. { data: { user } })
// however, this isn't realy possible with TypeScript that
// I'm aware of. So intead we enforce checking for data
// like so result.data!.user. This tells TS to use TData
// XXX is there a better way to do this?
data: TData | undefined;
...
}
Unfortunately, due to the extreme length of these links and stackoverflow's limit on answer lengths, I have to continue my answer in another answer. I guess this answer can serve as an explanation of how to start debugging the problem.

Resources