Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$httpParamSerializerJQLike doesn't encode array indices (as opposed to $.param) #12393

Closed
odedniv opened this issue Jul 21, 2015 · 8 comments
Closed

Comments

@odedniv
Copy link

odedniv commented Jul 21, 2015

Why does pressing Enter while in the title posts the issue?

Anyway, found an important encoding difference between $.param and $httpParamSerializerJQLike.
When encoding arrays, sometimes it's fine to leave the index out like that: a[]=1&a[]=2, which comes out as a = [1, 2] on the other side.

Sometimes this is not the case, mostly when you have array of objects: a[][x]=1&a[][y]=2, which comes out as a = [{ x: 1, y: 2 }], which is sometimes true, but what if you meant a = [{ x: 1 }, { y: 2 }]? In this case you need to encode the array index like so: a[0][x]=1&a[1][y]=2. JQuery does that with $.param, and Rails for example knows how to handle it.

If indices are not given, the server only appends a new object to the array when there are duplicate keys, like so: a[][x]=1&a[][y]=2&a[][x]=3, which should come out as a = [{ x: 1, y: 2 }, { x: 3 }].

@pkozlowski-opensource
Copy link
Member

@odedniv could you please share more details? A reproduce scenario?

@odedniv
Copy link
Author

odedniv commented Jul 21, 2015

Wow @pkozlowski-opensource, you were fast.

EDIT: funny, I see as if I made the edit 5 minutes before you made the comment even though I saw your comment adding up while I was typing.

@odedniv
Copy link
Author

odedniv commented Jul 21, 2015

Suggestion: why not have $httpParamSerializerJQLike just use angular.element.param? Or even copy JQuery's source for that matter.

@Narretz
Copy link
Contributor

Narretz commented Jul 21, 2015

@odedniv There is no angular.element.param afaik. Maybe what you are seeing is that when you have jquery included, $ gets aliased to angular.element, so a param method becomes available.

@odedniv
Copy link
Author

odedniv commented Jul 21, 2015

Right @Narretz, figured that might be the case. In any way, I still think the JQLike implementation should just be, JQ implementation.

lgalfaso added a commit to lgalfaso/angular.js that referenced this issue Jul 21, 2015
…objects

Follow jQuery when serializing arrays that contain objects

Close angular#12393
@lgalfaso
Copy link
Contributor

#12398 should take care of this

@lgalfaso
Copy link
Contributor

landed as 18a2e4f

netman92 pushed a commit to netman92/angular.js that referenced this issue Aug 8, 2015
…objects

Follow jQuery when serializing arrays that contain objects

Close angular#12393
Close angular#12398
ggershoni pushed a commit to ggershoni/angular.js that referenced this issue Sep 29, 2015
…objects

Follow jQuery when serializing arrays that contain objects

Close angular#12393
Close angular#12398
@jackvial
Copy link

I'm using Ionic an was able to update the Angular version so I threw this factory together with the fixed code. It's not pretty but it might be useful to someone.

   yourApp.factory('jQueryLikeSerializeFixed', function() {

        /**
         * This method is intended for encoding *key* or *value* parts of query component. We need a custom
         * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
         * encoded per http://tools.ietf.org/html/rfc3986:
         *    query       = *( pchar / "/" / "?" )
         *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
         *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
         *    pct-encoded   = "%" HEXDIG HEXDIG
         *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
         *                     / "*" / "+" / "," / ";" / "="
         */
        function encodeUriQuery(val, pctEncodeSpaces) {
            return encodeURIComponent(val).
            replace(/%40/gi, '@').
            replace(/%3A/gi, ':').
            replace(/%24/g, '$').
            replace(/%2C/gi, ',').
            replace(/%3B/gi, ';').
            replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
        }

        function serializeValue(v) {
            if (angular.isObject(v)) {
                return angular.isDate(v) ? v.toISOString() : angular.toJson(v);
            }
            return v;
        }

        function forEachSorted(obj, iterator, context) {
            var keys = Object.keys(obj).sort();
            for (var i = 0; i < keys.length; i++) {
                iterator.call(context, obj[keys[i]], keys[i]);
            }
            return keys;
        }

        /**
         * Fixed version of $httpParamSerializerJQLike
         * $httpParamSerializerJQLike with the current version of Angular for Ionic
         * does not serialize array indices correctly. This updated version was pulled from here
         * https://github.com/ggershoni/angular.js/blob/0c98ba4105d50afc1fde3f7a308eb13d234d0e57/src/ng/http.js
         * @param  {[Object]} params
         * @return {[String]} Serialized data
         */
        function jQueryLikeParamSerializer(params) {
            if (!params) return '';
            var parts = [];
            serialize(params, '', true);
            return parts.join('&');

            function serialize(toSerialize, prefix, topLevel) {
                if (toSerialize === null || angular.isUndefined(toSerialize)) return;
                if (angular.isArray(toSerialize)) {
                    angular.forEach(toSerialize, function(value, index) {
                        serialize(value, prefix + '[' + (angular.isObject(value) ? index : '') + ']');
                    });
                } else if (angular.isObject(toSerialize) && !angular.isDate(toSerialize)) {
                    forEachSorted(toSerialize, function(value, key) {
                        serialize(value, prefix +
                            (topLevel ? '' : '[') +
                            key +
                            (topLevel ? '' : ']'));
                    });
                } else {
                    parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
                }
            }
        };
        return jQueryLikeParamSerializer;
    });

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants