Our blazing fast Grid component built with pure JavaScript


Post by anuabr »

Hi Team,
We have a store configured with all the required urls' this url has a string which has to be replaced before every request with a global variable we are storing

I was listening to the beforeRequest & beforeLoad events to set replace the readUrl string with the actual selected ID like below:

this.readUrl=this.readUrl.replace('delId',PJTUtil.selectedDeliverableId);

while debugging the source code i can see that the readURL is having the updated after replacing string in above line
But when the request goes to the server i dont see that replace happening its taking the delId string in the url which is causing REST error.

could you pls help me with this

const store= new AjaxStore({
    modelClass: model,
    id: 'store',
    autoLoad:false,
    listeners: {
        afterRequest ( source, params) {
            debugger;
            this.data=source.json.items;
        },
        beforeLoad({source, params}){
            this.readUrl=this.readUrl.replace('delId',selId);
        },
        beforeRequest({ source, params, body, action }){
     
this.readUrl=this.readUrl.replace('delId',selId); }, }, readUrl: sample/delId/child/sample1?totalResults=true&limit=500", useRestfulMethods : true, httpMethods : { create:'POST', update : 'PATCH', delete : 'DELETE' }, createUrl: sample/delId/child/sample1", deleteUrl: sample/delId/child/sample1", updateUrl: sample/delId/child/sample1", headers: { 'Authorization': sample.authorization, 'Content-Type': 'application/json', 'Accept-Language': sample.faLocale, 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS' } });

the above is a representation of our store code

and we are loading the store like this

await store.load()

even after changing the readURL in the beforeLoad & beforeRequest, the request url which actually gets executed is the readURL which is configured to the store

expectation : sample/111111111/child/sample1?totalResults=true&limit=500",
current behaviour : sample/delId/child/sample1?totalResults=true&limit=500", (this is wrong it throws the REST error)


Post by alex.l »

Tried in our online demos with this code:

st = new AjaxStore({
  createUrl  : 'php/create',
  readUrl    : 'php/read',
  updateUrl  : 'php/update',
  deleteUrl  : 'php/delete'
});
st.on('beforerequest', ({ source }) => source.createUrl = '333' );
st.add({ a : 'test' });
st.commit();

All worked as expected. How can we repro the bug you posted ?

All the best,
Alex


Post by anuabr »

Hi Alex,
Im not sure how it can be reproduced but it doesnt seem to work for me and i have provided my exact store configuration

Also if it helps ill tell how this store load in executed

I have a grid
inside which there is a column > when we click on the column it will trigger Grid's cellClick event
which has come like this

 
 async cellClick({ grid, record, column, cellElement, target, event,source }) {
              await store.load()
}

can you check if it works for you incase of readUrl change

one observation
before calling the store.load if we set the read url like this

storeObj.readUrl=storeObj.readUrl.replace('delId',sampleId);
await store.load()

this works its calling the correct rest api but we dont want to set the url from the source widget
we want this to be implemented centrally inside the store event handlers


Post by Animal »

It seems those events are fired after the url for that particular request has been calculated. So it will not take effect for the network operation that is in flight.

I think this is a bug. The url should be calculated after the events.


Post by Animal »

Or, it should pull the url property from the event after triggering the event so that handlers can mutate it.


Post by Animal »

You could add this after you have imported all classes which would allow a beforeRequest handler to change the url property within the event object:

AjaxStore.prototype.sendLoadRequest = function(resolve, reject) {
        let
            me = this,
            [
                url,
                event,
                params,
                eventName,
                successFn
            ]  = me.loadArgs;

        // As soon as it kicks off, new load requests can be made which will result in another load
        me.loadTriggerPromise = null;
        if (url) {
            me._isLoading = true;

            // This may look redundant, but it allows for two levels of event listening.
            // Granular, where the observer observes only the events of interest, and
            // catch-all, where the observer is interested in all requests.
            me.trigger(`load${eventName}Start`, event);
            me.trigger('beforeRequest', event);

            // Allow event handler to mutate the URL at the last moment
            url = event.url;

            // Add filter information to the request parameters
            if (me.filterParamName && me.isFiltered) {
                params[me.filterParamName] = me.encodeFilterParams(me.filters.values);
            }

            // Add sorter information to the request parameters.
            // isSorted includes grouping in its evaluation.
            if (me.remoteSort && me.isSorted) {
                params[me.sortParamName] = me.encodeSorterParams(me.groupers ? me.groupers.concat(me.sorters) : me.sorters);
            }

            // Ensure our next page is passed to the server in the params if not already set.
            // Ensure our page size is always passed.
            if (me.isPaged) {
                if (!((me.pageParamName in params) || (me.pageStartParamName in params))) {
                    const
                        page = Math.min(me.currentPage || 1, me.totalCount ? me.lastPage : Infinity);

                    if (me.pageParamName) {
                        params[me.pageParamName] = page;
                    }
                    else {
                        params[me.pageStartParamName] = (page - 1) * me.pageSize;
                    }
                }
                params[me.pageSizeParamName] = me.pageSize;
            }

            const options = { headers : me.headers, parseJson : true };

            if (me.useRestfulMethods) {
                options.method = me.httpMethods.read;
                // user might define body in case of using custom restful method
                if (event.body) {
                    options.body = JSON.stringify(event.body);
                }
            }

            try {
                const
                    placeParametersInBody = me.paramsInBody && (options.method?.toUpperCase() === 'POST' || options.method?.toUpperCase() === 'PUT') && options.body == null,
                    url                   = event.url + (placeParametersInBody ? '' : me.buildQueryString(event.url, params)),
                    promise               = me.loadingPromise = AjaxHelper.get(
                        url,
                        ObjectHelper.assign(options, me.fetchOptions, placeParametersInBody ? { body : JSON.stringify(params) } : null)),
                    response              = await promise;

                if (me.isDestroyed) {
                    return;
                }

                const
                    data        = response.parsedJson,
                    isArray     = Array.isArray(data),
                    success     = isArray || (data && (ObjectHelper.getPath(data, me.responseSuccessProperty) !== false)),
                    remoteTotal = isArray ? null : ObjectHelper.getPath(data, me.responseTotalProperty);

                me.loadingPromise = null;
                me._isLoading     = false;
                event.response    = response;
                event.json        = data;

                if (success) {
                    if (remoteTotal != null) {
                        me.remoteTotal = parseInt(remoteTotal, 10);
                    }

                    // If we are issuing paged requests, work out what page we are on based
                    // on the requested page and the size of the dataset declared.
                    if (me.isPaged) {
                        if (me.remoteTotal >= 0) {
                            const requestedPage = me.pageParamName ? params[me.pageParamName] : params[me.pageStartParamName] / me.pageSize + 1;
                            me.currentPage      = Math.min(requestedPage, me.lastPage);
                        }
                        else {
                            throw new Error('A paged store must receive its responseTotalProperty in each data packet');
                        }
                    }
                    event.data = isArray ? data : ObjectHelper.getPath(data, me.responseDataProperty);

                    me.suspendModificationsTracking();
                    await successFn?.(event);
                    me.resumeModificationsTracking();

                    if (me.isPartOfProjectRaw) {
                        if (me.ignoreLoadPropagationChanges) {
                            me.suspendModificationsTracking();
                        }
                        await me.getProject()?.commitAsync();
                        if (me.ignoreLoadPropagationChanges) {
                            me.resumeModificationsTracking();
                        }
                    }

                    !me.isDestroyed && me.trigger('load' + eventName, event);
                    resolve(event);
                }
                else {
                    Object.assign(event, {
                        exception     : true,
                        exceptionType : 'server',
                        error         : data?.error
                    });
                    !me.isDestroyed && me.trigger('exception', event);
                    reject(event);
                }

                // finally
                !me.isDestroyed && me.trigger('afterRequest', event);
            }
            catch (responseOrError) {
                me._isLoading = false;

                event.exception = true;

                if (responseOrError instanceof Response) {
                    event.exceptionType = responseOrError.ok ? 'server' : 'network';
                    event.response      = responseOrError;
                    event.error         = responseOrError.error;
                }
                else {
                    event.exceptionType = 'server';
                    event.error         = responseOrError;
                }

                !me.isDestroyed && me.trigger('exception', event);
                reject(event);

                // finally
                !me.isDestroyed && me.trigger('afterRequest', event);
            }
        }
    }

Post by anuabr »

Do we have to implement all the code to change the url before request ?
Can we log a bug which would help us in changing the url out of box without the above overhead ?
just like you mentioned that the url should be calculated only after the events are triggered.


Post by Animal »

All you have to do is paste that in. We will make it so that you can mutate the url property of the event.


Post by Animal »


Post Reply