Remove with AJAX

Now that we can add albums to the cart, and see the total number both in cart overview and navigation menu, it would be nice to be able to remove albums from the cart as well.
This is a great occasion to employ AJAX in our application.
We'll write a simple script in JS that makes use of jQuery to remove selected album from the cart and update the cart view.

Download jQuery from here (I used the compressed / minified version) and add it to the project.
Don't forget to set the "Copy to Output Directory" property.

Now add new JS file to the project script.js, and fill in its contents:

The update-message div should be added to the nonEmptyCart view, just before the table.

We explicitly have to pass in non-empty text, because we cannot have an empty div element in HTML markup.
With jQuery and our script.js files, we can now attach them to the end of nonEmptyCart view, just after the table.

We also need to allow browsing for files with "js" extension in our handler:

App.fs

269:

pathRegex"(.*)\.(css|png|gif|js)">=>Files.browseHome

The script tries to reach route that is not mapped to any handler yet.
Let's change that by first adding removeFromCart to Db module:

this handler should not be invoked with NoSession, never prevents from unwanted requests

the same happens, when someone tries to invoke removeFromCart for albumId not present in his cart (Db.getCart returns None)

if proper cart has been found, Db.removeFromCart is invoked, and

an inline portion of HTML is returned. Note that we don't go through our html helper function here like before, but instead return just the a part that script.js will inject into the "main" div on our page with AJAX.

This almost concludes the cart feature.
One more thing before we finish this section:
What should happen if user first adds some albums to his cart and later decides to log on?

<summary>Typed representation of a database</summary>
<param name='ConnectionString'>The connection string for the SQL database</param>
<param name='ConnectionStringName'>The connection string name to select from a configuration file</param>
<param name='DatabaseVendor'> The target database vendor</param>
<param name='IndividualsAmount'>The amount of sample entities to project into the type system for each SQL entity type. Default 1000.</param>
<param name='UseOptionTypes'>If true, F# option types will be used in place of nullable database columns. If false, you will always receive the default value of the column's type even if it is null in the database.</param>
<param name='ResolutionPath'>The location to look for dynamically loaded assemblies containing database vendor specific connections and custom types.</param>
<param name='Owner'>The owner of the schema for this provider to resolve (Oracle Only)</param>
<param name='CaseSensitivityChange'>Should we do ToUpper or ToLower when generating table names?</param>
<param name='TableNames'>Comma separated table names list to limit a number of tables in big instances. The names can have '%' sign to handle it as in the 'LIKE' query (Oracle and MSSQL Only)</param>
<param name='OdbcQuote'>Odbc quote characters: Quote characters for the table and column names: `alias`, [alias]</param>
<param name='SQLiteLibrary'>Use System.Data.SQLite or Mono.Data.SQLite or select automatically (SQLite only)</param>

<summary>Returns an instance of the SQL Provider using the static parameters</summary>SqlDataProvider<...>.GetDataContext(transactionOptions: Transactions.TransactionOptions) : SqlDataProvider<...>.dataContext

<summary>Returns an instance of the SQL Provider</summary>
<param name='transactionOptions'>TransactionOptions for the transaction created on SubmitChanges.</param>SqlDataProvider<...>.GetDataContext(connectionString: string) : SqlDataProvider<...>.dataContext