Updating browser history from an ajax response

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Updating browser history from an ajax response

Esteban A. Maringolo
I have a Seaside application that is almost 100% AJAX driven to
replace the visual components (no call/answer involved).

I plan to to use history.pushState() and history.replaceState()

My plan is to update the browser url after replacing some components,
but using the URL building of the existing #updateUrl: (including
_s/_k parameters and whatnot).

Is it possible to obtain the URL of the session presenter within the
ajax response?

Thanks in advance,

Esteban A. Maringolo
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Johan Brichau-2
Hi Esteban,

I’m interested in this too.
We have the same kind of application (I guess… using mostly Ajax updates, I mean) but I have just been postponing a retry to address the inconsistency with ajax, server state and the back button for years….

Within a callback, you can get at the #actionUrl on the renderer.
Is that what you want?

I’m trying to dive into what would be required here… trying to understand what you need:
Ajax requests always update the same continuation state in Seaside (i.e. they do not create a new continuation)… so if you push that url to the history after each ajax update, the back button will essentially request the same but updated continuation from Seaside, meaning you should see the state as it is in on the server, and not how it was before you made the ajax request (which is what happens now).

Does that correspond to what you are trying to fix?

Johan

> On 12 Apr 2019, at 15:17, Esteban Maringolo <[hidden email]> wrote:
>
> I have a Seaside application that is almost 100% AJAX driven to
> replace the visual components (no call/answer involved).
>
> I plan to to use history.pushState() and history.replaceState()
>
> My plan is to update the browser url after replacing some components,
> but using the URL building of the existing #updateUrl: (including
> _s/_k parameters and whatnot).
>
> Is it possible to obtain the URL of the session presenter within the
> ajax response?
>
> Thanks in advance,
>
> Esteban A. Maringolo
> _______________________________________________
> seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Siemen Baader
Hi,

Not really answering your questions, but I think this is what Iliad is for. There you have access to a restful URL on every (xhr) request and every WAComponent is transparently updated via xhr, including when you use call-answer.

Just FYI. :)

Siemen

Sent from my iPhone

> On 12 Apr 2019, at 17.23, Johan Brichau <[hidden email]> wrote:
>
> Hi Esteban,
>
> I’m interested in this too.
> We have the same kind of application (I guess… using mostly Ajax updates, I mean) but I have just been postponing a retry to address the inconsistency with ajax, server state and the back button for years….
>
> Within a callback, you can get at the #actionUrl on the renderer.
> Is that what you want?
>
> I’m trying to dive into what would be required here… trying to understand what you need:
> Ajax requests always update the same continuation state in Seaside (i.e. they do not create a new continuation)… so if you push that url to the history after each ajax update, the back button will essentially request the same but updated continuation from Seaside, meaning you should see the state as it is in on the server, and not how it was before you made the ajax request (which is what happens now).
>
> Does that correspond to what you are trying to fix?
>
> Johan
>
>> On 12 Apr 2019, at 15:17, Esteban Maringolo <[hidden email]> wrote:
>>
>> I have a Seaside application that is almost 100% AJAX driven to
>> replace the visual components (no call/answer involved).
>>
>> I plan to to use history.pushState() and history.replaceState()
>>
>> My plan is to update the browser url after replacing some components,
>> but using the URL building of the existing #updateUrl: (including
>> _s/_k parameters and whatnot).
>>
>> Is it possible to obtain the URL of the session presenter within the
>> ajax response?
>>
>> Thanks in advance,
>>
>> Esteban A. Maringolo
>> _______________________________________________
>> seaside mailing list
>> [hidden email]
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
> _______________________________________________
> seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Paul DeBruicker
Oh man if only our past selves knew we really wanted Iliad apps what a world
this would be....



In my apps I use

     $.fn.updateUrl = function(anId){
       
         if (typeof(window.history.pushState) == 'function') {
             var relativeUrl = $(this).attr('href');
             if(typeof(relativeUrl) =='undefined'){
                 relativeUrl =
document.location.pathname+document.location.search;
             };
             if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
window.history.state){
                 window.history.pushState(relativeUrl, document.title,
relativeUrl);
             }
         }
     };

and call it as part of the onClick: script on anchors and buttons BEFORE
loading content via ajax.  

So the onClick: is


onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn: html)


Also it doesn't need to be a jQuery function but is because it was different
when I initially made it.  



Not sure its what you want to do but it definitely sets the window location
(url) to something that when you hit refresh gives you the same page you see
before you hit refresh (with updated server content, if anything there has
changed in the interim).






Siemen Baader wrote

> Hi,
>
> Not really answering your questions, but I think this is what Iliad is
> for. There you have access to a restful URL on every (xhr) request and
> every WAComponent is transparently updated via xhr, including when you use
> call-answer.
>
> Just FYI. :)
>
> Siemen
>
> Sent from my iPhone
>
>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;

> johan@

> &gt; wrote:
>>
>> Hi Esteban,
>>
>> I’m interested in this too.
>> We have the same kind of application (I guess… using mostly Ajax updates,
>> I mean) but I have just been postponing a retry to address the
>> inconsistency with ajax, server state and the back button for years….
>>
>> Within a callback, you can get at the #actionUrl on the renderer.
>> Is that what you want?
>>
>> I’m trying to dive into what would be required here… trying to understand
>> what you need:
>> Ajax requests always update the same continuation state in Seaside (i.e.
>> they do not create a new continuation)… so if you push that url to the
>> history after each ajax update, the back button will essentially request
>> the same but updated continuation from Seaside, meaning you should see
>> the state as it is in on the server, and not how it was before you made
>> the ajax request (which is what happens now).
>>
>> Does that correspond to what you are trying to fix?
>>
>> Johan
>>
>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;

> emaringolo@

> &gt; wrote:
>>>
>>> I have a Seaside application that is almost 100% AJAX driven to
>>> replace the visual components (no call/answer involved).
>>>
>>> I plan to to use history.pushState() and history.replaceState()
>>>
>>> My plan is to update the browser url after replacing some components,
>>> but using the URL building of the existing #updateUrl: (including
>>> _s/_k parameters and whatnot).
>>>
>>> Is it possible to obtain the URL of the session presenter within the
>>> ajax response?
>>>
>>> Thanks in advance,
>>>
>>> Esteban A. Maringolo
>>> _______________________________________________
>>> seaside mailing list
>>>

> seaside@.squeakfoundation

>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>
>> _______________________________________________
>> seaside mailing list
>>

> seaside@.squeakfoundation

>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
> _______________________________________________
> seaside mailing list

> seaside@.squeakfoundation

> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside





--
Sent from: http://forum.world.st/Seaside-General-f86180.html
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Paul DeBruicker
Oh and I forgot that the onClick: is housed in another function that allows
the ajax pushState anchors to work even if JS is disabled or there is no
session and looks like this:


WAAnchorTag>>#pjaxCallback: aBlockClosure on: html withSuccessScript:
aScript
  self
    onClick:
        (self
            processCallback: aBlockClosure
            onSuccess: self setBodyJs , aScript
            return: false
            thenUpdateBodyOn: html);
    callback: aBlockClosure


&

processCallback: aCallback onSuccess: aJSFunction return: aBoolean
thenUpdateBodyOn: html
  self hasSession
    ifTrue: [
      | scr |
      scr := html jQuery ajax
        html: [ :h |
              aCallback value.
              self renderUpdatedBodyContentFor: self session pjaxBody on: h
];
        onSuccess: aJSFunction.
      aBoolean notNil
        ifTrue: [ scr return: aBoolean ].
      ^ html jQuery this updateUrl
        , ((html jQuery class: 'active') removeClass: 'active') , scr ]
    ifFalse: [ ^ nil ]


So its

html anchor
    pjaxCallback:[ self doSomethingYourClientsLove ] on: html
withSuccessScript: (JSStream on:'alert("I don't believe that worked")');
    with: 'Click me'
 



Paul DeBruicker wrote

> Oh man if only our past selves knew we really wanted Iliad apps what a
> world
> this would be....
>
>
>
> In my apps I use
>
>      $.fn.updateUrl = function(anId){
>        
>          if (typeof(window.history.pushState) == 'function') {
>              var relativeUrl = $(this).attr('href');
>              if(typeof(relativeUrl) =='undefined'){
>                  relativeUrl =
> document.location.pathname+document.location.search;
>              };
>              if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
> window.history.state){
>                  window.history.pushState(relativeUrl, document.title,
> relativeUrl);
>              }
>          }
>      };
>
> and call it as part of the onClick: script on anchors and buttons BEFORE
> loading content via ajax.  
>
> So the onClick: is
>
>
> onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn: html)
>
>
> Also it doesn't need to be a jQuery function but is because it was
> different
> when I initially made it.  
>
>
>
> Not sure its what you want to do but it definitely sets the window
> location
> (url) to something that when you hit refresh gives you the same page you
> see
> before you hit refresh (with updated server content, if anything there has
> changed in the interim).
>
>
>
>
>
>
> Siemen Baader wrote
>> Hi,
>>
>> Not really answering your questions, but I think this is what Iliad is
>> for. There you have access to a restful URL on every (xhr) request and
>> every WAComponent is transparently updated via xhr, including when you
>> use
>> call-answer.
>>
>> Just FYI. :)
>>
>> Siemen
>>
>> Sent from my iPhone
>>
>>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;
>
>> johan@
>
>> &gt; wrote:
>>>
>>> Hi Esteban,
>>>
>>> I’m interested in this too.
>>> We have the same kind of application (I guess… using mostly Ajax
>>> updates,
>>> I mean) but I have just been postponing a retry to address the
>>> inconsistency with ajax, server state and the back button for years….
>>>
>>> Within a callback, you can get at the #actionUrl on the renderer.
>>> Is that what you want?
>>>
>>> I’m trying to dive into what would be required here… trying to
>>> understand
>>> what you need:
>>> Ajax requests always update the same continuation state in Seaside (i.e.
>>> they do not create a new continuation)… so if you push that url to the
>>> history after each ajax update, the back button will essentially request
>>> the same but updated continuation from Seaside, meaning you should see
>>> the state as it is in on the server, and not how it was before you made
>>> the ajax request (which is what happens now).
>>>
>>> Does that correspond to what you are trying to fix?
>>>
>>> Johan
>>>
>>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;
>
>> emaringolo@
>
>> &gt; wrote:
>>>>
>>>> I have a Seaside application that is almost 100% AJAX driven to
>>>> replace the visual components (no call/answer involved).
>>>>
>>>> I plan to to use history.pushState() and history.replaceState()
>>>>
>>>> My plan is to update the browser url after replacing some components,
>>>> but using the URL building of the existing #updateUrl: (including
>>>> _s/_k parameters and whatnot).
>>>>
>>>> Is it possible to obtain the URL of the session presenter within the
>>>> ajax response?
>>>>
>>>> Thanks in advance,
>>>>
>>>> Esteban A. Maringolo
>>>> _______________________________________________
>>>> seaside mailing list
>>>>
>
>> seaside@.squeakfoundation
>
>>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>>
>>> _______________________________________________
>>> seaside mailing list
>>>
>
>> seaside@.squeakfoundation
>
>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> _______________________________________________
>> seaside mailing list
>
>> seaside@.squeakfoundation
>
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
>
>
>
>
> --
> Sent from: http://forum.world.st/Seaside-General-f86180.html
> _______________________________________________
> seaside mailing list

> seaside@.squeakfoundation

> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside





--
Sent from: http://forum.world.st/Seaside-General-f86180.html
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Esteban A. Maringolo
In reply to this post by Johan Brichau-2
Hi Johan,

Hi reply below.

On Fri, Apr 12, 2019 at 12:23 PM Johan Brichau <[hidden email]> wrote:
>
> Hi Esteban,
>
> I’m interested in this too.
> We have the same kind of application (I guess… using mostly Ajax updates, I mean) but I have just been postponing a retry to address the inconsistency with ajax, server state and the back button for years….

I've been postponing this for a while but will need it sooner than later :)

> Within a callback, you can get at the #actionUrl on the renderer.
> Is that what you want?

I can't tell really, the actionUrl now, only has something like this
`/?_s=...&_k=...`, so certainly it doesnt.

> I’m trying to dive into what would be required here… trying to understand what you need:
> Ajax requests always update the same continuation state in Seaside (i.e. they do not create a new continuation)… so if you push that url to the history after each ajax update, the back button will essentially request the same but updated continuation from Seaside, meaning you should see the state as it is in on the server, and not how it was before you made the ajax request (which is what happens now).

I have a tree of nested component, which where working without using
AJAX, and had all the "#updateUrl:" methods in place, so if for
instance, I had a root component and opened a project ABCD, I added
the `/project/ABCD' to the URL. So when the user came back later for
that URL, I could lookup for that project and initialize the proper
"tree" of components to show it (or show an error if not found, etc.).

I then moved to everything as AJAX to make things faster and simplify
the state management... and lost this capability.

I drive most AJAX interactions by means of "script" AJAX responses, so
my idea is to change the component tree structure at the server side
(as I'm doing now), and at the end of some "top level" loading/reload
push the URL that would have been generated by means of a regular
visitor transversal to browser history stack.

Let's suppose we have a common CRUD like UI very much like the ones
used by REST APIs, so we have the following components

UsersComponent -> 'users'
UserComponent -> 'admin'
UserSectionComponent -> 'settings'

So if the app is showing aUsersComponent as its main component (I call
it `bodyComponent`) then the url would be `/users`, when some user is
selected, the bodyComponent (aUsersComponent) "shows" (#show:)
aUserComponent, this adds the userId component to the URL, and if
within that component I access a particular section of the user (e.g.
'settings' or 'preferences'), then that gets added to the URL as well.

I want that when I call `scriptReplaceOn: aScript` of a particular one
of these components they do something like this:

`UserComponent>>scriptReplaceOn: aScript

  aScript << ((aScript jQuery id: self ajaxId) replaceWith: [:h | self
renderContentOn: h]).
  aScript << ((JSStream on: 'window.history') call: 'pushState' with:
self historyState).

And  `historyState` would have this "resolved" WAUrl instance, that I
guess would be initialized using a WAUpdateUrlVisitor from current
component (e.g. UserSectionComponent) to the root (although in the
inverse order).

> Does that correspond to what you are trying to fix?

I guess it does. But I'm not sure whether I'm using the states and
continuations the canonical way.
Things work, causing a full page reload shows the exact same content
as it was before, but backbutton does nothing (it behaves as a full
page reload).

I feel like I have not answered your question, but since there is no
"recommended" way to work with components with AJAX (e.g. replacing
the instVar, using show/call/answer, etc.), I don't know whether a
"suitable" solution might be found.


Esteban A. Maringolo
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Esteban A. Maringolo
In reply to this post by Paul DeBruicker
Hi Paul,

I've seen that code before ;-) and maybe setting the URL **before** is
the way to go.

In your case you always replace the "body" component of your page, I
might need to adapt it in a way that could replace any DOM element.

Regarding this:
"Not sure its what you want to do but it definitely sets the window location
(url) to something that when you hit refresh gives you the same page you see
before you hit refresh (with updated server content, if anything there has
changed in the interim)."

I don't remember how you consumed that in #initialRequest and whether
you specified #updateUrl: (and kept it in sync on each
"pjaxCallback:..." call).

Regards,

Esteban A. Maringolo

On Fri, Apr 12, 2019 at 1:47 PM Paul DeBruicker <[hidden email]> wrote:

>
> Oh and I forgot that the onClick: is housed in another function that allows
> the ajax pushState anchors to work even if JS is disabled or there is no
> session and looks like this:
>
>
> WAAnchorTag>>#pjaxCallback: aBlockClosure on: html withSuccessScript:
> aScript
>   self
>     onClick:
>         (self
>             processCallback: aBlockClosure
>             onSuccess: self setBodyJs , aScript
>             return: false
>             thenUpdateBodyOn: html);
>     callback: aBlockClosure
>
>
> &
>
> processCallback: aCallback onSuccess: aJSFunction return: aBoolean
> thenUpdateBodyOn: html
>   self hasSession
>     ifTrue: [
>       | scr |
>       scr := html jQuery ajax
>         html: [ :h |
>               aCallback value.
>               self renderUpdatedBodyContentFor: self session pjaxBody on: h
> ];
>         onSuccess: aJSFunction.
>       aBoolean notNil
>         ifTrue: [ scr return: aBoolean ].
>       ^ html jQuery this updateUrl
>         , ((html jQuery class: 'active') removeClass: 'active') , scr ]
>     ifFalse: [ ^ nil ]
>
>
> So its
>
> html anchor
>     pjaxCallback:[ self doSomethingYourClientsLove ] on: html
> withSuccessScript: (JSStream on:'alert("I don't believe that worked")');
>     with: 'Click me'
>
>
>
>
> Paul DeBruicker wrote
> > Oh man if only our past selves knew we really wanted Iliad apps what a
> > world
> > this would be....
> >
> >
> >
> > In my apps I use
> >
> >      $.fn.updateUrl = function(anId){
> >
> >          if (typeof(window.history.pushState) == 'function') {
> >              var relativeUrl = $(this).attr('href');
> >              if(typeof(relativeUrl) =='undefined'){
> >                  relativeUrl =
> > document.location.pathname+document.location.search;
> >              };
> >              if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
> > window.history.state){
> >                  window.history.pushState(relativeUrl, document.title,
> > relativeUrl);
> >              }
> >          }
> >      };
> >
> > and call it as part of the onClick: script on anchors and buttons BEFORE
> > loading content via ajax.
> >
> > So the onClick: is
> >
> >
> > onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn: html)
> >
> >
> > Also it doesn't need to be a jQuery function but is because it was
> > different
> > when I initially made it.
> >
> >
> >
> > Not sure its what you want to do but it definitely sets the window
> > location
> > (url) to something that when you hit refresh gives you the same page you
> > see
> > before you hit refresh (with updated server content, if anything there has
> > changed in the interim).
> >
> >
> >
> >
> >
> >
> > Siemen Baader wrote
> >> Hi,
> >>
> >> Not really answering your questions, but I think this is what Iliad is
> >> for. There you have access to a restful URL on every (xhr) request and
> >> every WAComponent is transparently updated via xhr, including when you
> >> use
> >> call-answer.
> >>
> >> Just FYI. :)
> >>
> >> Siemen
> >>
> >> Sent from my iPhone
> >>
> >>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;
> >
> >> johan@
> >
> >> &gt; wrote:
> >>>
> >>> Hi Esteban,
> >>>
> >>> I’m interested in this too.
> >>> We have the same kind of application (I guess… using mostly Ajax
> >>> updates,
> >>> I mean) but I have just been postponing a retry to address the
> >>> inconsistency with ajax, server state and the back button for years….
> >>>
> >>> Within a callback, you can get at the #actionUrl on the renderer.
> >>> Is that what you want?
> >>>
> >>> I’m trying to dive into what would be required here… trying to
> >>> understand
> >>> what you need:
> >>> Ajax requests always update the same continuation state in Seaside (i.e.
> >>> they do not create a new continuation)… so if you push that url to the
> >>> history after each ajax update, the back button will essentially request
> >>> the same but updated continuation from Seaside, meaning you should see
> >>> the state as it is in on the server, and not how it was before you made
> >>> the ajax request (which is what happens now).
> >>>
> >>> Does that correspond to what you are trying to fix?
> >>>
> >>> Johan
> >>>
> >>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;
> >
> >> emaringolo@
> >
> >> &gt; wrote:
> >>>>
> >>>> I have a Seaside application that is almost 100% AJAX driven to
> >>>> replace the visual components (no call/answer involved).
> >>>>
> >>>> I plan to to use history.pushState() and history.replaceState()
> >>>>
> >>>> My plan is to update the browser url after replacing some components,
> >>>> but using the URL building of the existing #updateUrl: (including
> >>>> _s/_k parameters and whatnot).
> >>>>
> >>>> Is it possible to obtain the URL of the session presenter within the
> >>>> ajax response?
> >>>>
> >>>> Thanks in advance,
> >>>>
> >>>> Esteban A. Maringolo
> >>>> _______________________________________________
> >>>> seaside mailing list
> >>>>
> >
> >> seaside@.squeakfoundation
> >
> >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
> >>>
> >>> _______________________________________________
> >>> seaside mailing list
> >>>
> >
> >> seaside@.squeakfoundation
> >
> >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
> >> _______________________________________________
> >> seaside mailing list
> >
> >> seaside@.squeakfoundation
> >
> >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
> >
> >
> >
> >
> >
> > --
> > Sent from: http://forum.world.st/Seaside-General-f86180.html
> > _______________________________________________
> > seaside mailing list
>
> > seaside@.squeakfoundation
>
> > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
>
>
>
>
> --
> Sent from: http://forum.world.st/Seaside-General-f86180.html
> _______________________________________________
> seaside mailing list
> [hidden email]
> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Paul DeBruicker



I fully believe there is a better/less hackish way to do this if its going
to be a seaside feature.  But what I have does to the partial page
update/back button/shareable links stuff well enough for me for now.

You're right I do generally use it for navigation toreplace the content of a
div with the id 'body' but  I also use it to

    - update the page title
    - load on page help for the current view
    - move the person along in the guided tour if they're taking one
    - update other indicators/notifications if they've changed.   e.g. "You
have a new message from ..."

I think that the DOM editing code could easily be adapted to also replace an
arbitrary collection of specific DOM components like you'd prefer without
too much trouble.  If you had an id for every DOM element you wanted to
replace in the real DOM and a matching div with the content in the Seaside
ajax response then you could itereate of the received divs putting their
contents into the target ids in the real DOM.  The duplicated ids would only
exist in the response handling code.  And you could update multiple spots on
the page at once for a given round trip to the server.


The setBodyJs code just searches the received div's putting them in the
correct spots and then runs the scripts that were included in the response.  
Anyway that code is

MyComponent>>#setBodyJs
        ^ (JSStream on: '$("#body").setBody(resp, status, xhr);') asFunction:
#('resp' 'status' 'xhr')


 $.fn.setBody = function (response, txtStatus, jqXhr) {
         var content, body = $('#body'), newElement, target;
         newElement=document.createElement('div');
         newElement.innerHTML = response;
         
         content = newElement.querySelector('#b');
         if(content){
                 body.empty().append(content.innerHTML);
         } else {
             window.location.href = window.location.href.split("?")[0];
         };

         content = newElement.querySelector('#h');
         if(content){
             $('#helpBarContent').empty().append(content.innerHTML);
         };

         content = newElement.querySelector('#t');
         if(content){
             $('#tour').empty().append(content.innerHTML);
         };

         content = newElement.querySelector('#tp');
         if(content){
             $('#tourProgress').empty().append(content.innerHTML);
         };

         $(response).filter('script').each(function () {
             $.globalEval(this.text || this.textContent || this.innerHTML ||
'');
         });
         document.title = newElement.querySelector('#ti').innerHTML;
         $viewport.scrollToTop();
 };

So you can see that its just finding elements by id ('#b' '#h' '#t' '#tp'
'#ti') in the Seaside response and replacing others in the DOM and could be
generalized into a loop if you wanted to spend the time to do it (if you
were making it part of an open source package others could use for example
;) ) .  


For keeping the URL in sync I do not use updateUrl: because it only (I
think?) edits the response URL & instead I just edit the url for the anchor
before the pjaxCallback: e.g.

html anchor
     backEndUrl:'mypage';
     pjaxCallback:[self showMyPage] on: html;
     with: 'click me to see my page'.


and WAAnchorTag>>#backEndUrl: is

backEndUrl: anExtraPath
        self
                useBaseUrl;
                extraPath: MyApp appName;
                extraPath: anExtraPath

By editing the url for the anchor it can be copy and pasted among users both
from the anchor on the page and the url bar in the browser once they've
arrived on the page of interest.  It is only dissected and used in
#initialRequest: when people share links  and when the back button is
clicked as described below. I use cookies for sessions so there isn't a  _s
parameter in the urls that are created.  


To handle the back button I'm overriding the browser's native back button
handling.  There is some js where I add a header when the back button is
clicked, and then look for that in my session class and handle it.

The JS that adds the header is is

window.onpopstate = function (event) {
   // console.log(event.state);
    if ($('body.back').length && event.state) {
      $.ajax({url: event.state,
                success: function(data, textStatus, jqXhr)
{$('body').setBodyAfterBackButton(data, textStatus, jqXhr);},
                beforeSend:
function(xhr){xhr.setRequestHeader("X-Requested-With", "pjaxBB");}});    
    }
};

and the setBodyAfterBackButton replaces the existing page with the Seaside
ajax response.

$.fn.setBodyAfterBackButton = function (data, status, jqXhr) {
         var newDoc = document.open("text/html", "replace");
         newDoc.write(data);
         newDoc.close();
};


Then in the session I override #handleFiltered: like this


MySession>>#handleFiltered: aRequestContext
        (self isBackButtonPjaxRequest: aRequestContext)
                ifTrue: [ self handleBackButtonRequest: aRequestContext ]
                ifFalse: [ super handleFiltered: aRequestContext ]


MySession>>#isBackButtonPjaxRequest: aRequestContext
        ^ (aRequestContext request headers at: 'x-requested-with' ifAbsent: [ nil
]) = 'pjaxBB'


MySession>>#handleBackButtonRequest: aRequestContext
                | bdy path |
                path := aRequestContext request uri path allButFirst.
                bdy := self bodyFromBackButtonPath: path.
                self  body: bdy.
                aRequestContext request uri addField: '_n'.
                super handleFiltered: aRequestContext


MySession>>#bodyFromBackButtonPath: path
   "App specific code to find which page of the app should be shown and what
content should be in it just like in #initialRequest: "




Thats it I think.  It works. Could be clearer/cleaner/more general.   I'd
prefer it to be more automatic (evidenlty like Iliad I guess) but I'm not
creating so many new things that setting the urls & adding the lookup code
for the #bodyFromBackButtonPath:/#initialRequest: is a burden.  


And like you remember I really only use this for navigation among areas of
the app.  It doesn't remember if you had a modal open or a few accordions
expanded and others collapsed or were editing a thing on one page and
another thing on another page.  And I don't know how to go back to prior
server states with this. It will take you back ten screens in order but that
screen will have the most recently edited data.  Not be in the same state it
was when you first looked at it.   Unlike the counter demo I'm not sure it
should in a multi user app where others might've seen it or been affected by
the changes.

Hope this helps

Paul





Esteban A. Maringolo wrote

> Hi Paul,
>
> I've seen that code before ;-) and maybe setting the URL **before** is
> the way to go.
>
> In your case you always replace the "body" component of your page, I
> might need to adapt it in a way that could replace any DOM element.
>
> Regarding this:
> "Not sure its what you want to do but it definitely sets the window
> location
> (url) to something that when you hit refresh gives you the same page you
> see
> before you hit refresh (with updated server content, if anything there has
> changed in the interim)."
>
> I don't remember how you consumed that in #initialRequest and whether
> you specified #updateUrl: (and kept it in sync on each
> "pjaxCallback:..." call).
>
> Regards,
>
> Esteban A. Maringolo
>
> On Fri, Apr 12, 2019 at 1:47 PM Paul DeBruicker &lt;

> pdebruic@

> &gt; wrote:
>>
>> Oh and I forgot that the onClick: is housed in another function that
>> allows
>> the ajax pushState anchors to work even if JS is disabled or there is no
>> session and looks like this:
>>
>>
>> WAAnchorTag>>#pjaxCallback: aBlockClosure on: html withSuccessScript:
>> aScript
>>   self
>>     onClick:
>>         (self
>>             processCallback: aBlockClosure
>>             onSuccess: self setBodyJs , aScript
>>             return: false
>>             thenUpdateBodyOn: html);
>>     callback: aBlockClosure
>>
>>
>> &
>>
>> processCallback: aCallback onSuccess: aJSFunction return: aBoolean
>> thenUpdateBodyOn: html
>>   self hasSession
>>     ifTrue: [
>>       | scr |
>>       scr := html jQuery ajax
>>         html: [ :h |
>>               aCallback value.
>>               self renderUpdatedBodyContentFor: self session pjaxBody on:
>> h
>> ];
>>         onSuccess: aJSFunction.
>>       aBoolean notNil
>>         ifTrue: [ scr return: aBoolean ].
>>       ^ html jQuery this updateUrl
>>         , ((html jQuery class: 'active') removeClass: 'active') , scr ]
>>     ifFalse: [ ^ nil ]
>>
>>
>> So its
>>
>> html anchor
>>     pjaxCallback:[ self doSomethingYourClientsLove ] on: html
>> withSuccessScript: (JSStream on:'alert("I don't believe that worked")');
>>     with: 'Click me'
>>
>>
>>
>>
>> Paul DeBruicker wrote
>> > Oh man if only our past selves knew we really wanted Iliad apps what a
>> > world
>> > this would be....
>> >
>> >
>> >
>> > In my apps I use
>> >
>> >      $.fn.updateUrl = function(anId){
>> >
>> >          if (typeof(window.history.pushState) == 'function') {
>> >              var relativeUrl = $(this).attr('href');
>> >              if(typeof(relativeUrl) =='undefined'){
>> >                  relativeUrl =
>> > document.location.pathname+document.location.search;
>> >              };
>> >              if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
>> > window.history.state){
>> >                  window.history.pushState(relativeUrl, document.title,
>> > relativeUrl);
>> >              }
>> >          }
>> >      };
>> >
>> > and call it as part of the onClick: script on anchors and buttons
>> BEFORE
>> > loading content via ajax.
>> >
>> > So the onClick: is
>> >
>> >
>> > onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn:
>> html)
>> >
>> >
>> > Also it doesn't need to be a jQuery function but is because it was
>> > different
>> > when I initially made it.
>> >
>> >
>> >
>> > Not sure its what you want to do but it definitely sets the window
>> > location
>> > (url) to something that when you hit refresh gives you the same page
>> you
>> > see
>> > before you hit refresh (with updated server content, if anything there
>> has
>> > changed in the interim).
>> >
>> >
>> >
>> >
>> >
>> >
>> > Siemen Baader wrote
>> >> Hi,
>> >>
>> >> Not really answering your questions, but I think this is what Iliad is
>> >> for. There you have access to a restful URL on every (xhr) request and
>> >> every WAComponent is transparently updated via xhr, including when you
>> >> use
>> >> call-answer.
>> >>
>> >> Just FYI. :)
>> >>
>> >> Siemen
>> >>
>> >> Sent from my iPhone
>> >>
>> >>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;
>> >
>> >> johan@
>> >
>> >> &gt; wrote:
>> >>>
>> >>> Hi Esteban,
>> >>>
>> >>> I’m interested in this too.
>> >>> We have the same kind of application (I guess… using mostly Ajax
>> >>> updates,
>> >>> I mean) but I have just been postponing a retry to address the
>> >>> inconsistency with ajax, server state and the back button for years….
>> >>>
>> >>> Within a callback, you can get at the #actionUrl on the renderer.
>> >>> Is that what you want?
>> >>>
>> >>> I’m trying to dive into what would be required here… trying to
>> >>> understand
>> >>> what you need:
>> >>> Ajax requests always update the same continuation state in Seaside
>> (i.e.
>> >>> they do not create a new continuation)… so if you push that url to
>> the
>> >>> history after each ajax update, the back button will essentially
>> request
>> >>> the same but updated continuation from Seaside, meaning you should
>> see
>> >>> the state as it is in on the server, and not how it was before you
>> made
>> >>> the ajax request (which is what happens now).
>> >>>
>> >>> Does that correspond to what you are trying to fix?
>> >>>
>> >>> Johan
>> >>>
>> >>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;
>> >
>> >> emaringolo@
>> >
>> >> &gt; wrote:
>> >>>>
>> >>>> I have a Seaside application that is almost 100% AJAX driven to
>> >>>> replace the visual components (no call/answer involved).
>> >>>>
>> >>>> I plan to to use history.pushState() and history.replaceState()
>> >>>>
>> >>>> My plan is to update the browser url after replacing some
>> components,
>> >>>> but using the URL building of the existing #updateUrl: (including
>> >>>> _s/_k parameters and whatnot).
>> >>>>
>> >>>> Is it possible to obtain the URL of the session presenter within the
>> >>>> ajax response?
>> >>>>
>> >>>> Thanks in advance,
>> >>>>
>> >>>> Esteban A. Maringolo
>> >>>> _______________________________________________
>> >>>> seaside mailing list
>> >>>>
>> >
>> >> seaside@.squeakfoundation
>> >
>> >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> >>>
>> >>> _______________________________________________
>> >>> seaside mailing list
>> >>>
>> >
>> >> seaside@.squeakfoundation
>> >
>> >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> >> _______________________________________________
>> >> seaside mailing list
>> >
>> >> seaside@.squeakfoundation
>> >
>> >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> >
>> >
>> >
>> >
>> >
>> > --
>> > Sent from: http://forum.world.st/Seaside-General-f86180.html
>> > _______________________________________________
>> > seaside mailing list
>>
>> > seaside@.squeakfoundation
>>
>> > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>
>>
>>
>>
>>
>> --
>> Sent from: http://forum.world.st/Seaside-General-f86180.html
>> _______________________________________________
>> seaside mailing list
>>

> seaside@.squeakfoundation

>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
> _______________________________________________
> seaside mailing list

> seaside@.squeakfoundation

> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside





--
Sent from: http://forum.world.st/Seaside-General-f86180.html
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Paul DeBruicker
This could be a better way for instance:

https://dockyard.com/blog/2018/12/12/phoenix-liveview-interactive-real-time-apps-no-need-to-write-javascript





Paul DeBruicker wrote

> I fully believe there is a better/less hackish way to do this if its going
> to be a seaside feature.  But what I have does to the partial page
> update/back button/shareable links stuff well enough for me for now.
>
> You're right I do generally use it for navigation toreplace the content of
> a
> div with the id 'body' but  I also use it to
>
>     - update the page title
>     - load on page help for the current view
>     - move the person along in the guided tour if they're taking one
>     - update other indicators/notifications if they've changed.   e.g.
> "You
> have a new message from ..."
>
> I think that the DOM editing code could easily be adapted to also replace
> an
> arbitrary collection of specific DOM components like you'd prefer without
> too much trouble.  If you had an id for every DOM element you wanted to
> replace in the real DOM and a matching div with the content in the Seaside
> ajax response then you could itereate of the received divs putting their
> contents into the target ids in the real DOM.  The duplicated ids would
> only
> exist in the response handling code.  And you could update multiple spots
> on
> the page at once for a given round trip to the server.
>
>
> The setBodyJs code just searches the received div's putting them in the
> correct spots and then runs the scripts that were included in the
> response.  
> Anyway that code is
>
> MyComponent>>#setBodyJs
> ^ (JSStream on: '$("#body").setBody(resp, status, xhr);') asFunction:
> #('resp' 'status' 'xhr')
>
>
>  $.fn.setBody = function (response, txtStatus, jqXhr) {
>          var content, body = $('#body'), newElement, target;
>          newElement=document.createElement('div');
>          newElement.innerHTML = response;
>          
>          content = newElement.querySelector('#b');
>          if(content){
>                  body.empty().append(content.innerHTML);
>          } else {
>              window.location.href = window.location.href.split("?")[0];
>          };
>
>          content = newElement.querySelector('#h');
>          if(content){
>              $('#helpBarContent').empty().append(content.innerHTML);
>          };
>
>          content = newElement.querySelector('#t');
>          if(content){
>              $('#tour').empty().append(content.innerHTML);
>          };
>
>          content = newElement.querySelector('#tp');
>          if(content){
>              $('#tourProgress').empty().append(content.innerHTML);
>          };
>
>          $(response).filter('script').each(function () {
>              $.globalEval(this.text || this.textContent || this.innerHTML
> ||
> '');
>          });
>          document.title = newElement.querySelector('#ti').innerHTML;
>          $viewport.scrollToTop();
>  };
>
> So you can see that its just finding elements by id ('#b' '#h' '#t' '#tp'
> '#ti') in the Seaside response and replacing others in the DOM and could
> be
> generalized into a loop if you wanted to spend the time to do it (if you
> were making it part of an open source package others could use for example
> ;) ) .  
>
>
> For keeping the URL in sync I do not use updateUrl: because it only (I
> think?) edits the response URL & instead I just edit the url for the
> anchor
> before the pjaxCallback: e.g.
>
> html anchor
>      backEndUrl:'mypage';
>      pjaxCallback:[self showMyPage] on: html;
>      with: 'click me to see my page'.
>
>
> and WAAnchorTag>>#backEndUrl: is
>
> backEndUrl: anExtraPath
> self
> useBaseUrl;
> extraPath: MyApp appName;
> extraPath: anExtraPath
>
> By editing the url for the anchor it can be copy and pasted among users
> both
> from the anchor on the page and the url bar in the browser once they've
> arrived on the page of interest.  It is only dissected and used in
> #initialRequest: when people share links  and when the back button is
> clicked as described below. I use cookies for sessions so there isn't a
> _s
> parameter in the urls that are created.  
>
>
> To handle the back button I'm overriding the browser's native back button
> handling.  There is some js where I add a header when the back button is
> clicked, and then look for that in my session class and handle it.
>
> The JS that adds the header is is
>
> window.onpopstate = function (event) {
>    // console.log(event.state);
>     if ($('body.back').length && event.state) {
>       $.ajax({url: event.state,
>                 success: function(data, textStatus, jqXhr)
> {$('body').setBodyAfterBackButton(data, textStatus, jqXhr);},
>                 beforeSend:
> function(xhr){xhr.setRequestHeader("X-Requested-With", "pjaxBB");}});    
>     }
> };
>
> and the setBodyAfterBackButton replaces the existing page with the Seaside
> ajax response.
>
> $.fn.setBodyAfterBackButton = function (data, status, jqXhr) {
>          var newDoc = document.open("text/html", "replace");
>          newDoc.write(data);
>          newDoc.close();
> };
>
>
> Then in the session I override #handleFiltered: like this
>
>
> MySession>>#handleFiltered: aRequestContext
> (self isBackButtonPjaxRequest: aRequestContext)
> ifTrue: [ self handleBackButtonRequest: aRequestContext ]
> ifFalse: [ super handleFiltered: aRequestContext ]
>
>
> MySession>>#isBackButtonPjaxRequest: aRequestContext
> ^ (aRequestContext request headers at: 'x-requested-with' ifAbsent: [ nil
> ]) = 'pjaxBB'
>
>
> MySession>>#handleBackButtonRequest: aRequestContext
> | bdy path |
> path := aRequestContext request uri path allButFirst.
> bdy := self bodyFromBackButtonPath: path.
> self  body: bdy.
> aRequestContext request uri addField: '_n'.
> super handleFiltered: aRequestContext
>
>
> MySession>>#bodyFromBackButtonPath: path
>    "App specific code to find which page of the app should be shown and
> what
> content should be in it just like in #initialRequest: "
>
>
>
>
> Thats it I think.  It works. Could be clearer/cleaner/more general.   I'd
> prefer it to be more automatic (evidenlty like Iliad I guess) but I'm not
> creating so many new things that setting the urls & adding the lookup code
> for the #bodyFromBackButtonPath:/#initialRequest: is a burden.  
>
>
> And like you remember I really only use this for navigation among areas of
> the app.  It doesn't remember if you had a modal open or a few accordions
> expanded and others collapsed or were editing a thing on one page and
> another thing on another page.  And I don't know how to go back to prior
> server states with this. It will take you back ten screens in order but
> that
> screen will have the most recently edited data.  Not be in the same state
> it
> was when you first looked at it.   Unlike the counter demo I'm not sure it
> should in a multi user app where others might've seen it or been affected
> by
> the changes.
>
> Hope this helps
>
> Paul
>
>
>
>
>
> Esteban A. Maringolo wrote
>> Hi Paul,
>>
>> I've seen that code before ;-) and maybe setting the URL **before** is
>> the way to go.
>>
>> In your case you always replace the "body" component of your page, I
>> might need to adapt it in a way that could replace any DOM element.
>>
>> Regarding this:
>> "Not sure its what you want to do but it definitely sets the window
>> location
>> (url) to something that when you hit refresh gives you the same page you
>> see
>> before you hit refresh (with updated server content, if anything there
>> has
>> changed in the interim)."
>>
>> I don't remember how you consumed that in #initialRequest and whether
>> you specified #updateUrl: (and kept it in sync on each
>> "pjaxCallback:..." call).
>>
>> Regards,
>>
>> Esteban A. Maringolo
>>
>> On Fri, Apr 12, 2019 at 1:47 PM Paul DeBruicker &lt;
>
>> pdebruic@
>
>> &gt; wrote:
>>>
>>> Oh and I forgot that the onClick: is housed in another function that
>>> allows
>>> the ajax pushState anchors to work even if JS is disabled or there is no
>>> session and looks like this:
>>>
>>>
>>> WAAnchorTag>>#pjaxCallback: aBlockClosure on: html withSuccessScript:
>>> aScript
>>>   self
>>>     onClick:
>>>         (self
>>>             processCallback: aBlockClosure
>>>             onSuccess: self setBodyJs , aScript
>>>             return: false
>>>             thenUpdateBodyOn: html);
>>>     callback: aBlockClosure
>>>
>>>
>>> &
>>>
>>> processCallback: aCallback onSuccess: aJSFunction return: aBoolean
>>> thenUpdateBodyOn: html
>>>   self hasSession
>>>     ifTrue: [
>>>       | scr |
>>>       scr := html jQuery ajax
>>>         html: [ :h |
>>>               aCallback value.
>>>               self renderUpdatedBodyContentFor: self session pjaxBody
>>> on:
>>> h
>>> ];
>>>         onSuccess: aJSFunction.
>>>       aBoolean notNil
>>>         ifTrue: [ scr return: aBoolean ].
>>>       ^ html jQuery this updateUrl
>>>         , ((html jQuery class: 'active') removeClass: 'active') , scr ]
>>>     ifFalse: [ ^ nil ]
>>>
>>>
>>> So its
>>>
>>> html anchor
>>>     pjaxCallback:[ self doSomethingYourClientsLove ] on: html
>>> withSuccessScript: (JSStream on:'alert("I don't believe that worked")');
>>>     with: 'Click me'
>>>
>>>
>>>
>>>
>>> Paul DeBruicker wrote
>>> > Oh man if only our past selves knew we really wanted Iliad apps what a
>>> > world
>>> > this would be....
>>> >
>>> >
>>> >
>>> > In my apps I use
>>> >
>>> >      $.fn.updateUrl = function(anId){
>>> >
>>> >          if (typeof(window.history.pushState) == 'function') {
>>> >              var relativeUrl = $(this).attr('href');
>>> >              if(typeof(relativeUrl) =='undefined'){
>>> >                  relativeUrl =
>>> > document.location.pathname+document.location.search;
>>> >              };
>>> >              if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
>>> > window.history.state){
>>> >                  window.history.pushState(relativeUrl, document.title,
>>> > relativeUrl);
>>> >              }
>>> >          }
>>> >      };
>>> >
>>> > and call it as part of the onClick: script on anchors and buttons
>>> BEFORE
>>> > loading content via ajax.
>>> >
>>> > So the onClick: is
>>> >
>>> >
>>> > onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn:
>>> html)
>>> >
>>> >
>>> > Also it doesn't need to be a jQuery function but is because it was
>>> > different
>>> > when I initially made it.
>>> >
>>> >
>>> >
>>> > Not sure its what you want to do but it definitely sets the window
>>> > location
>>> > (url) to something that when you hit refresh gives you the same page
>>> you
>>> > see
>>> > before you hit refresh (with updated server content, if anything there
>>> has
>>> > changed in the interim).
>>> >
>>> >
>>> >
>>> >
>>> >
>>> >
>>> > Siemen Baader wrote
>>> >> Hi,
>>> >>
>>> >> Not really answering your questions, but I think this is what Iliad
>>> is
>>> >> for. There you have access to a restful URL on every (xhr) request
>>> and
>>> >> every WAComponent is transparently updated via xhr, including when
>>> you
>>> >> use
>>> >> call-answer.
>>> >>
>>> >> Just FYI. :)
>>> >>
>>> >> Siemen
>>> >>
>>> >> Sent from my iPhone
>>> >>
>>> >>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;
>>> >
>>> >> johan@
>>> >
>>> >> &gt; wrote:
>>> >>>
>>> >>> Hi Esteban,
>>> >>>
>>> >>> I’m interested in this too.
>>> >>> We have the same kind of application (I guess… using mostly Ajax
>>> >>> updates,
>>> >>> I mean) but I have just been postponing a retry to address the
>>> >>> inconsistency with ajax, server state and the back button for
>>> years….
>>> >>>
>>> >>> Within a callback, you can get at the #actionUrl on the renderer.
>>> >>> Is that what you want?
>>> >>>
>>> >>> I’m trying to dive into what would be required here… trying to
>>> >>> understand
>>> >>> what you need:
>>> >>> Ajax requests always update the same continuation state in Seaside
>>> (i.e.
>>> >>> they do not create a new continuation)… so if you push that url to
>>> the
>>> >>> history after each ajax update, the back button will essentially
>>> request
>>> >>> the same but updated continuation from Seaside, meaning you should
>>> see
>>> >>> the state as it is in on the server, and not how it was before you
>>> made
>>> >>> the ajax request (which is what happens now).
>>> >>>
>>> >>> Does that correspond to what you are trying to fix?
>>> >>>
>>> >>> Johan
>>> >>>
>>> >>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;
>>> >
>>> >> emaringolo@
>>> >
>>> >> &gt; wrote:
>>> >>>>
>>> >>>> I have a Seaside application that is almost 100% AJAX driven to
>>> >>>> replace the visual components (no call/answer involved).
>>> >>>>
>>> >>>> I plan to to use history.pushState() and history.replaceState()
>>> >>>>
>>> >>>> My plan is to update the browser url after replacing some
>>> components,
>>> >>>> but using the URL building of the existing #updateUrl: (including
>>> >>>> _s/_k parameters and whatnot).
>>> >>>>
>>> >>>> Is it possible to obtain the URL of the session presenter within
>>> the
>>> >>>> ajax response?
>>> >>>>
>>> >>>> Thanks in advance,
>>> >>>>
>>> >>>> Esteban A. Maringolo
>>> >>>> _______________________________________________
>>> >>>> seaside mailing list
>>> >>>>
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >>>
>>> >>> _______________________________________________
>>> >>> seaside mailing list
>>> >>>
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >> _______________________________________________
>>> >> seaside mailing list
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >
>>> >
>>> >
>>> >
>>> >
>>> > --
>>> > Sent from: http://forum.world.st/Seaside-General-f86180.html
>>> > _______________________________________________
>>> > seaside mailing list
>>>
>>> > seaside@.squeakfoundation
>>>
>>> > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>>
>>>
>>>
>>>
>>>
>>> --
>>> Sent from: http://forum.world.st/Seaside-General-f86180.html
>>> _______________________________________________
>>> seaside mailing list
>>>
>
>> seaside@.squeakfoundation
>
>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> _______________________________________________
>> seaside mailing list
>
>> seaside@.squeakfoundation
>
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
>
>
>
>
> --
> Sent from: http://forum.world.st/Seaside-General-f86180.html
> _______________________________________________
> seaside mailing list

> seaside@.squeakfoundation

> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside





--
Sent from: http://forum.world.st/Seaside-General-f86180.html
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Achor validation

Maarten Mostert-2
Hello,

Is there something inside that allows me to trigger a smalltalk action as a validation before anything else.

I do something like this:

(html anchor)
attributeAt: 'href' put: ’Somefile' ;
class: 'pure-button pure-button-primary';
attributeAt: 'download' put:  ’some download file';
callback: [self  getInventoryFile];
with: 
[(html tag: 'i')
class: 'fas fa-download';
style: 'font-size: 14px;'.
html space.
html space.
html text: 'Download counter’]

The callback is not executed now as the anchor directly responds to the download attribute.

Thanks in advance.

Maarten

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Achor validation

Johan Brichau-2
Maarten,

What do you mean with ‘validation’ ? What do you want to validate?

The download attribute only tells the browser to download the url contents rather than open it in the browser [1]

If you want to produce the contents of the file from within Seaside, you will need to control the response in the callback, rather than execute an ‘action’.
The return value of an action callback is never what Seaside answers to the browser. This is what you seem to expect in your code snippet.

You need something like:
callback:[ self requestContext respond: [:response | self writeYourFileOnTheResponse: response ] ]

Mind that #callback: sets (and overwrites) the href attribute on the anchor.
That means the first statement that sets the href manually does not have any effect (and is, in fact, not correct).
If you want to put a href manually, use #url: 

Hope this helps
Johan


On 30 Apr 2019, at 18:24, Maarten Mostert <[hidden email]> wrote:

Hello,

Is there something inside that allows me to trigger a smalltalk action as a validation before anything else.

I do something like this:

(html anchor)
attributeAt: 'href' put: ’Somefile' ;
class: 'pure-button pure-button-primary';
attributeAt: 'download' put:  ’some download file';
callback: [self  getInventoryFile];
with: 
[(html tag: 'i')
class: 'fas fa-download';
style: 'font-size: 14px;'.
html space.
html space.
html text: 'Download counter’]

The callback is not executed now as the anchor directly responds to the download attribute.

Thanks in advance.

Maarten
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Updating browser history from an ajax response

Esteban A. Maringolo
In reply to this post by Paul DeBruicker
Hi Paul,

Yes, such alternative was presented in the "Combining Seaside with React" thread some weeks ago.

However it seems to be a big leap from the basic updating of URL using traditional request/response XHR requests.

The morphdom(), however, seems trivial to implement as an alternative to jQuery's replace().

Regards!

ps: I haven't pursued the PJAX approach not because I chose something else, but because my backlog got filled with more important things :)

Esteban A. Maringolo


On Mon, Apr 29, 2019 at 2:58 PM Paul DeBruicker <[hidden email]> wrote:
This could be a better way for instance:

https://dockyard.com/blog/2018/12/12/phoenix-liveview-interactive-real-time-apps-no-need-to-write-javascript





Paul DeBruicker wrote
> I fully believe there is a better/less hackish way to do this if its going
> to be a seaside feature.  But what I have does to the partial page
> update/back button/shareable links stuff well enough for me for now.
>
> You're right I do generally use it for navigation toreplace the content of
> a
> div with the id 'body' but  I also use it to
>
>     - update the page title
>     - load on page help for the current view
>     - move the person along in the guided tour if they're taking one
>     - update other indicators/notifications if they've changed.   e.g.
> "You
> have a new message from ..."
>
> I think that the DOM editing code could easily be adapted to also replace
> an
> arbitrary collection of specific DOM components like you'd prefer without
> too much trouble.  If you had an id for every DOM element you wanted to
> replace in the real DOM and a matching div with the content in the Seaside
> ajax response then you could itereate of the received divs putting their
> contents into the target ids in the real DOM.  The duplicated ids would
> only
> exist in the response handling code.  And you could update multiple spots
> on
> the page at once for a given round trip to the server.
>
>
> The setBodyJs code just searches the received div's putting them in the
> correct spots and then runs the scripts that were included in the
> response. 
> Anyway that code is
>
> MyComponent>>#setBodyJs
>       ^ (JSStream on: '$("#body").setBody(resp, status, xhr);') asFunction:
> #('resp' 'status' 'xhr')
>
>
>  $.fn.setBody = function (response, txtStatus, jqXhr) {
>          var content, body = $('#body'), newElement, target;
>          newElement=document.createElement('div');
>          newElement.innerHTML = response;
>         
>          content = newElement.querySelector('#b');
>          if(content){
>                  body.empty().append(content.innerHTML);
>          } else {
>              window.location.href = window.location.href.split("?")[0];
>          };
>
>          content = newElement.querySelector('#h');
>          if(content){
>              $('#helpBarContent').empty().append(content.innerHTML);
>          };
>
>          content = newElement.querySelector('#t');
>          if(content){
>              $('#tour').empty().append(content.innerHTML);
>          };
>
>          content = newElement.querySelector('#tp');
>          if(content){
>              $('#tourProgress').empty().append(content.innerHTML);
>          };
>
>          $(response).filter('script').each(function () {
>              $.globalEval(this.text || this.textContent || this.innerHTML
> ||
> '');
>          });
>          document.title = newElement.querySelector('#ti').innerHTML;
>          $viewport.scrollToTop();
>  };
>
> So you can see that its just finding elements by id ('#b' '#h' '#t' '#tp'
> '#ti') in the Seaside response and replacing others in the DOM and could
> be
> generalized into a loop if you wanted to spend the time to do it (if you
> were making it part of an open source package others could use for example
> ;) ) . 
>
>
> For keeping the URL in sync I do not use updateUrl: because it only (I
> think?) edits the response URL & instead I just edit the url for the
> anchor
> before the pjaxCallback: e.g.
>
> html anchor
>      backEndUrl:'mypage';
>      pjaxCallback:[self showMyPage] on: html;
>      with: 'click me to see my page'.
>
>
> and WAAnchorTag>>#backEndUrl: is
>
> backEndUrl: anExtraPath
>       self
>               useBaseUrl;
>               extraPath: MyApp appName;
>               extraPath: anExtraPath
>
> By editing the url for the anchor it can be copy and pasted among users
> both
> from the anchor on the page and the url bar in the browser once they've
> arrived on the page of interest.  It is only dissected and used in
> #initialRequest: when people share links  and when the back button is
> clicked as described below. I use cookies for sessions so there isn't a
> _s
> parameter in the urls that are created. 
>
>
> To handle the back button I'm overriding the browser's native back button
> handling.  There is some js where I add a header when the back button is
> clicked, and then look for that in my session class and handle it.
>
> The JS that adds the header is is
>
> window.onpopstate = function (event) {
>    // console.log(event.state);
>     if ($('body.back').length && event.state) {
>       $.ajax({url: event.state,
>                 success: function(data, textStatus, jqXhr)
> {$('body').setBodyAfterBackButton(data, textStatus, jqXhr);},
>                 beforeSend:
> function(xhr){xhr.setRequestHeader("X-Requested-With", "pjaxBB");}});     
>     }
> };
>
> and the setBodyAfterBackButton replaces the existing page with the Seaside
> ajax response.
>
> $.fn.setBodyAfterBackButton = function (data, status, jqXhr) {
>          var newDoc = document.open("text/html", "replace");
>          newDoc.write(data);
>          newDoc.close();
> };
>
>
> Then in the session I override #handleFiltered: like this
>
>
> MySession>>#handleFiltered: aRequestContext
>       (self isBackButtonPjaxRequest: aRequestContext)
>               ifTrue: [ self handleBackButtonRequest: aRequestContext ]
>               ifFalse: [ super handleFiltered: aRequestContext ]
>
>
> MySession>>#isBackButtonPjaxRequest: aRequestContext
>       ^ (aRequestContext request headers at: 'x-requested-with' ifAbsent: [ nil
> ]) = 'pjaxBB'
>
>
> MySession>>#handleBackButtonRequest: aRequestContext
>               | bdy path |
>               path := aRequestContext request uri path allButFirst.
>               bdy := self bodyFromBackButtonPath: path.
>               self  body: bdy.
>               aRequestContext request uri addField: '_n'.
>               super handleFiltered: aRequestContext
>
>
> MySession>>#bodyFromBackButtonPath: path
>    "App specific code to find which page of the app should be shown and
> what
> content should be in it just like in #initialRequest: "
>
>
>
>
> Thats it I think.  It works. Could be clearer/cleaner/more general.   I'd
> prefer it to be more automatic (evidenlty like Iliad I guess) but I'm not
> creating so many new things that setting the urls & adding the lookup code
> for the #bodyFromBackButtonPath:/#initialRequest: is a burden. 
>
>
> And like you remember I really only use this for navigation among areas of
> the app.  It doesn't remember if you had a modal open or a few accordions
> expanded and others collapsed or were editing a thing on one page and
> another thing on another page.  And I don't know how to go back to prior
> server states with this. It will take you back ten screens in order but
> that
> screen will have the most recently edited data.  Not be in the same state
> it
> was when you first looked at it.   Unlike the counter demo I'm not sure it
> should in a multi user app where others might've seen it or been affected
> by
> the changes.
>
> Hope this helps
>
> Paul
>
>
>
>
>
> Esteban A. Maringolo wrote
>> Hi Paul,
>>
>> I've seen that code before ;-) and maybe setting the URL **before** is
>> the way to go.
>>
>> In your case you always replace the "body" component of your page, I
>> might need to adapt it in a way that could replace any DOM element.
>>
>> Regarding this:
>> "Not sure its what you want to do but it definitely sets the window
>> location
>> (url) to something that when you hit refresh gives you the same page you
>> see
>> before you hit refresh (with updated server content, if anything there
>> has
>> changed in the interim)."
>>
>> I don't remember how you consumed that in #initialRequest and whether
>> you specified #updateUrl: (and kept it in sync on each
>> "pjaxCallback:..." call).
>>
>> Regards,
>>
>> Esteban A. Maringolo
>>
>> On Fri, Apr 12, 2019 at 1:47 PM Paul DeBruicker &lt;
>
>> pdebruic@
>
>> &gt; wrote:
>>>
>>> Oh and I forgot that the onClick: is housed in another function that
>>> allows
>>> the ajax pushState anchors to work even if JS is disabled or there is no
>>> session and looks like this:
>>>
>>>
>>> WAAnchorTag>>#pjaxCallback: aBlockClosure on: html withSuccessScript:
>>> aScript
>>>   self
>>>     onClick:
>>>         (self
>>>             processCallback: aBlockClosure
>>>             onSuccess: self setBodyJs , aScript
>>>             return: false
>>>             thenUpdateBodyOn: html);
>>>     callback: aBlockClosure
>>>
>>>
>>> &
>>>
>>> processCallback: aCallback onSuccess: aJSFunction return: aBoolean
>>> thenUpdateBodyOn: html
>>>   self hasSession
>>>     ifTrue: [
>>>       | scr |
>>>       scr := html jQuery ajax
>>>         html: [ :h |
>>>               aCallback value.
>>>               self renderUpdatedBodyContentFor: self session pjaxBody
>>> on:
>>> h
>>> ];
>>>         onSuccess: aJSFunction.
>>>       aBoolean notNil
>>>         ifTrue: [ scr return: aBoolean ].
>>>       ^ html jQuery this updateUrl
>>>         , ((html jQuery class: 'active') removeClass: 'active') , scr ]
>>>     ifFalse: [ ^ nil ]
>>>
>>>
>>> So its
>>>
>>> html anchor
>>>     pjaxCallback:[ self doSomethingYourClientsLove ] on: html
>>> withSuccessScript: (JSStream on:'alert("I don't believe that worked")');
>>>     with: 'Click me'
>>>
>>>
>>>
>>>
>>> Paul DeBruicker wrote
>>> > Oh man if only our past selves knew we really wanted Iliad apps what a
>>> > world
>>> > this would be....
>>> >
>>> >
>>> >
>>> > In my apps I use
>>> >
>>> >      $.fn.updateUrl = function(anId){
>>> >
>>> >          if (typeof(window.history.pushState) == 'function') {
>>> >              var relativeUrl = $(this).attr('href');
>>> >              if(typeof(relativeUrl) =='undefined'){
>>> >                  relativeUrl =
>>> > document.location.pathname+document.location.search;
>>> >              };
>>> >              if(relativeUrl!=='javascript:void(0)' && relativeUrl !==
>>> > window.history.state){
>>> >                  window.history.pushState(relativeUrl, document.title,
>>> > relativeUrl);
>>> >              }
>>> >          }
>>> >      };
>>> >
>>> > and call it as part of the onClick: script on anchors and buttons
>>> BEFORE
>>> > loading content via ajax.
>>> >
>>> > So the onClick: is
>>> >
>>> >
>>> > onClick: html jQuery this updateUrl, (self doContentLoadingMagicOn:
>>> html)
>>> >
>>> >
>>> > Also it doesn't need to be a jQuery function but is because it was
>>> > different
>>> > when I initially made it.
>>> >
>>> >
>>> >
>>> > Not sure its what you want to do but it definitely sets the window
>>> > location
>>> > (url) to something that when you hit refresh gives you the same page
>>> you
>>> > see
>>> > before you hit refresh (with updated server content, if anything there
>>> has
>>> > changed in the interim).
>>> >
>>> >
>>> >
>>> >
>>> >
>>> >
>>> > Siemen Baader wrote
>>> >> Hi,
>>> >>
>>> >> Not really answering your questions, but I think this is what Iliad
>>> is
>>> >> for. There you have access to a restful URL on every (xhr) request
>>> and
>>> >> every WAComponent is transparently updated via xhr, including when
>>> you
>>> >> use
>>> >> call-answer.
>>> >>
>>> >> Just FYI. :)
>>> >>
>>> >> Siemen
>>> >>
>>> >> Sent from my iPhone
>>> >>
>>> >>> On 12 Apr 2019, at 17.23, Johan Brichau &lt;
>>> >
>>> >> johan@
>>> >
>>> >> &gt; wrote:
>>> >>>
>>> >>> Hi Esteban,
>>> >>>
>>> >>> I’m interested in this too.
>>> >>> We have the same kind of application (I guess… using mostly Ajax
>>> >>> updates,
>>> >>> I mean) but I have just been postponing a retry to address the
>>> >>> inconsistency with ajax, server state and the back button for
>>> years….
>>> >>>
>>> >>> Within a callback, you can get at the #actionUrl on the renderer.
>>> >>> Is that what you want?
>>> >>>
>>> >>> I’m trying to dive into what would be required here… trying to
>>> >>> understand
>>> >>> what you need:
>>> >>> Ajax requests always update the same continuation state in Seaside
>>> (i.e.
>>> >>> they do not create a new continuation)… so if you push that url to
>>> the
>>> >>> history after each ajax update, the back button will essentially
>>> request
>>> >>> the same but updated continuation from Seaside, meaning you should
>>> see
>>> >>> the state as it is in on the server, and not how it was before you
>>> made
>>> >>> the ajax request (which is what happens now).
>>> >>>
>>> >>> Does that correspond to what you are trying to fix?
>>> >>>
>>> >>> Johan
>>> >>>
>>> >>>> On 12 Apr 2019, at 15:17, Esteban Maringolo &lt;
>>> >
>>> >> emaringolo@
>>> >
>>> >> &gt; wrote:
>>> >>>>
>>> >>>> I have a Seaside application that is almost 100% AJAX driven to
>>> >>>> replace the visual components (no call/answer involved).
>>> >>>>
>>> >>>> I plan to to use history.pushState() and history.replaceState()
>>> >>>>
>>> >>>> My plan is to update the browser url after replacing some
>>> components,
>>> >>>> but using the URL building of the existing #updateUrl: (including
>>> >>>> _s/_k parameters and whatnot).
>>> >>>>
>>> >>>> Is it possible to obtain the URL of the session presenter within
>>> the
>>> >>>> ajax response?
>>> >>>>
>>> >>>> Thanks in advance,
>>> >>>>
>>> >>>> Esteban A. Maringolo
>>> >>>> _______________________________________________
>>> >>>> seaside mailing list
>>> >>>>
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >>>
>>> >>> _______________________________________________
>>> >>> seaside mailing list
>>> >>>
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >> _______________________________________________
>>> >> seaside mailing list
>>> >
>>> >> seaside@.squeakfoundation
>>> >
>>> >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>> >
>>> >
>>> >
>>> >
>>> >
>>> > --
>>> > Sent from: http://forum.world.st/Seaside-General-f86180.html
>>> > _______________________________________________
>>> > seaside mailing list
>>>
>>> > seaside@.squeakfoundation
>>>
>>> > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>>>
>>>
>>>
>>>
>>>
>>> --
>>> Sent from: http://forum.world.st/Seaside-General-f86180.html
>>> _______________________________________________
>>> seaside mailing list
>>>
>
>> seaside@.squeakfoundation
>
>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>> _______________________________________________
>> seaside mailing list
>
>> seaside@.squeakfoundation
>
>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
>
>
>
>
>
> --
> Sent from: http://forum.world.st/Seaside-General-f86180.html
> _______________________________________________
> seaside mailing list

> seaside@.squeakfoundation

> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside





--
Sent from: http://forum.world.st/Seaside-General-f86180.html
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside