I am trying to write a component that exposes the data from an AJAX call in Polymer. I would like to end with something like.
<get-db data={{data}}></get-db>
<template is="dom-repeat" items="{{data}}">
<div>{{item}}</div>
</template>
However, when I expose the data property from get-db component in another element, the data doesn't bind to the dom-repeat template.
The get-db component parts are as follows
<iron-ajax id="ajax"
url="https://api/endpoint"
method="post"
handle-as="json"
content-type="application/json"
body="[[request]]"
last-response="{{response}}"></iron-ajax>
...
static get properties() {
return {
response: Object,
request: Object,
username: String,
data: Object
}
}
getResponse() {
if (this.username && this.apiKey) {
this.request = {
"body": "bodyText"
};
let request = this.$.ajax.generateRequest();
request.completes.then(req => {
this.setResponse();
})
.catch(rejected => {
console.log(rejected.request);
console.log(rejected.error);
})
}
}
setResponse() {
if(this.response[0]) {
this.data = this.response[0];
}
}
The data property needs to be set to notify.
data: {
type: Object,
notify: true
}
You need to add notify but also you need to use this.set method in order to get observable changes for data properties. Also you are setting only one item to data properties (assuming you have a array at response properties.) I guess you need all array instead only 0. index as you are looping in dom-repeat. That's why this below code may help:
static get properties() {
return {
response:{
type:Object,
observer:'checkResponce'},
request: Object,
username: String,
data: {
type:Array,
notify:true}
}
}
static get observers() { return [ 'checkUserNApi(username,apiKey)']}
checkUserNApi(u,a){
if (u && a) this.$.ajax.generateRequest();
}
checkResponce(d) {
if (d) {
this.set('data',d); //with this method data property at parent will change upon you received responce
}
}
Finally, you may add to iron-ajax a line check error on-error="{{error}}" if you want to see an error report.
DEMO
Related
I have a checkbox list of domain tlds, such as com, net, io, etc. I also have a search text input, where I can drill down the list of 500 or so domains to a smaller amount. For example, if I start to type co in to my search text input, I will get back results that match co, such as co, com, com.au, etc. I am using Laravel and Vue,js 3 to achieve this with a watcher. It works beautifully. How can an achieve the same within a Pinia store?
Here is my code currently:
watch: {
'filters.searchedTlds': function(after, before) {
this.fetchsearchedTlds();
}
},
This is inside my vue component.
Next is the code to fetch searched tlds:
fetchsearchedTlds() {
self = this;
axios.get('/fetch-checked-tlds', { params: { searchedTlds: self.filters.searchedTlds } })
.then(function (response) {
self.filters.tlds = response.data.tlds;
console.log(response.data.tlds);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
},
And finally, the code inside my Laravel controller:
public function fetchCheckedTlds(Request $request)
{
$data['tlds'] = Tld::where('tld', 'LIKE','%'.$request->input('searchedTlds').'%')->pluck('tld');
return response()->json($data);
}
I am converting my code to use a Pinia store and I am stuck on how to convert my vue component watcher to Pinia?
Many thanks in advance.
To watch a pinia status, you may watch a computed attribute based on pinia or use watch getter
Your pinia may look like the one below.
~/store/filters.js
export const useFilters = defineStore('filters', {
state: () => {
return {
_filters: {},
};
},
getters: {
filters: state => state._filters,
},
...
}
In where you want to watch
<script setup>
import { computed, watch } from 'vue';
import { useFilters } from '~/store/filters.js';
const filters = useFilters();
// watch a computed attributes instead
const searchedTlds = computed(() => {
return filters.filters?.searchedTlds || '';
});
watch(
searchedTlds,
(newValue, oldValue) {
fetchsearchedTlds();
}
);
// or use watch getter
watch(
() => {
return filters.filters?.searchedTlds || '';
},
(newValue, oldValue) {
fetchsearchedTlds();
}
);
</script>
The first parameter of watch() can be a single ref or a getter function, or an array of getter functions, for more details, please view the Watch Source Types.
I have a custom hook that looks something like this:
import { useQuery, useQueryClient } from 'react-query'
import { get } from '#/util/api' // Custom API utility
import produce from 'immer' // Using immer for deep object mutation
export function useData() {
const queryClient = useQueryClient()
const { data, isSuccess } = useQuery(
'myData', () => get('data')
)
function addData(moreData) {
const updatedData = produce(data.results, (draft) => {
draft.push(moreData)
})
setData(updatedData)
}
function setData(newData) {
queryClient.setQueryData('myData', newData)
}
return {
data: data && data.results,
setData,
addData,
}
}
My data in data.results is an array of objects. When I call addData it creates a copy of my current data, mutates it, then calls setData where queryClient.setQueryData is called with a new array of objects passed in as my second argument. But my cached data either doesn't update or becomes undefined in the component hooked up to the useData() hook. Does anyone know what I'm doing wrong?
code looks good from react-query perspective, but I'm not sure if that's how immer works. I think with your code, you will get back the same data instance with just a new data.results object on it. I would do:
const updatedData = produce(data, (draft) => {
draft.results.push(moreData)
})
I am digging graphql so I followed a tutorial, And I stucked in this part.
Home.js
function Home() {
const {
loading,
data: { getPosts: posts } // <===## Here ##
} = useQuery(FETCH_POSTS_QUERY);
return (
<div>
{loading ? (
<h1>Loading posts..</h1>
) : (
posts &&
posts.map((post) => (
<p>
{post.content}
</p>
))
)}
</div>
);
}
const FETCH_POSTS_QUERY = gql`
{
getPosts {
id
content
}
}
`;
export default Home;
resolver
Query: {
async getPosts() {
try {
const posts = await Post.find().sort({ createdAt: -1 });
return posts;
} catch (err) {
throw new Error(err);
}
}
},
Whole code: https://github.com/hidjou/classsed-graphql-mern-apollo/tree/react10
In above example is working well, and it use it use data: { getPosts: posts } for deconstruction of returned data. but In my code, I followed it but I got an error
TypeError: Cannot read property 'getPosts' of undefined
Instead, If I code like below,
function Home() {
const {
loading,
data // <===## Here ##
} = useQuery(FETCH_POSTS_QUERY);
if(loading) return <h1>Loading...</h1>
const { getPosts: posts } = data // <===## Here ##
return (
<div>
{loading ? (
<h1>Loading posts..</h1>
) : (
posts &&
posts.map((post) => (
<p>
{post.content}
</p>
))
)}
</div>
);
}
It working well. Seems like my code try to reference data before it loaded. But I don't know why this happen. Code is almost same. Different things are 1. my code is on nextjs, 2. my code is on apollo-server-express. Other things are almost same, my resolver use async/await, and will return posts. Am I miss something?
my resolver is like below.
Query: {
async getPosts(_, { pageNum, searchQuery }) {
try {
const perPage = 5
const posts =
await Post
.find(searchQuery ? { $or: search } : {})
.sort('-_id')
.limit(perPage)
.skip((pageNum - 1) * perPage)
return posts
} catch (err) {
throw new Error(err)
}
},
Your tutorial may be out of date. In older versions of Apollo Client, data was initially set to an empty object. This way, if your code accessed some property on it, it wouldn't blow up. While this was convenient, it also wasn't particularly accurate (there is no data, so why are we providing an object?). Now, data is simply undefined until your operation completes. This is why the latter code is working -- you don't access any properties on data until after loading is false, which means the query is done and data is no longer undefined.
If you want to destructure data when your hook is declared, you can utilize a default value like this:
const {
loading,
data: { getPosts: posts } = {}
} = useQuery(FETCH_POSTS_QUERY)
You could even assign a default value to posts as well if you like.
Just keep in mind two other things: One, data will remain undefined if a network error occurs, even after loading is changed to true, so make sure your code accounts for this scenario. Two, depending on your schema, if there's errors in your response, it's possible for your entire data object to end up null. In this case, you'll still hit an issue with destructuring because default values only work with undefined, not null.
I've been trying to research on how to add another root property of a GraphQL response but found nothing after 1 hour.
Normally, a GraphQL query looks like this:
{
myQuery() {
name
}
}
It responds with:
{
"data": {
"myQuery": []
}
}
I'm curious if I can add another root property in this response say "meta"
{
"data": {
"myQuery": []
},
"meta": {
"page": 1,
"count": 10,
"totalItems": 90
}
}
Is this possible, if not what's the best approach in tackling this with respect to GraphQL?
Thanks!
The apollo-server middleware can be configured with a number of configuration options, including a formatResponse function that allows you to modify the outgoing GraphQL response
const formatResponse = (response) => {
return {
meta
...response
}
}
app.use('/graphql', bodyParser.json(), graphqlExpress({
schema,
formatResponse,
}));
You could pass the req object down to your context, mutate it within your resolver(s) and then use the result inside formatResponse. Something like...
app.use('/graphql', bodyParser.json(), (req, res, next) => graphqlExpress({
schema,
formatResponse: (gqlResponse) => ({
...gqlResponse
meta: req.metadata
}),
})(req, res, next));
Typically, though, you would want to include the metadata as part of your actual schema and have it included with the data. That will also allow you to potentially request multiple queries and get the metadata for all of them.
There's any number of ways to do that, depending on how your data is structured, but here's an example:
type Query {
getFoos: QueryResponse
getBars: QueryResponse
}
type QueryResponse {
results: [Result]
meta: MetaData
}
union Result = Bar | Foo
You can add anything in the response as well... Please follow below code.
app.use('/graphql', bodyParser.json(), graphqlExpress(req => {
return {
schema: tpSchemaNew,
context: {
dbModel
},
formatError: err => {
if (err.originalError && err.originalError.error_message) {
err.message = err.originalError.error_message;
}
return err;
},
formatResponse : res => {
res['meta'] = 'Hey';
return res;
}
}
}))
Apollo Server-specific:
Just adding to the previous answers that formatResponse() has another useful argument, requestContext.
If you are interested in extracting values from that (for example, the context passed to the resolver), you can do the following. BEWARE HOWEVER, the context will likely contain sensitive data that is supposed to be private. You may be leaking authentication data and secrets if not careful.
const server = new ApolloServer({
schema,
formatResponse: (response, requestContext) => {
//return response
const userId = requestContext.context.user.id
response = Object.assign(response, {
extensions: {
meta: {
userId: userId
}
}
}
return response
},
})
The above will return something like this in the gql query response (note the extensions object):
{
data: {
user: {
firstName: 'Hello',
lastName: 'World'
}
},
extensions: { // <= in Typescript, there is no `meta` in GraphQLResponse, but you can use extensions
meta: {
userId: 1234 //<= data from the context
}
}
}
The full list of properties available in requestContext:
at node_modules/apollo-server-types/src/index.ts>GraphQLRequestContext
export interface GraphQLRequestContext<TContext = Record<string, any>> {
readonly request: GraphQLRequest;
readonly response?: GraphQLResponse;
readonly context: TContext;
readonly cache: KeyValueCache;
// This will be replaced with the `operationID`.
readonly queryHash?: string;
readonly document?: DocumentNode;
readonly source?: string;
// `operationName` is set based on the operation AST, so it is defined even if
// no `request.operationName` was passed in. It will be set to `null` for an
// anonymous operation, or if `requestName.operationName` was passed in but
// doesn't resolve to an operation in the document.
readonly operationName?: string | null;
readonly operation?: OperationDefinitionNode;
/**
* Unformatted errors which have occurred during the request. Note that these
* are present earlier in the request pipeline and differ from **formatted**
* errors which are the result of running the user-configurable `formatError`
* transformation function over specific errors.
*/
readonly errors?: ReadonlyArray<GraphQLError>;
readonly metrics?: GraphQLRequestMetrics;
debug?: boolean;
}
in telerik extenstion to pass additional data to ajax request I used
function onDataBinding(e)
{
e.data = {argument : 4};
}
where e was div cointainer with data object inside,
How can I do this using kendo ? I tried the same but for Kendo e arqument is sth totally different.
Finally i got the answer my own and it is :
$('#grid').data('kendoGrid').dataSource.read({name:value})
Sorry for the terrible late at the party, but i've got some special cake that you may find tasty:
function readData()
{
return {
anagId: selectedItem.ID
};
}
$("#grid").kendoGrid({
dataSource: {
type: "ajax",
transport: {
read: {"url":"#Url.Action("RecordRead", "Tools")","data":readData}
}
[ rest of the grid configuration]
I came across this code by inspecting the code generated by Kendo Asp.Net MVC helpers.
I don't know if this is a further implementation that didn't exist at the age of the post, but this way looks really the most flexible compared to the other answers that i saw. HTH
Try this:
Add this to your grid read function or any CRUD operation:
.Read(read => read.Action("ReadCompanyService", "Admin").Data("CompanyServiceFilter"))
Add javascript:
function CompanyServiceFilter()
{
return {
company: $("#ServiceCompany").val()
}
}
In your controller:
public ActionResult ReadCompanyService([DataSourceRequest]DataSourceRequest request, string company)
{
var gridList = repository.GetCompanyServiceRateList(company);
return Json(gridList.ToDataSourceResult(request));
}
Please note, only string type data is allowed to be passed on read, create, update and delete operations.
If you want to pass some param to ajax request, you can use parameterMap configuration on your grid.
This will get passed on to your Ajax request.
parameterMap: function (options, operation) {
if (operation === "read") {
var selectedID = $("#SomeElement").val();
return {ID: selectedID }
}
return kendo.stringify(options.models) ;
}
Try this:
.Read(read => read.Action("Controller", "Action")
.Data(#<text>
function() {
return {
searchModel: DataFunctionName(),
userName: '#=UserName#'
}
}
</text>)
)
JS function
function DataFunctionName() {
var searchModel = {
Active: $("#activityMonitorIsActive").data('kendoDropDownList').value(),
Login: $("#activityMonitorUsers").data('kendoComboBox').value()
};
return searchModel;
}