Those demos demonstrate ability of Flask-Starter to easily show same content inside both modal and regular windows.
Also, chain loading, submitting of forms from modal windows with return to page inside modal or normal redirect.
Everything easily configured on View-level and by html classes.
This is only brief overview of RemoteModal and Requests JS modules capabilities. But for beginners the structure of project itself should be more interesting.

Lorem ipsum demo

Separate page Modal page
<a href="{{ url_for('main.lorem_ipsum') }}" class="btn btn-primary">Separate page</a>
<a href="{{ url_for('main.lorem_ipsum') }}" class="btn btn-info modal_remote">Modal page</a>
class LoremIpsum(MainPage):
    page_header = "Lorem Ipsum"
    template = "demos/lorem_ipsum.html"

Lorem ipsum, login required

Separate page Modal page
<a href="{{ url_for('main.lorem_ipsum_login_required') }}" class="btn btn-primary">Separate page</a>
<a href="{{ url_for('main.lorem_ipsum_login_required') }}" class="btn btn-info modal_remote">Modal page</a>
 class LoremIpsumLoginRequired(MainPage):
     login_required = True
     page_header = "Lorem Ipsum, Login Required"
     template = "demos/lorem_ipsum.html"

Button as Post

1) POST it to the next page
action class forces request to link to be made with POST method with CSRF key
<a data-href="{{ url_for('main.requires_post') }}" class="btn btn-primary action">1) POST it with traversing page</a>
class ActionRequiresPost(MainPage):
 track = False #no need to track such actions
 page_header = "Action submitted"

 def get(self,*k,**kk):
     #if came by regular link -> just redirect to current page
     return redirect(self.tracker.current)

 @action #forces csrf check
 def post(self,*k,**kk):
     if self.method=='modal':
         return render_template("demos/action_submitted.html",**self.context)
         return redirect(url_for('main.action_submitted'))
2) POST it in background with modal
action-remote class sends request by AJAX and expect response, also sets method to "modal", so same View actualy used
<a style="margin-top: 4px;" data-href="{{ url_for('main.requires_post') }}" class="btn btn-primary action action-remote">2) POST it in background with modal</a>
3) POST it with message response
This view is very similar and classes are same. The only difference is different response from server which produce different client side result
class ActionRequiresPostMessage(ActionRequiresPost):

    def post(self,*k,**kk):
        return response_ok(message="Action submitted")

Action with confirmation

Delete Roll Dice!
Same View called two times, one time this view returns confirmation URL and if user confirms it View called again, but this time confirmation verification passes.
Yet again, control on server side, just action and action-remote in html.
Also take a note, in Views/Pages that not designated to be viewed by regular GET request it is better to specify data-href and not href, so crawlers wont follow it and user also wont follow if some error occured.
But in views like Lorem ipsum it is better to leave HREF, so bots will follow it and see full page, while user sees modal. Same link, altering on-click.
<a data-href="{{ url_for('main.action_with_confirmation') }}" class="btn btn-danger action action-remote">Delete</a>
<a data-href="{{ url_for('main.action_with_confirmation_create') }}" class="btn btn-success action action-remote">Roll Dice!</a>
class ActionWithConfirmation(MainPage):

    def post(self,*k,**kk):
        if request.args.get('confirmed'):
            return response_ok(message="Item deleted")
            return response_request_confirmation(
                message="Are you sure?",

class ActionWithConfirmationCreate(MainPage):
    template = "demos/created_item_description.html"
    page_header = "Done!"


    def post(self,*k,**kk):
        if request.args.get('confirmed'):
            return render_template(self.template,**self.context)
            return response_request_confirmation(
                message="Are you sure? You got only one chance!",

Modal links chaining

Page With link
Another popular issue is links inside modal windows. Some links should be opened in same modal, some in new window, some should redirect to another page.
Everything is possible by simple classes!
By default links are opened in modal and forms submitted to modal. While .direct_link, .popup_window can give desired behaviour.
<a href="{{ url_for('main.lorem_ipsum') }}" class="btn btn-info">Ipsum without required login</a><br>
<a href="{{ url_for('main.lorem_ipsum_login_required') }}" class="btn btn-info">Ipsum with required login</a><br>
<a href="{{ url_for('main.page_with_links2') }}" class="btn btn-warning">Another page with links</a><br>
<a href="/" class="btn btn-primary direct_link" >Link opened in main window</a><br>
<a href="/" class="btn btn-primary popup_window">Link opened in new tab/window</a><br>

User-owned pages

Only user "Tigra" can access Modal
Just redefine view_level_permissions in your View class, attach User object to self.item_user and it will be compared with current_user automaticaly
class PageWithItemUser(MainPage):
    template = "demos/page_with_item_user.html"
    page_header = "By Tigra for Tigra"

    def view_level_permissions(self,*k,**kk):
        return True

#in Real world however, something like this usualy done
    def view_level_permissions(self,item=item_id,*k,**kk):
        if self.__item is None:
                return False

        return True

And what about callbacks?

Also easy. Just attach callback to the action. Alternative way will be to return your JS <script></script> inside modal HTML
Action with callback Modal with callback Flask Starter App by Tigra
<a id="with_callback" data-href="{{ url_for('main.requires_post_message') }}" class="btn btn-primary action action-remote">Action with callback</a>
<a id="with_callback2" href="{{ url_for('main.lorem_ipsum') }}" class="btn btn-info modal_remote">Modal with callback</a>
{% block scripts %}
    {{ super() }} {# by calling block and super() JS actualy placed in the HEAD of page #}
            alert('Called back');
{% endblock %}
comments powered by Disqus