Tuesday, December 17, 2013

Little break.

Hi guys! For the next time I have to go on with my startup team, hence I can't get enough spare time for this blog.

I hope you understand my point of view!

#golang 4ever

Saturday, December 14, 2013

Post VIII: Delete an entry out of the Google App Engine Datastore!

Today I'm a little bit lazy, because of the christmas party last night. Here you get the code for the delete function. :-)
Easy as usual!


func deleteEntries(mealsID []string, r *http.Request) int{
 // Get context from 
    c := appengine.NewContext(r);

 
 for _,id := range mealsID{
  ID,_ := strconv.Atoi(id)
  // Build query to receive the values
  q:= datastore.NewQuery("Meal").Ancestor(mealStoreKey(c)).Filter("Id =", ID ).KeysOnly()
  // Receive all keys and delete them.
  keys, err := q.GetAll(c, nil)
  if err != nil{
   return 0
  }
  log.Printf("ID: %v ", id)
  log.Printf("Keys: %v ", keys)
  e := datastore.DeleteMulti(c, keys)
  if e != nil{
   log.Printf("%v ", e)
   return 33  
  }  
 }

 return len(mealsID)
}


I think, the next post will be longer!.

P.S. Checkout my repository on Bitbucket for the latest code!  https://bitbucket.org/loose11/mycanteen

Wednesday, December 11, 2013

Part VII: Now save and show asynchronous

Great stuff I have to told! We gonna make our app a little bit fancy. :-)
After this post, you got this:



We have to enhance our Javascript file and have to add a new function "show" to the go-code.
I also adjust the code a little bit, so we can save and receive a message if the storing was successful.


Our modified HTMLConstants + the template
// Parse the HTMLTemplate, despite it is not neccesary yet.
var adminPanelTemplate = template.Must(template.New("adminPanelHTML").Parse(adminPanelHTML))
var saveTemplate = template.Must(template.New("saveHTML").Parse(saveHTML))
var entriesTemplate = template.Must(template.New("entriesHTML").Parse(entriesHTML))

const saveHTML = `
Saved to Datastore!
` const entriesHTML = `{{range .}}
{{.Date}} {{.Course}}: {{.Name}} costs {{.Price}}$
{{end}}`

By the way we added to the adminPanelHTML a new form, so we can submit the button "save"

After that we add the new http.HandlFunc("/show", show) and the new show-function.
func show(w http.ResponseWriter, r *http.Request){

   allMeals := getAllEntries(r)

 if err := entriesTemplate.Execute(w,allMeals);err != nil{
    http.Error(w, err.Error(), http.StatusInternalServerError)
 }
 
}
Finally the Jquery-Code. It is very similiar to the function before, but we don't pass data via the ajax request.
$( document ).ready(function() {
 $("#saveMeal").submit(function(event){
  
   event.preventDefault();
   
      var formData = $("#saveMeal").serializeArray();
    var URL = $("#saveMeal").attr("action");
   
    $.post( 
              URL,
              formData,
              function(data) {
                 $('#result').html($(data).filter("#content"));
              }); 
      
    }); 
 $("#showMeal").submit(function(event){
  event.preventDefault();

  var URL = $("#showMeal").attr("action");
  $.post(
   URL,
   function(data){
    $('#result').html($(data).filter("#content"));   
   });
 });   

});



So thats all for this evening. Tommorow I will show you, how to delete some entries.

P.S. Checkout my repository on Bitbucket for the latest code!  https://bitbucket.org/loose11/mycanteen

Tuesday, December 10, 2013

Part VI: Something little happens with JQuery

Good evening folks! We going to change the code a little bit. First of all we add to our adminPanelHTML JQuery and our own JQuery-Script adminscript.js



<html>
<head>
		<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
		<script src="js/adminScript.js"></script>
</head>
<body>
<form action="/save" id="saveMeal">
<select name="course">
     <option value="starter">starter</option>
    <option value="entree">entree</option>
    <option value="dessert">dessert</option>
   </select>
   
   Name: <input id="name" name="name" type="text">
   
   Price: <input name="price" type="text">
   
   Date: <input id="datepicker" name="date" type="text">
   <input id="saveMealButton" type="submit" value="Check">
  </form>
  <div id="result"></div>
