Passing a value from database to vue component and assigning it to a variable - node.js

I am trying to pass a value from my database and then assign that value to a variable in my Vue Component. This successfully gets the data from the database however i get error when assigning that value to a variable in the component:
"TypeError: Cannot read property 'data' of undefined"
Vue Component:
import TransactionsService from '#/services/TransactionsService'
export default {
components: {
},
data(){
return {
transactions: null,
product: null,
amount: null
}
},
async mounted() {
try{
this.transactions = (await TransactionsService.index()).data.transactions
for( transaction in transactions){
this.amount = transaction.amount
}
console.log(amount)
this.userId = this.$store.state.user.priviledge
} catch(error){
this.error = error.response.data.message
}
}
}
I want to assign the value at transaction.amount to the variable amount

In that for ... in loop, transaction is the item's array index, not the item itself. Here is a more common loop:
// forEach
transactions.forEach(transaction => {
this.amount = transaction.amount
})
This loop will work but still doesn't make sense because you will only keep setting this.amount to the next transaction amount and overwriting the last one. If you intended to add them up you could use:
this.amount += transaction.amount
(Note: It's a good practice to use any other loop type with arrays because index order isn't guaranteed with for ... in. Alternatives are forEach, for, or for ... of)

Related

GraphQL - Apollo Client 3 | Pagination

I'm new to Apollo Client and I'm trying to wrap my head around field policies to implement pagination.
So basically I have a category page where I perform a query that is based on the the slug that I receive from the URL of the page, returns a list of IDs (and I pass them down as props for the product component), for example:
query getProductId($slug: String!) {
slug(where: {slug: $slug}){
products {
Id
}
}
}
from this query I get and array of all the objects containing the IDs of the products.
I can pass a "first: " and "after: {id: }" to the products field and this way I could decide after which product ID I want to query. for example:
query getProductId($slug: String!) {
slug(where: {slug: $slug}){
products(first: 4, after: {id: 19}) {
Id
}
}
}
I know that in my ApolloClient instance I can define a field policy for the cache like this:
const apollo = new ApolloClient({
//...
cache: new InMemoryClient({
typePolicies: {
Query: {
fields: {
products: offsetLimitPagination(["<* keyArgs>"]),
},
},
},
})
})
This is just one random helper function I took, but in my case I think using a cursor based strategy is better since I could use the last ID in the list as cursor, I guess(?)
From here I'm completely lost, the more I read the docs the more I get confused.
{
keyArgs: ["first"],
merge(existing, incoming, { args: { cursor }, readField }) {
const merged = existing ? existing.slice(0) : [];
let offset = offsetFromCursor(merged, cursor, readField);
// If we couldn't find the cursor, default to appending to
// the end of the list, so we don't lose any data.
if (offset < 0) offset = merged.length;
// Now that we have a reliable offset, the rest of this logic
// is the same as in offsetLimitPagination.
for (let i = 0; i < incoming.length; ++i) {
merged[offset + i] = incoming[i];
}
return merged;
},
// // If you always want to return the whole list, you can omit
// // this read function.
// read(
// existing,
// { args: { cursor, limit = existing.length }, readField }
// ) {
// if (existing) {
// let offset = offsetFromCursor(existing, cursor, readField);
// // If we couldn't find the cursor, default to reading the
// // entire list.
// if (offset < 0) offset = 0;
// return existing.slice(offset, offset + limit);
// }
// },
},
},
},
},
}),
});
function offsetFromCursor(items, cursor, readField) {
// Search from the back of the list because the cursor we're
// looking for is typically the ID of the last item.
for (let i = items.length - 1; i >= 0; --i) {
const item = items[i];
// Using readField works for both non-normalized objects
// (returning item.id) and normalized references (returning
// the id field from the referenced entity object), so it's
// a good idea to use readField when you're not sure what
// kind of elements you're dealing with.
if (readField("id", item) === cursor) {
// Add one because the cursor identifies the item just
// before the first item in the page we care about.
return i + 1;
}
}
// Report that the cursor could not be found.
return -1;
}
Let's suppose I use this a field policy for the list of products, how do I go from here? I'm completely lost

node js access value in json result and get the value

