Javascript and query strings


  • I survived the hour long Uno hand

    Let's say you have an application and you want to parse query string parameters to do something on a page. Let's say a large portion of your functionality is client-side, but you're doing your best to encapsulate your JavaScript in re-usable objects using an MVC architecture.

    What is the best practice around getting that query-string data to the JavaScript that needs it? I could list off five or six ideas I have, but I'm looking for the one that will be most testable, and the best practice when using object-oriented programming.


  • :belt_onion:

    Why are you putting it in the query string instead of the document in the first place?

    If you're generating GET/POST to the server, then do the reading of the values in javascript from the base rather than building the GET and trying to read that.

    If you need a form values reader for JS, there are some good ones. MS has a good one included in the .NET packages


  • sockdevs

    @Yamikuronue said:

    JavaScript

    okay, i've got some ideas..
    @Yamikuronue said:
    object-oriented programming.

    wait... what?

    I applaud you for your planning and dedication to best practices, but i admit i've never seen a project built in JS that went for OO work well. At least not Classical OO... i've seen a couple using Prototypical OO that worked alright.

    anyway. let me know what you come up with because i've got a project that could use that coming up soon!


  • I survived the hour long Uno hand

    @darkmatter said:

    Why are you putting it in the query string

    Because reasons. I dunno. I'm working on standards and guidelines, so I'm trying to cover some hypothetical situations that might come up again, because I have absolutely no knowledge of how they plan to architect this upcoming application but I know the last one I got involved with used query strings. Probably to generate links to specific subsets of data already sorted and filtered so you can see what they're looking at, or some such.


  • I survived the hour long Uno hand

    @accalia said:

    At least not Classical OO... i've seen a couple using Prototypical OO that worked alright.

    Isn't Prototypical OO mandated by the language choice?

    Look, I'm not doing anything too fancy here, we like Backbone is all. I've been asked to make recommendations on improving testability, I'm not in a position to overhaul our design decisions.


  • :belt_onion:

    Well the guideline/standard in this case should be: DON'T DO IT

    Parsing the query string in JS on a site you have control of yourself is almost certainly not the way to do whatever it is you wanted to do. And depending on where in the pipeline you are parsing those query strings in JS, it's an XSS link hole waiting to happen.


  • I survived the hour long Uno hand

    @darkmatter said:

    Parsing the query string in JS

    Ah! yes. That's the thing. I didn't mean to limit the choices to DOING the parsing in JS, but that's all I can find in google searches. Doing the parsing server-side and passing the information in to the view object seems like a more appropriate use-case, but I'm not sure the best practices around how to do it.


  • :belt_onion:

    Shouldn't be a need to parse query strings on the client at all, ever. For the most part these are the only ways you'd have query strings:

    1. generated server side as links
    2. forms
    3. generated by javascript

    1. have the server send what you need in JS as json, or put the data into the document model to read onload
    2. read the form using any of a number of good JS packages that read form data (almost every js framework has a built in form reader)
    3. you already generated the query string in JS, so you shouldn't need to also parse it

  • Winner of the 2016 Presidential Election

    I use http://benalman.com/projects/jquery-bbq-plugin/ for query manipulation on the client side. It will give you $.param.query() and $.deparam.query() (assuming you're already using jQuery, which I'm slowly starting to dislike).

    My Javascript has been becoming more and more object-oriented over time. I'm writing more Typescript these days, which is Javascript but with types and classes.


  • I survived the hour long Uno hand

    Yes, we are on the same page :smile:

    How do we facilitate 1? Is a JSON dump like (assuming PHP because that's one language I know we won't be using)

     var data = <?=$json_var?>
    

    better or worse than

    var var1 = <?=$var1?>;
    var var2 = <?=$var2?>;
    

    which is better or worse than

    var view = new SpecificView(<?=$view_json?>)
    

    which is better or worse than

    <div id="renderViewHere" data_var1=<?=$var1?> data_var2=<?=$var2?>>
    

    which is better or worse than

    <? echo "<div id='renderViewHere'";
        foreach($vars as $var) {
            echo "data_" . $i . "='" . $var . "'";
            $i++;
        } ?>
    

    all of which I assume is better than @error's suggestion of reading it from the client, but it's JavaScript land where everything is crazy and inverted, so maybe there's better performance or something doing it that way.


  • :belt_onion:

    Merge any URL plus query string or fragment params—in an object, params string or second URL (including the current document location)—into a new URL.

    Update the “URL attribute” (ie. a[href], img[src], form[action], etc) in multiple elements, merging any URL plus query string or fragment params—in an object, params string or second URL (including the current document location)—into a new URL, which is then set into that attribute.

    While those are decent possible reasons for parsing query strings, I would still think it'd be better to have JS Objects already containing the information necessary to create the query string and just have a method like obj.toQueryString() rather than reading the URL off the link to parse, edit, then dump back into the link....

    But that is debatably up to the programmer.



  • At least you definitely want to do proper escaping. Modern browsers have a json decode function - it performs better than eval()ing it as javascript directly, and prevents injection issues.

    In our codebase we typically base64-encode for transfer to get around any escaping nightmares.


  • :belt_onion:

    I have done all of those things you listed, for whatever that's worth.
    Depends on the situation, and how much I expect to be adding on to it later.
    The least friendly one to use is
    [code]<div id="renderViewHere" data_var1=<?=$var1?> data_var2=<?=$var2?>>[/code]
    if you think there is any chance in hell that it will ever have to have more values.

    I generally do either
    [code] var data = <?=$json_var?>[/code] or
    [code]var view = new SpecificView(<?=$view_json?>)[/code]

    There was actually a topic on this about a month ago, the best way to output Javascript/JSON in PHP.


  • :belt_onion:

    In .Net the last major thing I did of this sort basically had an MVC type architecture. The Models were data output in JSON similar to your first example (var data = JSON;) and the Views & Controllers were javascript classes built to deal with the GUI View & updating the Model via GUI Controls. In the page load event I fed the model references to the views & controllers and initialized everything.


  • I survived the hour long Uno hand

    That sounds a lot nicer to test than our current Backbone.js setup. It has no controllers, and our models tend to consist of long complex fetch methods that stuff the retrieved JSON into a variable called "data" inside the model. So we have a model.getData() and model.setData() and model.fetch() and that's about it. I'm not a happy fairy.



  • You do if you are building a single page app that supports push state and you are building the page dynamically just using JS and data from a push service.


  • :belt_onion:

    @lucas said:

    You do if you are building a single page app that supports push state.

    True, sort of. If you're doing a single page app like that, you likely aren't writing query strings so much as storing a state after the #, so it's less about parsing a query string and more about expanding your saved state from after the hash symbol.



  • The only justification to put data in a query string is to make it bookmarkable. Most people favor virtual paths instead of query strings nowadays, and that will be done server side.



  • @Yamikuronue said:

    assuming PHP because that's one language I know we won't be using

    @darkmatter said:
    the best way to output Javascript/JSON in PHP

    In .Net here's how I do JavaScript literals:

            private static System.Text.RegularExpressions.Regex JSDateRX = new System.Text.RegularExpressions.Regex(@"""\\/Date\(-?(\d*)(-\d*)?\)\\/""");
    
            public static string GetJavaScriptLiteral<T>(T x)
            {
                if (x == null)
                {
                    return "null";
                }
                using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                {
                    System.Runtime.Serialization.Json.DataContractJsonSerializer ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
                    ser.WriteObject(ms, x);
                    ms.Position = 0;
                    using (System.IO.StreamReader sr = new System.IO.StreamReader(ms))
                    {
                        return JSDateRX.Replace(sr.ReadToEnd(), "new Date($1)");
                    }
                }
            }
    
    

    I treat JavaScript literals different from JSON because JSON doesn't have real dates.



  • BTW, anyone know why the quotes didn't work?


  • I survived the hour long Uno hand

    .....

    no comment



  • Because Discourse.

    @discoursebot



  • Days Since Last Bug: 0<t3393p22>



  • If you are using jQuery with the data attributes, you just do $("#mydiv").data() and you pretty much have a JavaScript object. This isn't far off from what Polymer is doing.

    You can then write you ascx accordingly to support that and the JS magically knows what to do without any AJAX and you keep the JS and Server side code separate.

    Unless I am massively misunderstanding something?


  • :belt_onion:

    @lucas said:

    Unless I am massively misunderstanding something?

    Just depends on the frameworks and what type of objects you're passing around, I think. If you have a server side data object that can be serialized and you're using analogous code for dealing with the object on both the server & js side, tis way easier to just serialize the object and load in JS one time, without having to transcribe to data attribute(s) in the ASCX/ASPX and then read those attributes back out in JS.

    But that's just my preference due to the frameworks and data I've got to deal with. I don't think that either way is more right or wrong.



  • Thinking about it now, spending most of my dev life being a CMS dev. That is probably the viewpoint that I am coming from, the data attribute approach tends to work better because you tend to have a set of components on the page that are mostly separate from one another.

    EDITED: My grammar sucks today.



  • @Jaime said:

    I treat JavaScript literals different from JSON because JSON doesn't have real dates.

    Yes, but then you set up a situation where anyone sending strings intended as actual strings, but that contain something resembling a date, will get a screwed up representation of said strings.

    In cases like date handling you're better off using typed model definitions on the client-side and restoring types there on a property-by-property basis by running 'type converter' functions for your model properties as needed. Most solidly built frameworks give you the ability to provide 'type converter' functions for your model properties.

    (Btw. Thanks for reminding me how bad the DataContractJsonSerializer API is...)

    @lucas said:

    Thinking about it now, spending most of my dev life being a CMS dev. That is probably the viewpoint that I am coming from, the data attribute approach tends to work better because you tend to have a set of components on the page that are most separate from one another.

    You could just put a data-module="<module-id>" attribute on an element and then write some code that does:

    document
      .querySelectorAll( "[data-module]" );
      .forEach( function( element ) {
        require([ element.getAttribute( "data-module" )], function( Module ) {
          new Module( element );
        })
      });
    

    where you use AMD / RequireJS to structure module definitions and use its ability to register blocks of per-module configuration that can be retrieved from inside the module in question, e.g.

    define([ "module" ], function( module ) {
      var config = module.config();
    
      return function( element ) { /* ... */  }
    });
    

    Very nice way to inject configuration that is meant to be shared across multiple instances of a module. If instead you need per-instance / per-occurence configuration, then you can still just write a single JSON string into a single attribute on an element and grab that to pass along to the constructor:

    document
      .querySelectorAll( "[data-module]" );
      .forEach( function( element ) {
        require([ element.getAttribute( "data-module" )], function( Module ) {
          var options = element.getAttribute( "data-module-config" ),
          options = options ? JSON.parse( options ) : null;
          new Module( element, options );
        })
      });
    

    This is what I'm doing for a few websites where customers have the ability to snap configurable components into available 'slots' on a particular page type template. Very maintainable, nice and DRYd out and still fairly low in performance overhead. Works great.



  • Yes that is more or less what I am already doing but I use the jQuery plugin structure rather the AMD/RequireJS stuff. TBH I don't do CMS dev as of two months ago.



  • @Ragnax said:

    Yes, but then you set up a situation where anyone sending strings intended as actual strings, but that contain something resembling a date, will get a screwed up representation of said strings.

    In cases like date handling you're better off using typed model definitions on the client-side and restoring types there on a property-by-property basis by running 'type converter' functions for your model properties as needed. Most solidly built frameworks give you the ability to provide 'type converter' functions for your model properties.


    Fortunately, they pack dates in a way that's impossible to confuse with real strings; it has an invalid escape sequence in it. No type convertor will ever fix the fact that JSON has absolutely no way to represent a date. I stay away from "typed model definitions on the client-side" because JavaScript doesn't support typed models, so any attempt to do so amounts to pounding a round peg into a square hole. You end up with stuff like spotty browser support for setters and getters and no way to actually enforce the fake type safety that you think you have.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.