Knockout-Contrib / Knockout-Validation

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

Validation with custom extension seems not showing error message on screen #23

Closed tpever closed 12 years ago

tpever commented 12 years ago

Hi,

I was doing some evaluation about knockoutjs and validation, Thanks for your plugin it is really helpful.

One use case seems not working in my case - i have used autonumeric extension from (https://gist.github.com/1341303) When i apply validation rules to autonumeric field in ko.validation.group(model) it gives error message properly. but somehow it is not showing on screen.

can you please help me out for this.

Thanks and regards

ericmbarnard commented 12 years ago

@tpever

Could you put together the code you have in a jsFiddle or gist so I could see what syntax you are using?

tpever commented 12 years ago

Hi,

Please have a look at the snapshot of script given below.


<html>
<head>
<title> KO Test </title>
</head>
<body>
<script type="text/javascript" src="jquery-1.6.js"></script>
<script type="text/javascript" src="knockout-latest.debug.js"> </script>
<script type="text/javascript" src="knockout.validation.js"> </script>
<script type="text/javascript" src="autoNumeric.js"> </script>

<script type="text/javascript">
    ko.validation.init({
        registerExtenders: true,
        messagesOnModified: true,
        insertMessages: true,
        parseInputAttributes: true,
        messageTemplate: null  /* Template not working  - exception */
    });
    ko.validation.rules.pattern.message = 'Invalid.';

    ko.bindingHandlers.autoNumeric = {
        init: function (el, valueAccessor, bindingsAccessor, viewModel) {
            var $el = $(el),
                bindings = bindingsAccessor(),
                settings = bindings.settings,
                value = valueAccessor();

            function updateModelValue() {
                value(getElementValue($el));
            };

            if (settings.pSign === 's') {
                settings.aSign = ' ' + settings.aSign;
            } else {
                settings.aSign = settings.aSign + ' ';
            }
            settings.aNeg = "-";
            settings.vMin = -9999999999999.99999;
            $el.autoNumeric(settings);
            $el.autoNumericSet(getModelValue(value), settings);
            $el.change(updateModelValue);
        },
        update: function (el, valueAccessor, bindingsAccessor, viewModel) {
            var $el = $(el),
                newValue = getModelValue(valueAccessor()),
                elementValue = getElementValue($el),
                valueHasChanged = (newValue != elementValue);

            if ((newValue === 0) && (elementValue !== 0) && (elementValue !== "0")) {
                valueHasChanged = true;
            }

            if (valueHasChanged) {
                $el.autoNumericSet(newValue);
                setTimeout(function () { $el.change() }, 0);
            }
        }
    };


    function getElementValue(el) {
        return parseFloat(el.autoNumericGet(), 10);
    }

    function getModelValue(accessor) {
        return parseFloat(ko.utils.unwrapObservable(accessor()), 10);
    }
</script>

<script type="text/javascript">

var cartItem = function(parent){
    this.ItemCode = ko.observable().extend({ minLength: 2, maxLength: 10, required: { message: 'Please select item'} });
    this.ItemName = ko.observable().extend({ minLength: 2, maxLength: 50, required: { message: 'Please select item'} });
    this.Qty = ko.observable().extend({ min: 0, max: 1000, required: { message: 'Please enter Quantity required'} });
    this.Rate = ko.observable().extend({ required: { message: 'Please enter a valid rate'} });
    this.Amount = ko.dependentObservable(function () {
       return this.Rate()*this.Qty();
    },this);
};

var model = {
    CartSessionID: ko.observable(),
    CustomerName: ko.observable().extend({ minLength: 2, maxLength: 10, required: { message: 'Please enter customer name'} }),
    CustomerAddress: ko.observable().extend({ minLength: 10, maxLength: 100, required: { message: 'Please enter Address '} }),
    CustomerContact: ko.observable().extend({ minLength: 10, maxLength: 20, required: { message: 'Please enter contact details'} }),
    cart: ko.observableArray([]),
     AddItem : function (){
         model.cart.push(new cartItem(model));
     },
     RemoveItem :function (item){
          model.cart.remove(item);
     }
};

  model.totalAmount = ko.dependentObservable(function () {
       var total =0;
       $(this.cart()).each(function(){
            total = total + parseFloat(this.Amount());
       });
       return total;
    },model);

</script>

<form id="form1" action="/sumbitcart" method=post>

<fieldset>
 <legend>Cart Details</legend>
 <div id="cart">
   <div id="custinfo">
      <ul> 
          <li> Customer Name </li>
          <li> 
        <input type="hidden" id="CartSessionID" data-bind="value:CartSessionID" />
        <input type="text" id="CustomerName" data-bind="value:CustomerName" />
      </li>
      </ul>
       <ul> 
          <li> Customer Address </li>
          <li> 
        <input type="text" id="CustomerAddress" data-bind="value:CustomerAddress" />
      </li>
      </ul>
      <ul> 
          <li> Customer Contact </li>
          <li> 
        <input type="text" id="CustomerContact" data-bind="value:CustomerContact" />
      </li>
      </ul>    
   </div>
   <div id="cartinfo">
     <input type="button" id="btnAdd" value="Add" data-bind="click:AddItem" />
     <table>
         <thead> 
            <tr> <td> Item Name  </td> <td> Item Qty </td> <td> Rate </td>  <td> Amount </td> <td>&nbsp;</td></tr>
         </thead>
         <tbody data-bind='foreach:cart'>
            <tr> <td> <input type="text" id="ItemName" data-bind="value:ItemName, uniqueValue:true" /> </td> 
                  <td> <input type="text" id="ItemQty" data-bind="autoNumeric:Qty, uniqueValue:true, settings:{aSign:'',mDec:2}" /> </td> 
                <td> <input type="text" id="ItemRate" data-bind="autoNumeric:Rate, uniqueValue:true, settings:{aSign:'$',mDec:2}" /> </td>  
                <td> <input type="text" id="ItemAmount" readonly="readonly" data-bind="text:Amount, uniqueValue:true , settings:{aSign:'$',mDec:2}" /> </td>
                <td> <input type="button" id="btnRemove" value="Remove" data-bind="click:$parent.RemoveItem" /></td>
            </tr>
         </tbody>
         <tfoot> 
             <tr> <td> Total </td> <td> <span id="totalQty" /> </td> <td> &nbsp; </td>  <td> <span id="totalAmout" data-bind="value:totalAmount" /> </td><td>&nbsp;</td> </tr>
       </tfoot> 
     </table>
     <!-- Working Sample -->
     <table>
         <thead> 
            <tr> <td> Item Name  </td> <td> Item Qty </td> <td> Rate </td>  <td> Amount </td> <td>&nbsp;</td></tr>
         </thead>
         <tbody data-bind='foreach:cart'>
            <tr> <td> <input type="text" id="Text1" data-bind="value:ItemName, uniqueValue:true" /> </td> 
                  <td> <input type="text" id="Text2" data-bind="value:Qty, uniqueValue:true, settings:{aSign:'',mDec:2}" /> </td> 
                <td> <input type="text" id="Text3" data-bind="value:Rate, uniqueValue:true, settings:{aSign:'$',mDec:2}" /> </td>  
                <td> <input type="text" id="Text4" readonly="readonly" data-bind="text:Amount, uniqueValue:true , settings:{aSign:'$',mDec:2}" /> </td>
                <td> <input type="button" id="Button1" value="Remove" data-bind="click:$parent.RemoveItem" /></td>
            </tr>
         </tbody>
         <tfoot> 
             <tr> <td> Total </td> <td> <span id="Span1" /> </td> <td> &nbsp; </td>  <td> <span id="Span2" data-bind="value:totalAmount" /> </td><td>&nbsp;</td> </tr>
       </tfoot> 
     </table>
   </div>
 </div>
</fieldset>
</form>

<script type="text/javascript">
    ko.applyBindings(model);
</script>
</body>
tpever commented 12 years ago

Hope above code snapshot helps you to determine the problem.

Thanks a lot.

tpever commented 12 years ago

Hope you are able to trace the issue, please let me know.

Thanks and regards

ericmbarnard commented 12 years ago

@tpever

I don't see a call to the 'group' function in your code. Could you take what you pasted and get it working in jsFiddle (www.jsFiddle.net) ? It would be much more helpful to me if you could replicate what you are running into in that, and then just paste the link here so I can look at it on my side.

I'm glad to fix any issues that you are running into related to this plugin, but from this source I cannot tell where your issue might be stemming from (either ko.validation or the autonumeric plugin)

tpever commented 12 years ago

Thanks for reply, please have a look at Url http://jsfiddle.net/Q8xC8/3/

Based on my understanding it seems issue using any custom binding or extension, the valueAccessor() is not set and due to that couldn't add span tag and and hence message is not getting display,

May be issue at utils.isValidatable(valueAccessor())) if (config.insertMessages && utils.isValidatable(valueAccessor())) at registerValueBindingHandler

Again thanks for your help.

ericmbarnard commented 12 years ago

Thanks!

Inserting validation messages only works with the value binding in KO. It looks like your autonumeric binding doesn't utilize the value binding internally. I think this is a good use case for the validationMessage binding... see the fiddle:

http://jsfiddle.net/Q8xC8/6/

Let me know if that works for you

tpever commented 12 years ago

Thanks a lot for reply,

ya it seems validation scripts only works with the value bindings. Thanks a lot for pointing me the actual issue, in custom binding "value" binding is not utilized updated it and seems working for this case.

Still I am evaluating KOVaidation and KOGrid for very complex form having more then 100 fields and complex business rules. and going to use multiple KO extension and subscribable objects. It will be difficult to add separate span for each field while design the form i might have to find some work around for same.

Again many thanks for help.

nmocruz commented 11 years ago

try use ko.validation.makeBindingHandlerValidatable("autoNumeric")