i have following json object which i get from API end point
let myjson = { Team1: { SCORE: 10 } }
i want to access the score inside Team but not able to complete as i need to just the result as 10
i have tried following code but not able to get the result
for(var attribute name in JSON.parse(myjson)){
return console.log(attributename+": "+body[attributename]);
}
i also used bellow code
const userStr = JSON.stringify(myjson);
JSON.parse(userStr, (key, value) => {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
});
Not a node developer but why do you need to json.stringify it? Can't you just reach the value with dot notation like this:
myJson.Team1.SCORE
myjson is already an Object, you don't need to do JSON.parse nor JSON.stringify on it.
Just access the property directly:
console.log(myjson.Team1.SCORE)
If you have multiple teams, or want to access it dynamically:
const obj = { Team1: { SCORE: 10 }, Team2: { SCORE: 20 } }
for(const [team, value] of Object.entries(obj)) {
console.log(`${team}: ${value.SCORE}`)
}
you also can use this if it fulfills your query.
here is the code.
let myjson = {Team1: {SCORE:10}, Team2: {SCORE: 20}};
Object.keys(myjson).forEach(function(item) {
console.log(myjson[item].SCORE);
});
Not sure if there can be more teams in that object, so I put here some more complex solution and then the straightforward one.
const myjson = { Team1: { SCORE: 10 }, Team2: { SCORE: 20 } }
const result = Object.keys(myjson).map(key => myjson[key].SCORE);
console.log('For dynamic resolution', result);
console.log('If there is only Team1', myjson.Team1.SCORE);

Cannot read property 'map' of undefined with promise all

