Archive

Archive for the ‘Angular’ Category

Differences in browsers engines under AngularJS

Working to updates on a AngularJS site, i discovered subtle differences.
I don’t know if there were these problems before, but certainly they raised while testing with Angular 1.6.5
First, if the user was switching the site language from English to Italian while working , saving a date Moment was not able to parse the date giving “Invalid date” using the latest Chrome and Firefox, while with Ie11 and Edge all ok.
In origin the code was (oh yeah, it uses the DOM objects instead of the binding..)

var testDate = moment($("#txtBirthDate").val()).format($scope.DateFormat.toUpperCase());
if (testDate === "Invalid date") {
    logWarning(localize.localizeText("DateNotValid"), null, true);
    $("#txtBirthDate").focus();
    return false;
}

For the first thing , using Visual Studio 2017 and starting the project i noticed that finally the breakpoints in javascript code are working also under Chrome, great thing:

Obvious , $scope.DateFormat.toUpperCase() is evaluated at runtime as YYYY-MM-DD for English and DD/MM/YYYY for Italian.
In order to work also with Chrome and Firefox this is the correct Moment syntax:

var testDate = moment($("#txtBirthDate").val(), $scope.DateFormat.toUpperCase()).format($scope.DateFormat.toUpperCase());

This code instead works without changes in all browsers:

var datBirthDate = moment($("#txtBirthDate").val(), $scope.DateFormat.toUpperCase()).toDate();
var BirthDate = datBirthDate.yyyymmdd();  // as requested for SQL Server

Then the other issue, an awkward issue.
The customers have the bad habit to press F5 or worse the browser refresh button in the pages, that in Angular does little sense.
In the site the other controllers (the ones not relatives to the home page) are loaded with ocLazyLoad (current version v1.0.10), in config.route.js the code was

}, {
    url: '/phones',
    config: {
        title: 'Phones',
        role: 'Administrator;PowerUser;User',
        templateUrl: 'app/tabelle/phones.html', 
        resolve: requireDependencies(['app/services/datacontextphones.js'], ['app/tabelle/phones.js']]),
        settings: {
            content: '<i class="fa fa-mobile"></i> {placeholder}',
            nav: 3,
            localizeKey: 'mnuPhones',
            minicontent: '<i class="fa fa-mobile" title="{placeholder}"></i>'
        }
    }
}, {

The function:

function requireDependencies(depContext, depMain) {
    return {
        resolvedData: ['$q', '$rootScope', '$ocLazyLoad', function ($q, $rootScope, $ocLazyLoad) {
            $ocLazyLoad.load(depContext);
            $ocLazyLoad.load(depMain);
        }]
    };
}

and this code with Ie11 and Edge was working fine, the users could hit F5, the browser refresh button, and the page is refreshed without problems.
Instead with Chrome and Firefox:

After some struggle, the working code in config.route.is that doesn’t use the function requireDependencies and all pages are surviving to the refresh in all browsers:

}, {
    url: '/phones',
    config: {
        title: 'Phones',
        role: 'Administrator;PowerUser;User',
        templateUrl: 'app/tabelle/phones.html', 
        resolve: {
            deps: ['$ocLazyLoad', function ($ocLazyLoad) {
                return $ocLazyLoad.load({
                    name: "phones",
                    files: [
                        'app/services/datacontextphones.js',
                        'app/tabelle/phones.js'
                    ]
                });
            }]
        },
        settings: {
            content: '<i class="fa fa-mobile"></i> {placeholder}',
            nav: 3,
            localizeKey: 'mnuPhones',
            minicontent: '<i class="fa fa-mobile" title="{placeholder}"></i>'
        }
    }
}, {

Advertisements
Categories: .NET, Angular, Javascript, Vs2017

Firefox needs UTF8 for GeoXml3


I’m developing an WebAPI2 solution with Angular that receives, from Android devices, GPS points.
So Latitude , Longitude , Speed etc. are saved in a SQL Server 2014 table; from the web site the headquarter would follow the real time navigation.
The chosen approach was to write an KML file from the db data, and show this KML in a GoogleMaps window.
Having local files (not on another domain) the best solution is to use the GeoXml3 library, which is relatively easy to use (examples here and here).
For the javascript part, no problem: there is an simple setTimeout that calls via jQuery an WebAPI2 method that re-write the kml (the name is the record ID), and then adding an random number to the querystring the boss can follow the persons wandering somewhere, seeing the kml line that slowly progress on the map.
But there was an problem: On IE10 , 11, Chrome, Edge all ok; instead on Firefox (Windows or Ubuntu or Android tablet) no kml shown.
Sometimes the things are discovered in a very casual manner…i tried to save the KML with another name using Notepad++ , and change the code in order to not use the API call and instead try to show immediately this second file where i wanted to test some change, in order to understand what was wrong : bingo, KML immediately visible!
This was a real head scratching…the files (the generated from the system, and the one saved from the previous) in Notepad++ was absolutely the same, no visible difference.
So i tried to compare them with WinMerge and surprise, the generated file was full of strange characters before every normal character, immediately i realized “oops, the encoding?” and only after i noticed in Notepad++ that in the Encoding menu the file saved from Notepad++ itself was UTF8 encoded, and the one produced from the C# code was without encoding.
In the WebAPI2 method effectively i used Unicode: so now, all the browsers shows the KML writing the file as:

private async Task WriteTextAsync(string filePath, string text)
{
    //byte[] encodedText = Encoding.Unicode.GetBytes(text);
    byte[] encodedText = Encoding.UTF8.GetBytes(text);
    //
    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 1048576, useAsync: true))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    };
}