</body>
</html>
After this, we add a new template for the async response:
var entriesTemplate = template.Must(template.New("entriesHTML").Parse(entriesHTML))

const entriesHTML =  `{{range .}}<div id="content">{{.Course}}{{.Name}}<input type="checkbox" name="checkbox" value="{{.Id}}"/></div></br>{{end}}`


And comment the saveHTML out.

After all we modify the Food struct, so we can save an Id (for us the actual timestamp, it is enough for our purpose) and we change saveTemplate to entriesTemplate

Now we do some JQuery, only easy stuff. Here is the script:

$( document ).ready(function() {$("#saveMeal").submit(function(event){
		        // Prevent from Default behavior, also submit
			event.preventDefault();
			
    		var formData = $("#saveMeal").serializeArray();
				var URL = $("#saveMeal").attr("action");
			
				$.post( 
	             URL,
	             formData,
	             function(data) {
	                $('#result').html($(data).filter("#content"));
	             });	
    		
    	});	
				

});

We wait until the Document is ready or loaded, then we catch the #saveMeal form and serialize the form to an array. After that we get the URL in our example "/save". And than the magic happened. The server get our request for the URL and we put the formData. Then we use a callback function, which populate us the content do the "result" div.

Easy going, what?! At the end we have to modify our app.yaml.
application: mycanteen
version: 1
runtime: go
api_version: go1
 
handlers:
- url: /js
  static_dir: js
- url: /.*
  script: _go_app


We added a static_dir, so the app engine finds the scripts.

P.S. Checkout my repository on Bitbucket for the latest code!  https://bitbucket.org/loose11/mycanteen

Sunday, December 8, 2013

Part V: Oh, we stored something to the App Engine? But where is it!

Good evening! First of all I have to apologize for my poor english writing.

Todays lesson will be about querying the App Engine Datastore to fetch all entities. Luckily we don't need new imports :) We have all what we need.

However we implement a new function called getAllEntries which gets the request and returns a "pointer" to an Food array. Adding a new template for our query output is also included.

We call this function in the save function.




package mycanteen
   
import(
 "net/http"
 "html/template"
 "strconv"
 "fmt"
 "appengine"
 "appengine/datastore"
)

type Food struct{
 Course string
 Name string 
 Date string
 Price float64
}
   
// No main function. Go App Engine use the init-func
func init() {
    http.HandleFunc("/", root)
    http.HandleFunc("/admin", admin);
    http.HandleFunc("/save", save)
}
   
func root(w http.ResponseWriter, r *http.Request) {    
    // Here we will add the Userpanel, but not know 
}
 
/*********************ADMINPANEL HANDLER**************************/
func admin(w http.ResponseWriter, r *http.Request) {      
    // Execute the parsing. We pass the ResponseWriter and a second value, which should be fill the gaps at the template. 
    // We have no gaps, so we have nothing to fill in. 
    if err := adminPanelTemplate.Execute(w, ""); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}
/********************SAVE/PERSIST HANDLER************************/
func save(w http.ResponseWriter, r *http.Request) {    


    // You have to parse the form from our admin panel. It's mandatory!
    if err := r.ParseForm(); err != nil {
     fmt.Fprint(w,err) 
    }
     
    // Now we a readey to get the fish ;-)
    course := r.FormValue("course")
    name := r.FormValue("name")
    date := r.FormValue("date")
    // The form is string based, for later purpose we parse it to float
    price, _ := strconv.ParseFloat(r.FormValue("price"), 64)
    
    f := Food{course,name,date,price,}
    // Persist to Datastore
    // At this moment we cant save the values, hence we setup only this output to check the values.
    if  saveToDatastore(f,r) != true{
     http.Error(w, "Error until persist the meal", http.StatusInternalServerError)
    }

 allMeals := getAllEntries(r)
 if err := saveTemplate.Execute(w,allMeals);err != nil{
  http.Error(w, err.Error(), http.StatusInternalServerError)
 }

}

func saveToDatastore(f Food, r *http.Request) bool{
 // Get context from 
    c := appengine.NewContext(r);

    key := datastore.NewIncompleteKey(c, "Meal", mealStoreKey(c))
    _, err := datastore.Put(c, key, &f)
    if err != nil{
 return false
    }else{
 return true
    }
}

/******************Fetch all Entries from the datastore********************/
func getAllEntries(r *http.Request) []*Food{
 var q *datastore.Query
 var allMeals []*Food

 c := appengine.NewContext(r);
 // Query for all entries in the datastore with the meal store key
 q = datastore.NewQuery("Meal").Ancestor(mealStoreKey(c))
 if _, err := q.GetAll(c, &allMeals); err != nil {  
        return allMeals
    }

    return allMeals
}

// foodKey returns the key used for all food entries.
func mealStoreKey(c appengine.Context) *datastore.Key {
    // The string "default_food" here could be varied to have multiple cantines.
    return datastore.NewKey(c, "MealStore", "meal", 0, nil)
}
 
// Parse the HTMLTemplate, despite it is not neccesary yet.
var adminPanelTemplate = template.Must(template.New("adminPanelHTML").Parse(adminPanelHTML))
var saveTemplate = template.Must(template.New("saveHTML").Parse(saveHTML))
   
// Here you define the HTML which will be parsed.
const adminPanelHTML= `
<html>
<head>
</head>
<body>
Name: Price: Date:
</body> </html> ` const saveHTML=` <html> <head> </head> <body> <table border=1> <tr><th>Course</th><th>Name</th><th>Price</th><th>Date</th></tr> {{range .}} <tr><td>{{.Course}}</td><td>{{.Name}}</td><td>{{.Price}}</td><td>{{.Date}}</td><td><input type="checkbox" /></td></tr> {{end}} </table> <a href="/admin">Back</a> </body> </html> `

For the QueryBuilder look at this page, they describe it very well :)  projectionqueries
The c.getAll function needs a query and a array (slice) to fetch all entities.


The checkbox is for later purpose, so we can delete entries later. :) Maybe in the next post.

See ya!

P.S. Checkout my repository on Bitbucket for the latest code!  https://bitbucket.org/loose11/mycanteen

Friday, December 6, 2013

Part IV: Access the backend datastore

Folks, what's going on? To night we gonna have party? Just kidding, we talk about serious stuff.
After all our app reads the input, but what should we do with these values? Easy!
Save it to the app engine datastore.
In first place we add some imports
"appengine", "appengine/datastore" 
also setup a struct for our entity finally we introduce two new functions one named
saveToDatastore(f Food, r *http.Request) bool
and second
mealStoreKey(c appengine.Context) *datastore.Key .

I added some more comments to the code.


package mycanteen
   
import(
 "net/http"
 "html/template"
 "strconv"
 "fmt"
 "appengine"
 "appengine/datastore"
)

type Food struct{
 Course string
 Name string 
 Date string
 Price float64
}
   
// No main function. Go App Engine use the init-func
func init() {
    http.HandleFunc("/", root)
    http.HandleFunc("/admin", admin);
    http.HandleFunc("/save", save)
}
   
func root(w http.ResponseWriter, r *http.Request) {    
    // Here we will add the Userpanel, but not know 
}
 
/*********************ADMINPANEL HANDLER**************************/
func admin(w http.ResponseWriter, r *http.Request) {      
    // Execute the parsing. We pass the ResponseWriter and a second value, which should be fill the gaps at the template. 
    // We have no gaps, so we have nothing to fill in. 
    if err := adminPanelTemplate.Execute(w, ""); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}