I was initially just running one query in node.js but I now need two sets of data so I ran two queries and used Promise.all like this:
Promise.all([products, subcats]);
res.status(200).json({
products,
subcats
});
In React I have:
class ProductList extends Component {
state = {
products: []
};
async componentDidMount() {
const catslug = this.props.match.params.catslug;
const { data: products } = await getCatProducts(catslug);
this.setState({ products: products });
}
When I was only running the one query I was running this without any issue:
this.state.products.map(product => (
Now because I have the 2 queries I need to change it to:
this.state.products.products.map(product => (
But as soon as I do that I get the error:
Cannot read property 'map' of undefined
So, I changed it to this and now it works with no errors:
{this.state.products.products &&
this.state.products.products.map(product => (
My question is why did it work before without having to put in the ... && bit but now I have to use it with the Promise.all in node.js?
It's because of the initial shape of your state
state = {
products: []
};
You see this.state.products is already defined as an array, so it can be mapped over (despite being empty). However this.products.products is NOT an array, it is undefined, so trying to map will return the error you are seeing. The line you have entered
{this.state.products.products &&
this.state.products.products.map(product => (
checks that the array exists before attempting to map it (which wont evaluate to true until your async code finishes, and then the array is defined).
An alternative fix would be to set your initial state shape to match your final state shape. i.e
state ={
products:{
products:[]
}
};
Or if you don't want the nested products property you can change
async componentDidMount() {
const catslug = this.props.match.params.catslug;
const { data } = await getCatProducts(catslug);
this.setState({ products: data.products });
}
and return to your old this.state.products.map()
The issue is in the following line:
const { data: products } = await getCatProducts(catslug);
If I understand correctly, when you were sending one value, you were sending it like:
res.status(200).json(products); // an array
But now you are sending an object, which further contains 2 arrays products and subcats.
What you have 2 do is add below changes to make it work:
const obj = await getCatProducts(catslug);
const products = obj.products
const subcats = obj.subcats

Optional but non-nullable fields in GraphQL

In an update to our GraphQL API only the models _id field is required hence the ! in the below SDL language code. Other fields such as name don't have to be included on an update but also cannot have null value. Currently, excluding the ! from the name field allows the end user to not have to pass a name in an update but it allows them to pass a null value for the name in, which cannot be allowed.
A null value lets us know that a field needs to be removed from the database.
Below is an example of a model where this would cause a problem - the Name custom scalar doesn't allow null values but GraphQL still allows them through:
type language {
_id: ObjectId
iso: Language_ISO
auto_translate: Boolean
name: Name
updated_at: Date_time
created_at: Date_time
}
input language_create {
iso: Language_ISO!
auto_translate: Boolean
name: Name!
}
input language_update {
_id: ObjectId!
iso: Language_ISO!
auto_translate: Boolean
name: Name
}
When a null value is passed in it bypasses our Scalars so we cannot throw a user input validation error if null isn't an allowed value.
I am aware that ! means non-nullable and that the lack of the ! means the field is nullable however it is frustrating that, as far as I can see, we cannot specify the exact values for a field if a field is not required / optional. This issue only occurs on updates.
Are there any ways to work around this issue through custom Scalars without having to start hardcoding logic into each update resolver which seems cumbersome?
EXAMPLE MUTATION THAT SHOULD FAIL
mutation tests_language_create( $input: language_update! ) { language_update( input: $input ) { name }}
Variables
input: {
_id: "1234",
name: null
}
UPDATE 9/11/18: for reference, I can't find a way around this as there are issues with using custom scalars, custom directives and validation rules. I've opened an issue on GitHub here: https://github.com/apollographql/apollo-server/issues/1942
What you're effectively looking for is custom validation logic. You can add any validation rules you want on top of the "default" set that is normally included when you build a schema. Here's a rough example of how to add a rule that checks for null values on specific types or scalars when they are used as arguments:
const { specifiedRules } = require('graphql/validation')
const { GraphQLError } = require('graphql/error')
const typesToValidate = ['Foo', 'Bar']
// This returns a "Visitor" whose properties get called for
// each node in the document that matches the property's name
function CustomInputFieldsNonNull(context) {
return {
Argument(node) {
const argDef = context.getArgument();
const checkType = typesToValidate.includes(argDef.astNode.type.name.value)
if (checkType && node.value.kind === 'NullValue') {
context.reportError(
new GraphQLError(
`Type ${argDef.astNode.type.name.value} cannot be null`,
node,
),
)
}
},
}
}
// We're going to override the validation rules, so we want to grab
// the existing set of rules and just add on to it
const validationRules = specifiedRules.concat(CustomInputFieldsNonNull)
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules,
})
EDIT: The above only works if you're not using variables, which isn't going to be very helpful in most cases. As a workaround, I was able to utilize a FIELD_DEFINITION directive to achieve the desired behavior. There's probably a number of ways you could approach this, but here's a basic example:
class NonNullInputDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
const { args: { paths } } = this
field.resolve = async function (...resolverArgs) {
const fieldArgs = resolverArgs[1]
for (const path of paths) {
if (_.get(fieldArgs, path) === null) {
throw new Error(`${path} cannot be null`)
}
}
return resolve.apply(this, resolverArgs)
}
}
}
Then in your schema:
directive #nonNullInput(paths: [String!]!) on FIELD_DEFINITION
input FooInput {
foo: String
bar: String
}
type Query {
foo (input: FooInput!): String #nonNullInput(paths: ["input.foo"])
}
Assuming that the "non null" input fields are the same each time the input is used in the schema, you could map each input's name to an array of field names that should be validated. So you could do something like this as well:
const nonNullFieldMap = {
FooInput: ['foo'],
}
class NonNullInputDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
const visitedTypeArgs = this.visitedType.args
field.resolve = async function (...resolverArgs) {
const fieldArgs = resolverArgs[1]
visitedTypeArgs.forEach(arg => {
const argType = arg.type.toString().replace("!", "")
const nonNullFields = nonNullFieldMap[argType]
nonNullFields.forEach(nonNullField => {
const path = `${arg.name}.${nonNullField}`
if (_.get(fieldArgs, path) === null) {
throw new Error(`${path} cannot be null`)
}
})
})
return resolve.apply(this, resolverArgs)
}
}
}
And then in your schema:
directive #nonNullInput on FIELD_DEFINITION
type Query {
foo (input: FooInput!): String #nonNullInput
}

array manipulation in node js and lodash

I have two arrays
typeArr = [1010111,23342344]
infoArr={'name':'jon,'age':25}
I am expecting following
[{'name:'jone','age':25,'type':1010111,'default':'ok'},{'name:'jone','age':25,'type':23342344,'default':'nok'}]
Code :
updaterecord(infoArr,type)
{
infoArr.type=type;
response = calculate(age);
if(response)
infoArr.default = 'ok';
else
infoArr.default = 'nok';
return infoArr;
}
createRecord(infoArr,typeArr)
{
var data = _.map(typeArr, type => {
return updaterecord(infoArr,type);
});
return (data);
}
var myData = createRecord(infoArr,typeArr);
I am getting
[{'name:'jone,'age':25.'type':23342344,'default':nok},{'name:'jone,'age':25.'type':23342344,'default':nok}]
with some reason the last record updates the previous one. I have tried generating array using index var but not sure what's wrong it keep overriding the previous item.
how can I resolve this
You are passing the entire infoArr array to your updaterecord() function, but updaterecord() looks like it's expecting a single object. As a result it is adding those properties to the array rather than individual members of the array.
It's not really clear what is supposed to happen because typeArr has two elements and infoArr has one. Do you want to add another to infoArr or should infoArr have the same number of elements as typeArr.
Assuming it should have the same number you would need to use the index the _map gives you to send each item from infoArr:
function createRecord(infoArr,typeArr) {
var data = _.map(typeArr, (type, i) => {
// use infoArr[i] to send one element
return updaterecord(infoArr[i],type);
});
return (data);
}
Edit:
I'm not sure how you are calculating default since it's different in your expected output, but based on one number. To get an array of objects based on infoArray you need to copy the object and add the additional properties the you want. Object.assign() is good for this:
let typeArr = [1010111,23342344]
let infoArr={'name':'jon','age':25}
function updaterecord(infoArr,type){
var obj = Object.assign({}, infoArr)
return Object.assign(obj, {
type: type,
default: infoArr.age > 25 ? 'ok' : 'nok' //or however your figuring this out
})
}
function createRecord(infoArr,typeArr) {
return _.map(typeArr, type => updaterecord(infoArr,type));
}
Result:
[ { name: 'jon', age: 25, type: 1010111, default: 'nok' },
{ name: 'jon', age: 25, type: 23342344, default: 'nok' } ]

Resources