I am trying to parse a map of QueryDocumentSnapshot into a model class in a flutter. Basically what I'm trying to do is get all movies into one place and Stream them to child classes without calling StreamBuilder in each widget.
class CrudModel extends ChangeNotifier{
final Stream<QuerySnapshot> _movieStream = FirebaseFirestore.instance.collection('movies').snapshots();
}
By using the following method I looking to create a List of Movies, Pass to child classes.
Stream<List<Movie>> get getListOfMovies{
return _movieStream.map((event) => event.docs.map((e) => Movie(
id: e.data()["id"] ?? 2,
title: e.data()["title"] ?? "default",
imageUrl: e.data()["imageUrl"] ?? "default",
description: e.data()["description"] ?? "default",
rating: e.data()["rating"] ?? "default",
year: e.data()["year"] ?? "default",
duration: e.data()["duration"] ?? "default",
)).toList());
}
e.data()["title"] won't let me assign the values saying receiver can be null.
I would also like to know is there any convention that I can use when streaming a list of data into child widgets using Provider?
Thank you for any help!
Related
I'm new to Nuxt. I have an error retrieving data. How can I fix it?
If you are using useFetch without specifying the type of the result, typescript cannot know, what type it is.
You could tell typescript that you are receiving a list of users from an endpoint and what a user looks like by doing something like this:
interface User {
id: number
name: string
email: string
}
const {data: users} = useFetch<User[]>('api.example.com');
useFetch returns the (parsed) body of the response in data. Looking at what the URL https://reqres.in/api/users really provides, you would probably want to access the data attribute of the body. You'd need something like this to get to the list of users:
interface User {
id: number
first_name: string
last_name: string
email: string
}
const { data: body } = useFetch<{ data: User[] }>('https://reqres.in/api/users')
const users = body.value?.data ?? []
I am trying to build an application using .Net and GraphQL. I need to get materials. not all of them but with the given Ids. When I pass it via playground or client side, I don't have any problem when I debug but I am not sure how to parse in the server side.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<IntGraphType>("ids");
// Do some action to bring datas
// Send data back
}
What am I missing here is there any methods to parse this in to list of int back?
Instead of using a GraphType for retrieving the argument, use the .NET type you want.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<List<int>>("ids");
// Do some action to bring datas
// Send data back
}
you can use MediatR. Create a Query class and pass it to mediateR. In CQRS, Command is used to write on DB(Create/Delete/Update of CRUD) and Query is used to read from DB(Read of CRUD).
create 'GetMaterialsByIdsQuery' and inside it write your code to get your materials by Id. then use it inside 'resolve' like this:
resolve: async context =>
{
var ids = context.GetArgument<List<int>>("ids");
return await mediator.Send(new GetMaterialsByIdsQuery(ids));
})
another way is that you can return somthing like MaterialsRepository.GetMaterialsByIds(ids) instead of using mediatR. But it is not recommended to use repository here. you can create a service class, inside it use your repository and then use your service here.
I have a MessageModel in which there are a few fields. But the field "edited" does not exist in the document of each message. It is a new field that I want to add later in the future. When I get all messages using stream it throws the error.
Unhandled Exception: Bad state: field does not exist within the DocumentSnapshotPlatform
Is there any way I can check if the field "edited" exists in the model or ignore it?
This is my MessageModel:
factory MessageModel.fromJson(DocumentSnapshot snapshot) => MessageModel(
chatId: snapshot["chat_id"],
messageId: snapshot["message_id"],
userId: snapshot["user_id"],
you: snapshot["you"],
time: snapshot["time"],
seen: snapshot["seen"],
type: snapshot["type"],
message: snapshot["message"],
fileURL: snapshot["file_url"] ?? "" ,
thumbnail: snapshot["thumbnail"] ?? "" ,
isUploading: RxBool(false),
isPlaying: RxBool(false),
file: File("").obs,
thumb: Uint8List(5).obs,
edited: snapshot["edited"]
);
I am using this stream to get all my messages from firestore. Or can I check here if the field exists?
Flutter code:
Stream<List<MessageModel>> getMessages() {
Stream<QuerySnapshot> stream =
CollectionReferences.chatRef.doc(userID.value).collection("messages").orderBy("time", descending: true).snapshots();
return stream.map((data) => data.docs.map((e) => MessageModel.fromJson(e)).toList());
}
If you capture the data as a map, you can check if the map contains a key:
edited: (snapshot.data() as Map<String, dynamic>).containsKey("edited") ?
snapshot["edited"] : null
I'm using Redux and ImmutableJS to manage the state of my app. I've created the following two Records:
export const OrderRecord = Record({
id: null,
productId: null,
amount: 1,
});
export const ProductRecord = Record({
id: null,
name: '',
price: 0,
});
My global state is normalized based on the normalizr approach like this:
const state = {
entities: {
orders: new OrderedMap(new Map({
1: new OrderRecord(createOrderItem(1, 1)),
})),
products: new OrderedMap(new Map({
1: new ProductRecord(createProductItem(1)),
})),
},
};
I'm using this specification for testing purposes.
Now I'm trying to make some selects with computed fields using Reselect.
export const getVisibleOrders = createSelector(
[getProducts, getOrders],
(products, orders) => {
orders.map(order => {
const product = products.get(order.productId.toString());
if (!product) {
return order;
}
const totalPrice = order.amount * product.price;
order.set('productName', product.name);
order.set('totalPrice', totalPrice);
return order;
});
}
);
, but I get the following error message:
Error: Cannot set unknown key "productName" on Record
I know the reason - Record cannot contain any undefined keys, but my question is: Is there any suggested approach how gracefully solved this problem?
I don't want to extend my Records to support this kind of computed parameters (product.name and totalPrice).
I don't want to keep the static and computed parameters in one place, because for example the 'productName' parametr is from "Product" entity and not from "Order" entity.
Thank you.
The whole point of using Immutable.Record is to not let you add new keys to your record, hence the error message you get. And the whole point of selectors is to expose such "computed" property if you want to consume them outside. In your case, you can simply return a new Map() object or a new record type if you need to use the dotted syntax :
return Map({
productName: 'foobar'
}).merge(order)
I'm getting the error "_id is not allowed by the schema" when trying to use an autoform to update a collection via a ValidatedMethod.
As far as I can see from this example and the official docs there is no expectation for my schema to include the _id field, and I wouldn't expect to be updating the id from an update statement, so I have no idea why this error is happening.
If I switch from using the validated method to writing directly to the collection (with a schema attached to the collection that doesn't have the id in) everything works as expected, so I'm assuming the issue is with my the validate in my ValidatedMethod.
Any idea what I'm doing wrong?
Template: customer-edit.html
<template name="updateCustomerEdit">
{{> quickForm
collection="CustomerCompaniesGlobal"
doc=someDoc
id="updateCustomerEdit"
type="method-update"
meteormethod="CustomerCompanies.methods.update"
singleMethodArgument=true
}}
</template>
Template 'code behind': customer-edit.js
Template.updateCustomerEdit.helpers({
someDoc() {
const customerId = () => FlowRouter.getParam('_id');
const instance = Template.instance();
instance.subscribe('CustomerCompany.get', customerId());
const company = CustomerCompanies.findOne({_id: customerId()});
return company;
}
});
Update Validated Method:
// The update method
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.update',
// register a method for validation, what's going on here?
validate: new SimpleSchema({}).validator(),
// the actual database updating part validate has already been run at this point
run( newCustomer) {
console.log("method: update");
return CustomerCompanies.update(newCustomer);
}
});
Schema:
Schemas = {};
Schemas.CustomerCompaniesSchema = new SimpleSchema({
name: {
type: String,
max: 100,
optional: false
},
email: {
type: String,
max: 100,
regEx: SimpleSchema.RegEx.Email,
optional: true
},
postcode: {
type: String,
max: 10,
optional: true
},
createdAt: {
type: Date,
optional: false
}
});
Collection:
class customerCompanyCollection extends Mongo.Collection {};
// Make it available to the rest of the app
CustomerCompanies = new customerCompanyCollection("Companies");
CustomerCompaniesGlobal = CustomerCompanies;
// Deny all client-side updates since we will be using methods to manage this collection
CustomerCompanies.deny({
insert() { return true; },
update() { return true; },
remove() { return true; }
});
// Define the expected Schema for data going into and coming out of the database
//CustomerCompanies.schema = Schemas.CustomerCompaniesSchema
// Bolt that schema onto the collection
CustomerCompanies.attachSchema(Schemas.CustomerCompaniesSchema);
I finally got to the bottom of this. The issue is that autoform passes in a composite object that represents the id of the record to be changed and also a modifier ($set) of the data, rather than just the data itself. So the structure of that object is along the lines of:
_id: '5TTbSkfzawwuHGLhy',
modifier:
{
'$set':
{ name: 'Smiths Fabrication Ltd',
email: 'info#smithsfab.com',
postcode: 'OX10 4RT',
createdAt: Wed Jan 27 2016 00:00:00 GMT+0000 (GMT Standard Time)
}
}
Once I figured that out, I changed my update method to this and everything then worked as expected:
// Autoform specific update method that knows how to unpack the single
// object we get from autoform.
update = new ValidatedMethod({
// register the name
name: 'CustomerCompanies.methods.updateAutoForm',
// register a method for validation.
validate(autoformArgs) {
console.log(autoformArgs);
// Need to tell the schema that we are passing in a mongo modifier rather than just the data.
Schemas.CustomerCompaniesSchema.validate(autoformArgs.modifier , {modifier: true});
},
// the actual database updating part
// validate has already been run at this point
run(autoformArgs)
{
return CustomerCompanies.update(autoformArgs._id, autoformArgs.modifier);
}
});
Excellent. Your post helped me out when I was struggling to find any other information on the topic.
To build on your answer, if for some reason you want to get the form data as a single block you can use the following in AutoForm.
type="method" meteormethod="myValidatedMethodName"
Your validated method then might look something like this:
export const myValidatedMethodName = new ValidatedMethod({
name: 'Users.methods.create',
validate(insertDoc) {
Schemas.NewUser.validate(insertDoc);
},
run(insertDoc) {
return Collections.Users.createUser(insertDoc);
}
});
NB: The Schema.validate() method then requires an Object, not the modifier as before.
I'm unclear if there are any clear advantages to either method in general.
The type="method-update" is obviously the way you want to go for updating documents because you get the modifier. The type="method" seems to be the best way to go for creating a new document. It would likely also be the best option in most cases where you're not intending to create a document from the form data.