How to Persist a Variable Across a Go

Is there a way to persist a variable across a go?

The go command is used to split code into separate batches. If that is exactly what you want to do, then you should use it, but it means that the batches are actually separate, and you can't share variables between them.

In your case the solution is simple; you can just remove the go statements, they are not needed in that code.

Side note: You can't use a variable in a use statement, it has to be the name of a database.

Global Variables with GO

Temporary tables do survive go's:

create table #vars (someVar1 varchar(10), someVar2 int)
insert #vars values ('abc',123)

Get value:

select someVar1 from #vars

Set value

update #vars set someVar1 = 'def'

Temporary tables are specific to your connection, so they're not more global than they have to be.

How to have a global variable accessible across all packages

declare a variable at the top level - outside of any functions:

var Global = "myvalue"

func InitApp() (string) {
var Global= "myvalue"
return Global

}

Since the name of the variable starts with an uppercase letter, the variable will be available both in the current package through its name - and in any other package when you import the package defining the variable and qualify it with the package name as in: return packagename.Global.

Here's another illustration (also in the Go playground: https://play.golang.org/p/h2iVjM6Fpk):

package main

import (
"fmt"
)

var greeting = "Hello, world!"

func main() {
fmt.Println(greeting)
}

See also Go Tour: "Variables" https://tour.golang.org/basics/8 and "Exported names" https://tour.golang.org/basics/3.

Does the value of global variable persist in multiple API calls

A global variable is not the answer here. It will be overwritten by each request as you suspected. Instead, the typical way to handle this situation is to have a context object that is created within the scope of the HTTP Request and passed to each method that requires knowledge of that context.

Persisting a Variable across Requests

Store the value in ViewState. For example:

ViewState["RideId"] = ride.identity;

When you go and use it in the line in your code, you would need to do this:

id_ridePendingConfirm = (long)ViewState["RideId"];

But be careful. Since ViewState[key] returns an object, you will need to make sure it isn't a null reference, or else you'll receive an InvalidCastException.

I normally tell my my peers with less experience to create a protected property that will store this value and persist it in ViewState, as follows:

protected const string RideIdViewStateKey = "CurrentRideId";

protected long CurrentRideId
{
get
{
object o = ViewState[RideIdViewStateKey];
return (null == o)? -1 : (long)o;
}

set
{
ViewState[RideIdViewStateKey] = value;
}
}

Then in your code, do this:

// Assignment before postback so that you can preserve the state:
CurrentRideId = ride.identity;

// After postback in the method you have above:
id_ridePendingConfirm = CurrentRideId;

Now, since I don't know what your code should do when no Ride identity is available, I decided for -1, but it depends on what your code actually needs.

I do not recommend Session state for your scenario because this apparently needs to persist between page postbacks, not for the entire duration of the user's session. Also be careful with how much information you store in ViewState because it can easily be abused.

For more information on ViewState and Session State see:

  • Undestanding View State (2004)
  • ViewState Property (.NET 4.0)
  • ASP.NET State Management Recommendations
  • ASP.NET Session State Overview

How to use global var across files in a package?

Edit: The problem is that you used Short variable declaration := and you just stored the created *DB value in a local variable and not in the global one.

This line:

db, err := NewDB(dbinfo)

Creates 2 local variables: db and err, and this local db has nothing to do with your global db variable. Your global variable will remain nil. You have to assign the created *DB to the global variable. Do not use short variable declaration but simple assignment, e.g:

var err error
db, err = NewDB(dbinfo)
if err != nil {
log.Fatal(err)
}

Original answer follows.


It's a pointer type, you have to initialize it before you use it. The zero value for pointer types is nil.

You don't have to export it (that's what starting it with a capital letter does). Note that it doesn't matter that you have multiple files as long as they are part of the same package, they can access identifiers defined in one another.

A good solution would be to do it in the package init() function which is called automatically.

Note that sql.Open() may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call DB.Ping().

For example:

var db *sql.DB

func init() {
var err error
db, err = sql.Open("yourdrivername", "somesource")
if err != nil {
log.Fatal(err)
}
if err = db.Ping(); err != nil {
log.Fatal(err)
}
}

Persist variables between page loads

As HTTP is stateless, every time you load the page it will use the initial values of whatever you set in JavaScript. You can't set a global variable in JS and simply make that value stay after loading the page again.

There are a couple of ways you could store the value in another place so that you can initialize it on load using JavaScript


Query string

When submitting a form using the GET method, the url gets updated with a query string (?parameter=value&something=42). You can utilize this by setting an input field in the form to a certain value. This would be the simplest example:

<form method="GET">
<input type="hidden" name="clicked" value="true" />
<input type="submit" />
</form>

On initial load of the page, no query string is set. When you submit this form, the name and value combination of the input are passed in the query string as clicked=true. So when the page loads again with that query string you can check if the button was clicked.

To read this data, you can use the following script on page load:

function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

var clicked = getParameterByName('clicked');

(Source)

Ability to use this depends on how your form currently works, if you already use a POST then this could be problematic.

In addition, for larger sets of data this is less than optimal. Passing around a string isn't a big deal but for arrays and objects of data you should probably use Web Storage or cookies. While the details differ a bit across browsers, the practical limit to URI length is around 2000 characters


Web Storage

With the introduction of HTML5 we also got Web Storage, which allows you to save information in the browser across page loads. There is localStorage which can save data for a longer period (as long as the user doesn't manually clear it) and sessionStorage which saves data only during your current browsing session. The latter is useful for you here, because you don't want to keep "clicked" set to true when the user comes back later.

Here I set the storage on the button click event, but you could also bind it to form submit or anything else.

$('input[type="submit"][value="Search"]').click(function() {
sessionStorage.setItem('clicked', 'true');
});

Then when you load the page, you can check if it's set using this:

var clicked = sessionStorage.getItem('clicked');

Even though this value is only saved during this browsing session, it might be possible you want to reset it earlier. To do so, use:

sessionStorage.removeItem('clicked');

If you would want to save a JS object or array you should convert that to a string. According to the spec it should be possible to save other datatypes, but this isn't correctly implemented across browsers yet.

//set
localStorage.setItem('myObject', JSON.stringify(myObject));

//get
var myObject = JSON.parse(localStorage.getItem('myObject'));

Browser support is pretty great so you should be safe to use this unless you need to support really old/obscure browsers. Web Storage is the future.


Cookies

An alternative to Web Storage is saving the data in a cookie. Cookies are mainly made to read data server-side, but can be used for purely client-side data as well.

You already use jQuery, which makes setting cookies quite easy. Again, I use the click event here but could be used anywhere.

$('input[type="submit"][value="Search"]').click(function() {
$.cookie('clicked', 'true', {expires: 1}); // expires in 1 day
});

Then on page load you can read the cookie like this:

var clicked = $.cookie('clicked');

As cookies persist across sessions in your case you will need to unset them as soon as you've done whatever you need to do with it. You wouldn't want the user to come back a day later and still have clicked set to true.

if(clicked === "true") {
//doYourStuff();
$.cookie('clicked', null);
}

(a non-jQuery way to set/read cookies can be found right here)

I personally wouldn't use a cookie for something simple as remembering a clicked state, but if the query string isn't an option and you need to support really old browsers that don't support sessionStorage this will work. You should implement that with a check for sessionStorage first, and only if that fails use the cookie method.


window.name

Although this seems like a hack to me that probably originated from before localStorage/sessionStorage, you could store information in the window.name property:

window.name = "my value"

It can only store strings, so if you want to save an object you'll have to stringify it just like the above localStorage example:

window.name = JSON.stringify({ clicked: true });

The major difference is that this information is retained across not only page refreshes but also different domains. However, it is restricted to the current tab you're in.

This means you could save some information on your page and as long as the user stays in that tab, you could access that same information even if he browsed to another website and back. In general, I would advice against using this unless you need to actually store cross-domain information during a single browsing session.

How to access global variables

I would "inject" the starttime variable instead, otherwise you have a circular dependency between the packages.

main.go

var StartTime = time.Now()
func main() {
otherPackage.StartTime = StartTime
}

otherpackage.go

var StartTime time.Time

Global variable private to file

For the purpose of variable scoping, files have no meaning in Go. Think of all files in a package as if they would be concatenated, which (simplified) is exactly what happens before compilation.

That means: No, there is no way of scoping a variable to a file.

If you need two global WaitGroups, you need to define them as individual variables.

Accessing variables across packages in Go

Capitalized variable names are exported for access in other packages, so App and Cfg would work. However, using sub-packages for name-spacing is generally not recommended; packages are intended for discrete, self-contained functionality so it is usually more trouble than it's worth to use them this way (for example, import cycles are strictly impossible, so if you have two sub-packages in this layout that need to talk to each other then you're out of luck).

If you're finding you need to prefix things with user and topic in order to avoid name collisions, then perhaps the underlying concept should be factored into its own package, and you can create one instance of it for user and one for topic?



Related Topics



Leave a reply



Submit