1
votes

I am looking into using the Model Validations with Breeze and I see that it does work when adding for example [Required] but the error message seems to be coming from Breeze itself, I was expecting it to pull the message directly from the Model but it's not the case. I have to build my next project with resources translation (English, French, Spanish) and I would rather have the Breeze Controller to somehow get the error message directly. As an example, in my previous project I had a model with this:

[Display(ResourceType = typeof(Resources.Validations), Name = "SiteName")]
[Required(ErrorMessageResourceType = typeof(Resources.Validations), ErrorMessageResourceName = "Required")]
public string siteName { get; set; }

which the 1st annotation Display will change the name of my input, I'll deal with that one in Angular so we can skip that one, but I would like to have the 2nd annotation Required(ErrorMessageResourceType = ...)], I would rather have Breeze to take it by itself... Having more explanation, in my resource file, I made my translation Required translation in English as this The {0} field is required. and the actual error message that I see inside Breeze is '%displayName%' is required. and so right away I can see it is not pulling anything from the Model neither the resource file and is rather a built-in Breeze error message. I found some information from this Breeze Server-side validation page, it does talk about custom validator but I thought this should actually be built-in, wouldn't it? Would there be a way to have them populated automatically? Oh and to provide some information about my own project, I use Breeze with the WebApi Controller in ASP MVC5 and also use EF6 with Breeze.ContextProvider.EF6. Does Breeze take all the Models data annotation possible from Models or does it implemented a limited set? From this breeze page Add a Breeze validator, I see a couple of them but I'm not sure if it's just client side.

I also found this question/answer Translate breeze validation messages, but that seems to be client side, again I would rather have Breeze to automatically get my translation from the resource provided in my Model.

If you provide an answer, could you please include code sample, I'm always more visual... Thanks :)

1

1 Answers

1
votes

So I did not have any answers, I'll reply with what I found so far... The answer for Breeze to support resource translations is at this point not yet supported though I've submit a suggestion in Breeze user voice here

To do translation, I had to first translate Breeze Validators when starting breeze in my EMFactory and the way I found was this:

function emFactory($cookies, breeze, fileService) {
    var lang = $cookies.lang || "en";

    // load the validator templates translation mapping (external files: validators.{lang}.json)
    var translations = loadValidatorsTranslation();
    breeze.Validator.messageTemplates = translations[lang];

    // Identify the endpoint for the remote data service
    var serviceRoot = window.location.protocol + '//' + window.location.host + '/';
    var serviceName = serviceRoot + 'breeze/BreezeApi';

    var factory = {
        newManager: function () { return new breeze.EntityManager(serviceName); },
        serviceName: serviceName,
        language: lang
    };

    return factory;
}

function loadValidatorsTranslation() {
    return {
        en: {
            // ...
            required: "'%displayName%' is required",
            string: "'%displayName%' must be a string",
            stringLength: "'%displayName%' must be a string with between %minLength% and %maxLength% characters",
            url: "The %displayName% '%value%' is not a valid url"
        },
        fr: {
            // ...
            required: "'%displayName%' est requis",
            string: "'%displayName%' doit être une chaîne de caractère",
            stringLength: "'%displayName%' doit être une chaîne de caractère entre %minLength% et %maxLength% caractères",
            url: "%displayName% '%value%' n'est pas un URL valide"
        }
    };
}

then I created a TranslationService to deal with the displayNames entities of my Breeze context:

appDemo.factory('translationService', ['$q', '$timeout', translationService]);

function translationService($q, $timeout) {
    // declare the displayNames translations of entities
    var displayMapping = {
        fr: {
            City: {
                Name: "Nom de Ville"
            },
            Speaker: {
                Bio: "Bio",
                Image: "Image",
                Name: "Nom du Conférencier"
            }
        },
        en: {
            City: {
                Name: "City Name"
            },
            Speaker: {
                Bio: "Bio",
                Image: "Image",
                Name: "Speaker Name"
            }
        }
    };

    // reveal the public functions & return the service 
    return {
        loadTranslationDisplayNames: loadTranslationDisplayNames
    };


    // -- public functions 
    // --------------------
    function loadTranslationDisplayNames(manager, lang, entityTypes) {        
        for (var i = 0, ln = entityTypes.length; i < ln; i++) {
            // get the specific context Entity
            var custType = manager.metadataStore.getEntityType(entityTypes[i]);
            var entityProperties = displayMapping[lang][entityTypes[i]];
            // loop through all properties of this Entity and update their DisplayName
            for (var name in entityProperties) {
                custType.getProperty(name).displayName = entityProperties[name];
            }
        }
    }
}

and finally in my DataService, I am calling my TranslationService with this

function dataService($rootScope, $q, breeze, entityManagerFactory, translationService) {
    var service = this;

    // reveal the public functions we want, any other functions will remain private
    service.getSpeakers = getSpeakers;

    var manager = entityManagerFactory.newManager();
    var lang = entityManagerFactory.language;

    return service;

    // -- public/private functions declaration
    function getSpeakers() {
        var query = new breeze.EntityQuery.from("Speakers");

        // load the translation of Breeze DisplayNames entities
        translationService.loadTranslationDisplayNames(manager, lang, ["City", "Speaker"]);

        startProcessingData();

        var promise =
            manager.executeQuery(query)
                   .catch(queryFailed)
                   .finally(processingDataComplete);

        return promise;
    }
}

... So this work and I simply update my TranslationService with my new translations whenever I want. If anyone else has a better/cleaner solution, I would be happy to see it...