Angular modal problem after update

I was already prepared to the head banging.
I had a .NET 4.5 project with Angular 1.3.15, reopening for some update i tried an NuGet update and voilà, Angular 1.4.1 available: but no updates for Bootstrap, still 3.3.4 … troubles to the horizon…
The site is essentially an employees management, with add update delete done using modal windows.
After the update apparently all still working until i tried an update (of a record).
The original code was

modalInstance = $modal.open({
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

With this code the modal window was appearing, inside the window no problem for fields editing, but after closing the window by the ESC on the keyboard or the Save button, on the browser canvas a grey background was covering everything: only after an F5 the site was working.
The problem seems related to the BootStrap version not aligned to Angular for the “modal” part; after some struggle i found that the problem is relative to the animation feature.
By adding the clause “animation: false, ” :

modalInstance = $modal.open({
    animation: false,
    templateUrl: 'app/partials/formemployees.html',
    controller: 'modalController',
    backdrop: "static",
    keyboard: true,
    modalFade: true,
    scope: $scope
})

Now the modal is working correctly.

Categories: .NET, Angular, SPA, Vs2013

WebAPI2 errors management in Android

In my last project is used an SQL Server 2014 Phones table, with an Android app that register the device itself in this table via an WebAPI2 call.

The table is simple:


We have an primarykey named “PK_Phones” on PhoneId.

The PhoneID is the IMEI code, if the device has phone capabilities and is installed an working SIM, or the Android ID.

There are other common fields for multiuser management as the timestamp which is very useful with the Entity Framework 6 used in this solution.

The Web counterpart (created with Visual Studio 2013) is a common MVC5 project using WebAPI2 with Breeze, Angular , Bootstrap, Modernizr, Moment, Ninject, Toastr…an huge blob installed from NuGet using the HotTowel package (Ninject , Jasmine for testing and other package needs to be installed by hand).

The first move was to create an ADO.NET Entity Data Model (here i don’t speak in detail of the project organization) and using “Code first from Database” i created the class representing my table:

namespace net.studioalessi.walkad.dl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    public partial class Phones
    {
        [Key]
        [StringLength(20)]
        public string PhoneId { get; set; }

        [StringLength(30)]
        public string Manufacturer { get; set; }

        [StringLength(20)]
        public string Model { get; set; }

        [StringLength(150)]
        public string Notes { get; set; }

        [StringLength(20)]
        public string PhoneNumber { get; set; }

        public bool Banned { get; set; }

        [StringLength(50)]
        public string UtenteModifica { get; set; }

        public DateTime? OraModifica { get; set; }

        [StringLength(50)]
        public string UtenteInserimento { get; set; }

        public DateTime? OraInserimento { get; set; }

        [Column(TypeName = "timestamp")]
        [MaxLength(8)]
        [Timestamp]
        public byte[] SSMATimeStamp { get; set; }
    }
}

Then the necessary Controller (right click on Controllers folder->add new scaffolded item->


Web API 2 Controller with actions, using Entity Framework as in figure.

Using the previous Phones class we generate the Controller and the Context, in our case the context is the class PhonesContext:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace net.studioalessi.walkad.dl
{
    public class PhonesContext : DbContext
    {
        public PhonesContext()
            : base("name=WalkadConn")
        {
        }

        // no virtual ? in the previous EF version was generated as virtual...
        public DbSet<Phones> Phones { get; set; }

        // Also the code below was generated automatically in the previous version.
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteModifica)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.UtenteInserimento)
                .IsUnicode(false);

            modelBuilder.Entity<Phones>()
                .Property(e => e.SSMATimeStamp)
                .IsFixedLength();
        }
    }
}

And the Controller is changed from the generated version to an BreezeController

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Walkad.Models;
using net.studioalessi.walkad.dl;
using Breeze.WebApi2;

namespace Walkad.Controllers
{
    [BreezeController]
    public class PhonesController : ApiController
    {
        private IPhoneRepository _repo;

        public PhonesController(IPhoneRepository repo)
        {
            _repo = repo;
        }

        [HttpGet]
        public string Metadata()
        {
            return _repo.MetaData;
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<IHttpActionResult> RegisterImei(PhoneModel model)
        {
            PhonesContext objT = new PhonesContext();
            try
            {
                Phones objTrace = new Phones
                {
                    UtenteInserimento = "RemoteUser",
                    OraInserimento = DateTime.Now,
                    Model = model.Model,
                    Manufacturer = model.Manufacturer,
                    PhoneId = model.Imei
                };
                //
                objT.Phones.Add(objTrace);
                await objT.SaveChangesAsync();
                return Ok();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.ToString());
            }
            finally
            {
                //
            }
        }
    }
}

The PhoneModel was created by hand and is the model representing the JSON string sent from the client (Web site or Android app, as in this case)

public class PhoneModel
{
    [Required]
    [StringLength(15)]
    public string Imei { get; set; }

    [StringLength(30)]
    public string Manufacturer { get; set; }

    [StringLength(20)]
    public string Model { get; set; }
}

At this point we can try the service from Fiddler


By clicking “Execute” we can see that our call was successful


And in SQL Management Studio we can see the new record:


Ok , we can create the Android app with Eclipse Luna (with the Android Developer Tools).

I don’t show the details, i created an simple Android app with an menu item that calls the routine named registerThisDevice:

private void registerThisDevice() {
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    String strBasePath = sharedPrefs.getString((String)getText(R.string.pref_httpbase), "http://192.168.0.77/Walkad");
    new WebApiRegisterImei().execute(strBasePath + "/api/Phones/RegisterImei");
}

Note the i’m using the IP , the tablet used for the test is in the same WiFi network but is not Windows, is not able to resolve the NETBIOS name.

Note also that we web address is registered in a Preference that defaults to my WiFi address.

In order to follow the best practices , the routine WebApiRegisterImei is an AsyncTask so is called with .execute() passing the server address as String argument (in every case , if we don’t use an AsyncTask we get an NetworkOnMainThread exception):

private class WebApiRegisterImei extends AsyncTask<String, Void, JSONObject> {
    ProgressDialog myProgressBar;
    public String ExportResult = "";
    public String LastException = "";
    public Exception LastExceptionObject;       

    protected JSONObject doInBackground(String... params) {
        BufferedInputStream  in = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(params[0]);
            urlConnection = (HttpURLConnection) url.openConnection();
            // the Android docs says that HttpURLConnection uses the GET method by default. It will use
            // POST if setDoOutput(true) has been called. Other HTTP methods
            // (OPTIONS, HEAD, PUT, DELETE and TRACE) can be used with
            // setRequestMethod(String). Anyway forcing POST as request method is useful.
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            //
            JSONStringer vm;
            vm = new JSONStringer()
                    .object().key("Imei").value(MainActivity.ImeiId)
                             .key("Manufacturer").value(android.os.Build.MANUFACTURER)
                             .key("Model").value(android.os.Build.MODEL)
                    .endObject();

            urlConnection.setFixedLengthStreamingMode(vm.toString().getBytes().length);
            urlConnection.setRequestProperty("Content-Type", "application/json");
            urlConnection.setRequestProperty("charset", "utf-8");
            DataOutputStream wr = new DataOutputStream (
                    urlConnection.getOutputStream ());
                    wr.writeBytes (vm.toString());
                    wr.flush ();
                    wr.close ();                           
            //
            int statusCode = urlConnection.getResponseCode();
            if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
                // handle unauthorized (if service requires user login)
            } else if (statusCode != HttpURLConnection.HTTP_OK) {
                InputStream errorstream = urlConnection.getErrorStream();
                BufferedReader br = null;
                if (errorstream == null){
                    InputStream inputstream = urlConnection.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputstream));
                }else{
                    br = new BufferedReader(new InputStreamReader(errorstream));
                }
                String strResponse = "";
                String strTemp;
                while ((strTemp = br.readLine()) != null){
                    strResponse += strTemp;
                }   
                strResponse = strResponse.toLowerCase();
                if(strResponse.contains("pk_phones"))
                {
                    ExportResult = getErrorDuplicated();
                    return null;
                }
            }
            //
            ExportResult = getExportCompleted();
        } catch (Exception e) {
            Mint.logException(e);
            LastException = e.toString();
            LastExceptionObject = e;                
        } finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    Mint.logException(e);
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();  
            }
        }
        return null;
    }

    private String getErrorDuplicated() {
        return (String)getText(R.string.imeiduplicatederr);
    }

    private String getExportCompleted(){
        return (String)getText(R.string.registerimeidone);
    }           

    @Override
    protected void onPreExecute() {
        try {
            myProgressBar = new ProgressDialog(MainActivity.thisMainActivity, ProgressDialog.STYLE_SPINNER);    
            myProgressBar.setMessage((String)getText(R.string.registerimeiwait));
            myProgressBar.show();
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }

    @Override
    protected void onPostExecute(JSONObject result) {
        try {
            myProgressBar.dismiss();
            if(LastException.length() > 0){
                Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();   
                Mint.logException(LastExceptionObject);
            }else{
                Toast.makeText(MainActivity.thisMainActivity, ExportResult, Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Mint.logException(e);
            Toast.makeText(getBaseContext(), (String)getText(R.string.genericerror),Toast.LENGTH_LONG).show();                  
        }
    }
}

Examining the code , we can note that onPreExecute and onPostExecute are the only places where we can interact with the main thread and relative controls (the progress bar) so in the background part we register the exceptions details in one string and one object, examining them on the onPostExecute.

Note the use of the JSONStringer: debugging the code you can see that is created the JSON string as used in Fiddler.

If all is ok, we can see that in SQL Server is written a new row in the Phones table.

The test for statusCode != HttpURLConnection.HTTP_OK was the difficult part to discover how it works, the subject of this post.

If you reissue the command from the Android app on the same device you violate the primary key, so the WebAPI code goes in the Exception and is sent to the client “BadRequest” (HTTP error 400) instead of “Ok”(200), sending as other info ex.ToString().

The management of the returned error is done as you can read in the code: we need to examine the returned stream , by reading this stream we can create a string that contains the original ex.ToString, for example

{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"System.Data.Entity.Infrastructure.DbUpdateException: 
An error occurred while updating the entries. See the inner exception for details. ---> 
System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. 
See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_Phones'. 
Cannot insert duplicate key in object 'dbo.Phones'. The duplicate key value is…..

So in the Android code we search if the string contains the name of the primary key , giving to the user an scoped message, or we give to the user an generic message and the code uses Splunk Mint Express for registering all infos about the unmanaged exception (that could be an network failure, and so on).

Breeze SaveChanges issue with Ie10

Every new technology is an hard path , an high mountain to climb.

I’m working to an MVC project with ASP.NET 4.5 , Breeze and Angular, authentication done with the new ASP.NET Identity, Entity Framework 6.

The big problem , for the average developer (me) , is:

  • For Microsoft, on MSDN you can find a lot of verbose pages about the syntax: an simple CRUD example using these technologies , add update delete of records, is hard to find.
  • For thirdy parties technologies you can find abstruse , overcomplexed samples ; but still there is a lack of a simple table management with CRUD operations.

In every case the samples simply ignore (at the least the ones that i discover with Google) the everyday questions with concurrency : another user could have deleted the record that you are trying to save, or you are trying to save a record of a table where there is an unique index for a field.

Ok , this was the rant, now the problem.

The code is about the update of a record in a Vehicle Classes table (ClassiMezzi).

Using Breeze, in a BreezeController (a class decorated with “[BreezeController]”) the typical sample for an update is

[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
    return _repo.SaveChanges(saveBundle);
}

This works with Ie11, the latest Firefox, Chrome, Safari; _repo is an repository object that at the end calls the Entity Framework context provider (in our sample EFContextProvider<ClassiMezziContext>)

But if you try to use this method for adding a new record on a table where there is an index UNIQUE, there are troubles with the error messages using Internet Explorer 10.

For these new apps i simply don’t consider Ie < 10; if you are still using Ie6 , Ie7.. time to upgrade …but to ignore the previous version is a bit excessive, for me: i test everything with the latest Firefox, Chrome also under Linux Android and Mac, for Ie to test the latest version and the previous seems reasonable.

And so, testing the code with multiuser issues (record deleted from another user, check constraints, UNIQUE indexes) i discovered the problem.

From the javascript side , the controller is called with an Ajax call with callbacks for success or failure:

function saveFailed(error) {
    try {
        manager.detachEntity(service.ClasseMezzoEntity);
    }
    catch (ex) { }
    service.ClasseMezzoEntity = null;
    if (error.httpResponse.status === 401 || error.httpResponse.status === 403) {
        logError(localize.localizeText("UnauthorizedOperation"), null, true);
        return;
    }
    //var strMsg = breeze.saveErrorMessageService.getErrorMessage(error).toLowerCase(); old version ok with Ie11 , not Ie10
    var strMsg = error.httpResponse.data.Errors[0].toLowerCase();
    if (strMsg.indexOf("entities may have been modified or deleted since entities were loaded") > 0) {
        logWarning(localize.localizeText("AnotherUsrHasChangeData"), null, true);
        return;
    }
    if (strMsg.indexOf("ix_classimezzi") > 0) {
        logWarning(localize.localizeText("ClassNameDuplicated"), null, true);
        return;
    }
    if (strMsg.indexOf("errdelete:anotheruser") > 0) {
        logWarning(localize.localizeText("AnotherUsrHasDeleteData"), null, true);
        return;
    }
    //  other code for unknown error management
    var msg = breeze.saveErrorMessageService.getErrorMessage(error);
    // etc.
};
 
function saveClasseMezzo() {
    service.isSaving = true;// see at the end of this module
    service.lastSaveSuccess = true;
    return manager.saveChanges()
        .catch(saveFailed)
        .then(saveClasseMezzoAttemptDone);
};

For example we try to add another ClasseMezzo (Vehicle class) with the same name of an existing item on the the db table.

With Ie11 and other latest browsers the first controller code is ok , and if our ajax call fails because the UNIQUE index the first

var strMsg = breeze…. commented code works perfectly.

But while with Ie11 etc. the error.message in javascript contains the string “Cannot insert duplicate key row …” with the index name that helps to manage the error (in this sample the index name is “ix_classimezzi”) in Ie10 we get


That is an anonymous “An error has occurred” that can’t help to manage the error, and no trace of the original SQL error in httpResponse: a big trouble if i would to say to the customer “you are trying to insert an duplicate item” because could be another thing , for example an SQL check violation.

The problem is the simple approach in the WebApi2 controller.

In order to manage correctly the errors coming from multiuser issues the WebAPI2 controller code must be:

[HttpPost]
[Authorize(Roles = "PowerUser")]
public SaveResult SaveChanges(JObject saveBundle)
{
    SaveResult objRes = null;
    List<object> objErr = null;
    var classimezziList = JsonConvert.DeserializeObject<List<ClassiMezzi>>(saveBundle.SelectToken("entities").ToString());
    try
    {
        objRes = _repo.SaveChanges(saveBundle);
        return objRes;
    }
    catch (Exception ex)
    {
        var keyMappings = new List<KeyMapping>();
        objErr = new List<object> { JsonConvert.SerializeObject(ex.Message) };
        return new SaveResult()
        {
            Entities = classimezziList.Cast<object>().ToList(),
            Errors = objErr,
            KeyMappings = keyMappings
        };
    }
}

Let’s examine the code.

We pass to the controller an JSON object , that we want deserialized in order to correctly manage the error; note in the catch management code: is explicitly created an SaveResult object , instead of the automatic creation.

Setting the Errors field to something != null , in our case the error message serialization, we cause the call of the javascript failure callback , and in the javascript failure code the message must be read as

var strMsg = error.httpResponse.data.Errors[0].toLowerCase();

because when the code is falling back we have this , now:


So we can manage the things as in the next rows.

This is valid also for Ie11 , and Firefox, Chrome under Windows, Linux, Mac, Android.

At least, this is the situation with the current latest Breeze (1.3.8)