Knockout-Contrib / Knockout-Validation

A validation library for Knockout JS
1.02k stars 379 forks source link

Live localization #158

Open yohanb opened 11 years ago

yohanb commented 11 years ago

Hi Eric,

Thanks for your great contribution.

Here's my issue: I need to localize the validation message (i.e.: Field is required) when clicking a "localize" button. My application needs to be able to switch languages (fr-en) in a "live" fashion.

How would you proceed to accomplish this using your plugin?

Thanks!

Yohan

IFYates commented 11 years ago

Hi Yohan,

I've had a few stabs at trying to get what you're asking for working without changing the library itself. Eventually, the simplest solution I found was simply to cause a revalidation to take place: http://jsfiddle.net/IanYates83/CVMRA/3/

Ideally, applying localisation would happen live and only update the messages currently shown, but maybe the above will be enough for your needs. I think anything more would require a code change.

Hope that helps

yohanb commented 11 years ago

Hi Ian,

Thanks for your response. Your jsfiddle doesn't seem to work as I would have expected. When the Toggle method is called, the validation message is changed but the observable isn't re-validated so the text doesn't change. You have to change the text inputs and re-validate the controls to get the new message.

IFYates commented 11 years ago

Interesting. I only have IE8 at work (don't get me started), and the button causes the displayed messages to update immediately... I'll take another look at home where I have some sensible browsers.

Kikketer commented 10 years ago

Sorry about reviving a zombie, but I'm having the same type of issue described above. @IanYates83 I hope you made it home ;)

I'm running ko.validation.localize() to switch the output text when I click the localize button. But my text is never changing either live or on revalidate. I noticed in the validator:

    // create a handler to correctly return an error message
            var errorMsgAccessor = function () {
                if (!config.messagesOnModified || isModified) {
                    return isValid ? null : obsv.error;
                } else {
                    return null;
                }
            };

The second time I run the localize function, the "obsv.error" always returns the first one.

crissdev commented 9 years ago

@Kikketer You need to revalidate the observable. You might achieve this with observable.valueHasMutated - after applying localization.

crissdev commented 9 years ago

This might land in some future version if more people will ask for it. Add your :+1: in the comments if you find this feature needed. We still have to see how it may fit in the current code base though.

ghost commented 9 years ago

:+1:

RCMax commented 9 years ago

We need this for the koco project & koco-i18next module. We might try to fork + pull request if we find the time to do it. :+1:

colincbc commented 9 years ago

:+1:

one-geek commented 8 years ago

Hello Guys. First, i recently start using knockoutjs and i really love it. Right now, i'm facing a big problem and its very irritating because so far everything was good. I think i'm facing the same problem as this ticket. Actually i'm using Globalizejs, a javaScript library for internationalization and localization to translate all JS variable in my application without refreshing the page. The message is assign to an observer and should change, but it doesn't. Please help.

Problem Context:

I think the ko.extenders.required messages are stick to the first assigned text on page load. It doesn't support live validation. Here a example of the code i'm using :

<span data-bind="text: Resource.helloText">Hello default value</span>  // Works
<input type="radio" value="1" data-bind="checked: Person.GenderId, checkedValue: 1" />
<input type="radio" value="2" data-bind="checked: Person.GenderId, checkedValue: 2" />
<span class="text-danger" data-bind="validationMessage: Person.GenderId">Please this field is required</span> // Doesn't switch the text
function ViewModel() {

    self.Person = {
        genderId : ko.observable(false)
    }

    [...]

    /*   // Resource.json English file
    *     'helloText' : 'Hello',
    *     'pageTitle' : 'My Application', 
    *     'genderRequired': 'Please this field is required'

    *    // Resource.json french file
    *     'helloText' : 'Bonjour',
    *     'pageTitle' : 'Mon application ',
    *     'genderRequired': 'Svp ce champ est requis'
    */

    $.getJSON("server/ressource." + Globalize.culture().name.toString() + ".json", function (data) {
            ko.mapping.fromJS(data, {}, self.Resource);
     });

    // Language switcher
    self.SwitchLanguage = function () {
        var activeLang = 'en-CA';

        if (self.LanguageAppId() == 1) {
            self.LanguageAppId(2);
        }
        else {
            self.LanguageAppId(1);
            activeLang = 'fr-CA';
        }

        Globalize.culture(activeLang);

        $.getJSON("server/ressource." + Globalize.culture().name.toString() + ".json", function (data) {
            ko.mapping.fromJS(data, {}, self.Resource);
        });

    }

    [...]

    self.Person.GenderId.extend({ required: { message: self.Resource.genderRequired() } }); 

}

Source : https://github.com/jquery/globalize

IanYates commented 8 years ago

I'm a lurker on here as I use KO extensively but don't yet use the validation plugin. However, from what I can see in the code given by @guerson, if the message parameter was updated such that it could take an observable as well as a static string then you'd be gold.

That is, the final line could be

self.Person.GenderId.extend({ required: { message: self.Resource.genderRequired } }); 

Note that the genderRequired observable is not being evaluated.

Knowing very little about the code of this library it doesn't seem like it'd be too big an imposition or change to have the message renderer to use ko.unwrap to grab the message. Actually, for all I know it already does that - give it a try @guerson

one-geek commented 8 years ago

Each time the observable is not being evaluated, i have this traceback.

Uncaught TypeError: message.replace is not a function
with a traceback of :
ko.validation.formatMessage @ knockout.validation.debug.js:279validateSync @
knockout.validation.debug.js:877ko.validation.validateObservable @
knockout.validation.debug.js:947(anonymous function) @
knockout.validation.debug.js:831computedFn.evaluateImmediate_CallReadThenEndDependencyDetection @ knockout
3.4.0.debug.js:2142computedFn.evaluateImmediate_CallReadWithDependencyDetection @ knockout
3.4.0.debug.js:2114computedFn.evaluateImmediate @ knockout
3.4.0.debug.js:2078computedFn.evaluatePossiblyAsync @ knockout
3.4.0.debug.js:2044ko_subscribable_fn.notifySubscribers @ knockout-
[...]
one-geek commented 8 years ago

My guess is that being not evaluated, the code that's calling ko.validation.formatMessage function which is not anymore a string but something not evaluated.

one-geek commented 8 years ago

Is there a way to remove the validation and re-apply it without refreshing the page?

crissdev commented 8 years ago

@guerson You can remove validation of an observable using the validatable extender:

// Set up validation
var obs = ko.observable().extend({required: true, email: true});

// Remove validation
obs.extend({validatable: false});
one-geek commented 8 years ago

Thank you for your quick answer.

Finally, since all extenders messages are evaluated immediately, I'll change them through a function later and notify all subscribers directly. Only problem is (valueHasMutated/notifySubscribers()) will trigger an form validation.

self.myExtend = self.Person.GenderId.extend({
    required: { message: self.Resource.genderRequired()},
    customExtender: { message: self.Resource.extenderRequired()} 
});

[...]

var obsv = self.myExtend;
obsv.rules()[0].message = self.Resource['genderRequired'](); // required
obsv.rules()[1].message = self.Resource['extenderRequired'](); // customExtender

if (obsv.name == 'observable' && typeof obsv.valueHasMutated === "function") {
    obsv.valueHasMutated();
} else if (observ.name == 'computedObservable' && typeof observ.notifySubscribers === "function") {
    obsv.notifySubscribers();
}

It seems that is the only way for me right now to support my Single-page application.

slaneyrw commented 8 years ago

We have override the validation template and use a custom binding that uses the validation "message" as a key to look up a localised version of the display message. The localisation is tied to the current culture via a computed and will update all strings on a page without having to re-evaluate the validation conditions if the language is updated. The use the jQuery Globalize library based on Unicode/CLDR, to load and query

omnoms commented 8 years ago

I've tried numerous ways of doing what you say you are @slaneyrw . Whatever I try to manipulate the element with, it's overriden by the validation plugin and unsuccessful in doing so.

Even this simple thing; and if I were to make that span's text-data-binding a ko.computed it won't ever be overwritten. It's always the validation message from the plugin.

I can't even get it to update the message with the proper localization bundles. if I have loaded english and swedish, and try to switch with ko.validation.locale("en") and ("se"), having populated the localization with "en" and "se" before attempting to do so and the javascript files for the localization are loaded, it won't update the DOM when doing ko.validation.locale() . If I check the global scope, it has changed for instance ko.validation.rules.required to the appropriate locale, but the DOM is not updated.

github-clearthought commented 6 years ago

KnockoutValidationFix.zip

For what it's worth, I fixed this. See the above link. Unzip and load page.html in a browser for the demo.

I modified knockout.validation.js and saved as knockout.validation.modified.js. My changes are marked "// Dan". I changed the error message object to a knockout observable. I also added some hooks in knockout.validation.fix.js to handle updating error messages when the bounded language changes. Finally local.js contains a small localization API and the localized strings are stored in localModel.js.