kxtronic
kxtronic

Reputation: 351

GAE/Python best practice for a pop-up alert?

I need to have an alert pop-up in Google App Engine when the user inputs a value that would create a duplicate item. What is the best way to do this?

I found a way to do it, but it's far from elegant:

When the user inputs a duplicate project code I show a new page "error-message.html" which explains the user what happenned and provides a "back" button. However, I would rather show an alert on the "add-project.html" page. Javascript would be a good idea but it can't validate in the database, so it's no use.

What is the best practice in GAE/Python to show an alert box triggered from code?

So any help or suggestions would be appreciated!

Thanks!

CODE:

The app I'm working on now is a simple project manager's tool. I'm using the webapp2 included with GAE.

Current workflow: 1) In AddProject I show the user a page to input new project data ("add-project.html). This page is based on a JINJA template. User fills in data and Clicks on submit. 2) Submit calls SaveAddedProject which checks in the NDB if the project code already exists. 3) If project code is not in the NDB yet, the new project is saved in the NDB and the code re-directs to the general projects page. 4) If the project exists I show a new page called "error-message.html" which tells the user he/she is inputting a duplicate value and provides a button to go back. The user can then change his value and retry.

Model:

class Project(ndb.Model):
  # Models and individual Project entry
  proj_id = ndb.StringProperty(required = True)
  proj_desc = ndb.StringProperty(required = True)
  proj_status = ndb.StringProperty(required = True)

Dispatchers:

class AddProject(webapp2.RequestHandler):
# Displays template to fill-in data for new project, 
# then calls SaveAddedProject
  def post(self):
    user = users.get_current_user() # We use Google's login system
    logout_url = users.create_logout_url(self.request.url)
    template_values = {
        'user_id': user,
        'logout_url': logout_url,
    }

    template = JINJA_ENVIRONMENT.get_template('add-project.html')
    self.response.write(template.render(template_values))

class SaveAddedProject(webapp2.RequestHandler):
# Saves a new project. Is called from AddProject
  def post(self):
    proj_id_str = self.request.get('proj_id')
    proj_desc_str = self.request.get('proj_desc')
    proj_status_str = self.request.get('proj_status')
    # The add template already validates required fields aren't empty :-)

    # Verify we don't duplicate the project_id
    pq = Project.query(ancestor = get_projects_key())
    pq = pq.filter(Project.proj_id == proj_id_str)
    pq_result = pq.fetch(limit=1)
    if len(pq_result) > 0:  # Notify the user
            template_values = {'error_message': 'Project ID ' + proj_id_str + ' already exists',
            }
            template = JINJA_ENVIRONMENT.get_template('error-message.html')
            self.response.write(template.render(template_values))
    else:
        # Create new project
        project = Project(parent=get_projects_key()) # Use root ancestor key  
        # Populate fields, save to NDB
        project.proj_id = proj_id_str
        project.proj_desc = proj_desc_str
        project.proj_status = proj_status_str
        project.put()
        self.redirect('/projects')

HTML for add-project.html:

<!DOCTYPE html>
{% autoescape true %}
<html>
  <head>
    <link type="text/css" rel="stylesheet" href="/stylesheets/pages.css" />
    <title>Action List</title>
    <script>
      function validateForm() {
    var count = 0;
    var x = document.forms["form1"]["proj_id"].value;
    if (x == null || x == "") {
       count = 1;
       }
     var x = document.forms["form1"]["proj_desc"].value;
     if (x == null || x == "") {
       count = 1;
       }
    if (count == 1) {
       document.getElementById('AlertMsg').innerHTML = '<font color="red">Please fill in all fields marked with *</font>';
        return false;
       }
     }
</script>
</head>
<body>
  <h2>Add Project</h2>
  <br>
  <b>User: {{ user_id }} </b>
  <br><br>
  <form name="form1" action="/p-save-add" onsubmit="return validateForm()" method="post">
    <div>
      ID * <input value="" name="proj_id" size="15">
     Description * <input value="" name="proj_desc" size="50">
     Status: <select name='proj_status'>
        <option value='Open'>Open</option>
        <option value='Closed'>Closed</option>
      </select>
    </div>   
    <br>
    <div><input type="submit" value="Save"></div>
  </form>
  <br>
  <br>
  <b id='AlertMsg' > </b>
</body>
</html>
{% endautoescape %}

HTML for error-message.html:

<!DOCTYPE html>
{% autoescape true %}

<script>
function goBack() {
  window.history.back();
}
</script>


<html>
  <head>
    <title>ActionList</title>
     <link type="text/css" rel="stylesheet"      href="/stylesheets/messages.css" />
  </head>

<body>
  <h3> Sorry, an error ocurred!</h3>    
  {{ error_message }}
  <br>
  <br>
  <button onclick="goBack()">Back</button>
</body>

{% endautoescape %}

Upvotes: 1

Views: 639

Answers (1)

Michael Aaron Safyan
Michael Aaron Safyan

Reputation: 95639

You should use both approaches. The approach where you simply show an error page is the right way to handle the issue both from a security perspective (validation needs to happen on the server, because the form can be submitted in a way that bypasses validation in JavaScript) and from a compatibility perspective (browsers may disable JavaScript and access your form without it).

However, from a user experience perspective, it is nice to get validation errors on the same page as the form without having to navigate to some new page just to see the error (and have to fill out the form again). Note that you can still do this validation in the UI via JavaScript even if your validation check has to happen on the server; you can do this by using an XMLHttpRequest to submit the form in JavaScript so that your JavaScript code is capable of seeing/handling the response. Then, you can have your response message indicate this as one of the various possible errors.

For an example, see how the "Feedback" mechanism works on my website; you'll note that there is a version of the form that works without JavaScript (the "/feedback" page), but if you have JavaScript enabled, the button actually opens a dialogue in JavaScript that submits the form using XMLHttpRequest, and if the form fails to submit, the error is reported directly in the dialogue without navigating to a new page (by reading the status code of the response or looking at the detailed error message included in the JSON response that was returned by the server).

Upvotes: 1

Related Questions