/********************SAVE/PERSIST HANDLER************************/
func save(w http.ResponseWriter, r *http.Request) {    


    // You have to parse the form from our admin panel. It's mandatory!
    if err := r.ParseForm(); err != nil {
     fmt.Fprint(w,err) 
    }
     
    // Now we a readey to get the fish ;-)
    course := r.FormValue("course")
    name := r.FormValue("name")
    date := r.FormValue("date")
    // The form is string based, for later purpose we parse it to float
    price, _ := strconv.ParseFloat(r.FormValue("price"), 64)
    
    f := Food{course,name,date,price,}
    // Persist to Datastore
    // At this moment we cant save the values, hence we setup only this output to check the values.
    if  saveToDatastore(f,r) == true{
     fmt.Fprintf(w, "You \"saved\" successfully: Our first %s %s costs %v %s on %s", course,name,price,string(0x20AC),date)
    }else{
        fmt.Fprintf(w, "Error happens, can't persist to database")
    }
 
}

func saveToDatastore(f Food, r *http.Request) bool{
 // Get context from 
    c := appengine.NewContext(r);

    key := datastore.NewIncompleteKey(c, "Meal", mealStoreKey(c))
    _, err := datastore.Put(c, key, &f)
    if err != nil{
 return false
    }else{
 return true
    }
}

// foodKey returns the key used for all food entries.
func mealStoreKey(c appengine.Context) *datastore.Key {
    // The string "default_food" here could be varied to have multiple cantines.
    return datastore.NewKey(c, "MealStore", "meal", 0, nil)
}
 
// Parse the HTMLTemplate, despite it is not neccesary yet.
var adminPanelTemplate = template.Must(template.New("adminPanelHTML").Parse(adminPanelHTML))
   
// Here you define the HTML which will be parsed.
const adminPanelHTML= `
<html>
<head>
</head>
<body>
Name: Price: Date:
</body> </html> `
mealStoreKey: This function returns a key used for all meal structs (entities) saveToDataStore: At this place we do the magic. We pass it a new, incomplete key so that the datastore will create a new key for this record automatically. Finally store it to the backend. Here are the evidence:

htpp://localhost:8000

See ya tommorrow!

Wednesday, December 4, 2013

Part III: Let's catch the fish

Folks! Today I'm going to show you, how to submit the form and pass the necessary values through a custom handler.
package mycanteen
  
import(
 "net/http"
 "html/template"
 "strconv"
 "fmt"
)
  
// No main function. Go App Engine use the init-func
func init() {
    http.HandleFunc("/", root)
    http.HandleFunc("/admin", admin);
    http.HandleFunc("/save", save)
}
  
func root(w http.ResponseWriter, r *http.Request) {    
    // Here we will add the Userpanel, but not know 
}

/*********************ADMINPANEL HANDLER**************************/ 
func admin(w http.ResponseWriter, r *http.Request) {      
    // Execute the parsing. We pass the ResponseWriter and a second value, which should be fill the gaps at the template. 
    // We have no gaps, so we have nothing to fill in. 
    if err := adminPanelTemplate.Execute(w, ""); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}
/********************SAVE/PERSIST HANDLER************************/
func save(w http.ResponseWriter, r *http.Request) {    
    // You have to parse the form from our admin panel. It's mandatory!
    if err := r.ParseForm(); err != nil {
     fmt.Fprint(w,err) 
    }
    
    // Now we a readey to get the fish ;-)
    course := r.FormValue("course")
    name := r.FormValue("name")
    date := r.FormValue("date")
    // The form is string based, for later purpose we parse it to float
    price, _ := strconv.ParseFloat(r.FormValue("price"), 64)
    
    // At this moment we cant save the values, hence we setup only this output to check the values. 
    fmt.Fprintf(w, "You \"saved\" successfully: Our first %s %s costs %v %s on %s", course,name,price,string(0x20AC),date)
    
}

// Parse the HTMLTemplate, despite it is not neccesary yet.
var adminPanelTemplate = template.Must(template.New("adminPanelHTML").Parse(adminPanelHTML))
  
// Here you define the HTML which will be parsed.
const adminPanelHTML= `
<html>
<head>
</head>
<body>
Name: Price: Date:
</body> </html> `
At the beginning we add our libaries strconv(String Converting) & fmt(Formatting).
Setting the new custom handler save, we are able to catch the request. Otherwise the app can't handle it.
Want not to explain the save-Function, because I write self-explaining comments. // Comments are helpful as usual!
Don't forget update the form action value.

That's all for today, stay tuned!