SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Guru Majglow's Avatar
    Join Date
    Aug 1999
    Location
    B-Town
    Posts
    645
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Implementing some sort of viewstate

    Viewstate or whatever you would like to call it.

    For example, you have a table of data and the user selected to sort it by date, and then decides to delete a row from it. Is it better to pass the order information through out all the pages or is it better to save it in some viewstate variable?

    If you save it in some viewstate variable, then how do you differenciate between various windows? For example, what if the same user has the same page open in two browser windows and he/she has one window ordered by date and then he/she orders the second window by price. Then the next time he/she does something on the first window, it will be ordered by price.

    It is kind of annoying to keep track of all these "minor" variables throuout pages... if it's a couple variables then it's no big deal, but if there are many, then things start to get ugly.

    asp.NET solved this problem with viewstates, but does anybody have any feedback on this?
    Ohai!

  2. #2
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, it is annoying so I know what your problem is... Take another example for example, you have paged results whereby you want to retain your position within those results, whilst at the same time you would like to group them as you suggest, such as by a status group.

    You can curtail the resultset by a search delimiter whilst you still want to move between the returned results still using the search delimiter, and also be able to redisplay by a group. The cleanest approach that I've found, and easiest to implement, is just to pass those variables via GET & POST.

    There isn't any real point, nor benifit to use SESSION in this case, since the variables change from one request to another in most cases, so no need for persistence - in fact if you use SESSION you are just adding another layer of complexity.

    Here is an example...

    PHP Code:
    // controller
    public function ShowAccountsActionHandler {
    // ...
    public function executeContext $contextIDataspace $dataspace ) {
    $url_parameters = new UrlParameters
                    
    $context -> get'request' ), 
                    array( 
    'page' => '1''searchby' => '''column' => 'id''orderby' => 'asc' ) );
                
                
    $model = new PagerModel$url_parameters );
                
    $model -> setModel( new AccountsModel$context -> get'db' ) ) );
                
    $model -> import$dataspace );
                
    $model -> findAll();
    // ...
    $view -> output$model -> export() );
    }
    // ... 
    The UrlParameters class, what it does is to preserve what possible variables are required for that request, with defaults; If the actual variable it's self exists within GET or POST, then the default is overwritten.

    There may be a situation whereby you don't want to export a specific variable when you create a link, as the variable may already be supplied by the link, so you also provide a means to remove a variable, ie

    You don't want the column variable exported during the generation of this link,

    Code:
    ...
    <a href="users/index.php?column=id&<?php echo( $generated_url ); ?>"><p class="para-padding">#</p></a>
    ...
    Here is the UrlParameters class,

    PHP Code:
    class UrlParameters extends Dataspace {
            public function 
    __constructRequest $request$parameters ) {
                
    $this -> compose$request$parameters );
            }
            
            public function 
    generateUrlWithMask$mask ) {
                
    $parameters $this -> export();
                
    $str '';
                foreach( 
    $parameters as $key => $v ) {
                    if( !
    in_array$key$mask ) ) {
                        
    $str .= (string) $key.'='.$v.'&';
                    }
                }
                return 
    rtrim$str'&' );
            }
            
            protected function 
    compose$request$parameters ) {
                foreach( 
    $parameters as $key => $v ) {
                    if( 
    $request -> has$key ) ) {
                        
    $this -> set$key$request -> get$key ) );
                    } else {
                        
    $this -> set$key$v );
                    }
                }
            }
        } 
    I would be interested in seeing what other members are using too, but I get the feeling that Routes may play a part in this area, but I've not given it too much thought at the moment

  3. #3
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The problem I have with ASP.NET viewstates is that it forces everything to be POST'ed, as the viewstate can be large and look horrid in URLs.

    Setting up defaults for the minor variables may help, using array_merge($defaults, $_GET) to produce a complete set of variables for processing, and when producing a URLs use array_diff_assoc($params, $defaults) and generate the URL from the result, will reduce the URL cruft. (This is part of what Routes does)

    Eg. if set up defaults for page, column & orderby, then it only needs to be in
    $defaults = array('page' => 1, 'column' => 'id', 'orderby' => 'asc' );

    $params['page'] = 1;
    $params['orderby'] = 'asc';
    $params['searchby'] = 'blah';
    $params['column'] = 'id';

    echo http_build_query(array_diff_assoc($params, $defaults));

    will just produce "searchby=blah", and because of the array_merge() the rest will get filled when the page get requested.

    My posting of a Routes implementation is..
    http://www.sitepoint.com/forums/show...0&postcount=27

    It isnt as aggressive as Ruby On Rails in reducing the Url size as yet, as RoR also removes path components that are at their defaults too.
    Last edited by Ren; Feb 26, 2006 at 06:05.

  4. #4
    SitePoint Guru
    Join Date
    May 2005
    Location
    Finland
    Posts
    608
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    PRADO tried to implement viewstate, but they seem to have run into problems since they're migrating out of it as of v3.

    Looking forward to the next version of your routing solution, Ren.

  5. #5
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As an experiment, I converted the PRADO hangman example to use GET based state propagation with WACT controllers instead of viewstate.

    First, you describe your input parameters, associating them with controller nodes:
    PHP Code:
    $game->addParameter(new GetParameter('level''game.level'));
    $game->addParameter(new GetParameter('word''game.word'));
    $game->addParameter(new GetParameter('guesses''game.guesses'));
    $game->addParameter(new GetParameter('misses''game.misses'));

    $guess->addParameter(new GetParameter('letter')); 
    The first parameter to GetParameter is the name. The last line is equivalent to $_GET['letter']. The second parameter is a location in the response model to bind the input parameter to. Because the input parameter is bound to the model, when we generate links back into the application, we can automatically apply those bound parameters without specifying them.

    In the view, you can generate a link with :
    PHP Code:
    $guess =& new LinkTransition(
        $this->dataRoot->data, $this->dataParent->data, 
        $this->root->controller, '/game/play/guess');
    $guess->build();

    <a href="<?= $guess->getHREF() ?>"></a>
    This code generates an url like:

    Code:
    http://localhost/examples/apps/hangman/index.new.php/play/guess?letter=C&misses=0&guesses=&word=ACCOUNT&level=10
    The first parameter to the LinkTransition constructor specifies the response model where bound input parameters can be found. In this case "level", "word", "guesses", and "misses". The second parameter specifies where unbound parameters can be found. In this case, "guess". The third parameter specifies a controller and the fourth parameter specifies a specific command on the controller: "/game/play/guess".

    Note that the controllers are hierarchical. The guess parameter is associated with the guess controller, which is a command. The rest of the parameters are associated with the parent game controller which mostly exists to define the game state. ("All my children use these parameters.")

    You can also think of this url like a function call:

    PHP Code:
    /game/play/guess$level$word$guesses$misses$guess); 
    The LinkTransition is intelligent about default values and can shorten the URL generated when a default can be used. That is why there is no 'game/' prefix on the generated URL.

    Using a template, there is a tag specifically for creating links:
    Code:
    <front:link command="/game/play/guess">{$letter}</front:link>
    This generates the exact same URL as above in the context of the template. Notice the $letter is the same as the letter parameter. These are both in the unbound parameter DataSource, but not otherwise related. The proper values of all five parameters can be deduced from the context where this is used and their controller definitions.

    Additionally, there is some optimization available to the template compiler. The template compiler can resolve many of the lookups at compile time. The code generated by the template compiler looks very much like the familiar:
    PHP Code:
    echo '<a href="index.php?letter='$letter,  '">'$letter'</a>'
    Only a bit more complicated and with all the parameters, not just letter. The key point being that the LinkTransition class is not required inside of a compiled template.

    The same principles can be applied to other types of input. Each type of input is called a channel. A LinkTransition has two available channels: GET and PATHINFO. A FormTransition adds POST. Other channels are possible, like COOKIE.

    An interesting case might be to create a CLI channel for parsing command line parameters. A weird consequence of this is that you could end up with a CLITransition class that could generate new CLI calls back into your application that preserve state. ("hangman -word account -guesses -misses 0 -level 10 guess A")

  6. #6
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Selkirk
    As an experiment, I converted the PRADO hangman example to use GET based state propagation with WACT controllers instead of viewstate.
    ...snip...
    Yes, the template compilation step is interesting, as otherwise when generating Urls you'd need to create controllers, just for their input info.

  7. #7
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Off Topic:

    Another thing to consider is how does Urls for things like AJAX get built.

  8. #8
    SitePoint Zealot
    Join Date
    Mar 2004
    Location
    Australia
    Posts
    101
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Prado still have viewstate in v3, it is optional. Another state, called control-state, was introduced and is required for some components to function (such as holding a data grid's paging, sorting states). The viewstate and/or control state can of course be written to session or some other form of temporary storage (e.g memcache, or a simple sqlite db), as well as writing to a hidden form input in the current page (i.e. the default behaviour in asp.net).

    Viewstate brings a quite a few advantages: tracking which state was changed, keeping form inputs as they were when submitted (great for validation, as you will be presented with the form including that data you just fill and some error stating some validation problems), provide a simple way to persist not-so-important data across multiple post backs.

    Of course, it also bring disavantages, such as, viewstate synchornization when requests are asychronous (e.g. ajax requests), serialization/deserialization overhead (+ encryption/decryption if required), increased page size (overcome by keeping states in session, disk, memory).

    Wei.

  9. #9
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A JSON client side viewstate would be nifty I think.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •