Tuesday, June 11, 2013

How to Use Jquery Methods with Knockout

Knockout out provides a sophisticated way to use jQuery methods with your binding. You can use a custom binding feature provided by knockout for this purpose. Suppose I want to use SlideDown and slideUp methods of jQuery to show and hide the elements. Although it can be achieve by using the “visible” property of knockout, but I want to do it in a fancy way.

Add a sub property in ko.bindingHandlers and write your custom code in init and update method. This blog does not explain how to create custom binding. You can find its help in the knockout documentation. Here is the complete code of the custom binding for show/hide element using jQuery.
ko.bindingHandlers.collapseVisible = {
  
 init: function (element, valueAccessor) {

   var value = valueAccessor();
   $(element).toggle(ko.utils.unwrapObservable(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable

 },
 
 update: function (element, valueAccessor, allBindingsAccessor) {

   var value = valueAccessor();
   var allBindings = allBindingsAccessor();

   // Grab some more data from another binding property
   var duration = allBindings.slideDuration || 500; 

   ko.utils.unwrapObservable(value) ? $(element).slideDown(duration) : $(element).slideUp(duration);

  }
 };



In the init method set the initial state of the element. This method calls once for each DOM element. Get element using $(element) and perform action which you want.

Whenever the associated observable change, the ko calls you update callback method. In this example I get the value of element using ko.utils.unwrapObservable(value) and call slideUp or slideDown method according to value.

Now next step is to bind your DOM element using ‘CollapseVisible’ custom binding.


 <div data-bind="collapseVisible: displayDetail, slideDuration: 1000" class="desc">
    Lorem Ipsum is simply dummy text of the ...
 </div> 


So whenever the value of displayDetail observable will change. The Update callback method will be called and according to value the slideUp and slideDown method will be called.

Complete Solution



Now here is complete working solution for it. Set style for your element.


 <script src="Scripts/jquery-2.0.0.js"></script>
 <script src="Scripts/knockout-2.2.1.js"></script>

 <style>
     .box
     {
       border: 1px black solid; width: 650px; margin-left: auto; margin-right: auto;
     }

     .box .title
     {
       padding: 8px; background-color: #e1e1e1; border-bottom: 1px black solid;
     }

     .box .title .showButton
     {
       float: right;
     }

     .box .desc
     {
       padding: 8px;font-size:12px;
     }
 
 </style>



Set your element, assign CSS classes and set binding etc.


 <div class="box">
    <div class="title">

       <span data-bind="html: title"></span>
       <a class="showButton" href="#" data-bind="html: showButtonText, event: { click: onShow }"></a>
    </div>
    <div data-bind="collapseVisible: displayDetail, slideDuration: 1000" class="desc">
         Lorem Ipsum is simply dummy...
    </div>
 </div>




Finally create your view model, custom binding and bind them to your page.


 <script type="text/javascript">

   var vm = {

      title: "Collapsible   Box",

      showButtonText: ko.observable('Show'),

      displayDetail: ko.observable(false),

      onShow: function () {

          var newVal = !this.displayDetail();


          this.displayDetail(newVal);
          this.showButtonText(newVal ? "Hide" : "Show");

      }

   };



   ko.bindingHandlers.collapseVisible = {
            
        init: function (element, valueAccessor) {

             var value = valueAccessor();
             $(element).toggle(ko.utils.unwrapObservable(value)); // Use "unwrapObservable" so we can handle 
                                                                  // values that may or may not be observable

            },            
        update: function (element, valueAccessor, allBindingsAccessor) {

                var value = valueAccessor();
                var allBindings = allBindingsAccessor();

                // Grab some more data from another binding property
                var duration = allBindings.slideDuration || 500; 

                ko.utils.unwrapObservable(value) ? $(element).slideDown(duration) : $(element).slideUp(duration);

            }
   };


   ko.applyBindings(vm);

 </script>

Friday, May 24, 2013

Use TinyMCE in Asp.Net Single Page Application (SPA) using Knockout


In one of our project, we were creating web site using Asp.Net single page application with knockout.js. We had to add TinyMCE html editor in one of my page. It took some time to make it work.
Add binding handler for tinyMce using knock out. Please see code below

ko.bindingHandlers.tinymce = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        var options = allBindingsAccessor().tinymceOptions || {};
        var modelValue = valueAccessor();
        var value = ko.utils.unwrapObservable(valueAccessor());
        var el = $(element)
        options.theme = "advanced";
      

        // Customize Tool Bars. Remove 3 lines below if you want standard tool bar
        options.theme_advanced_buttons1 = "bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,separator,bullist,numlist,undo,redo,link,unlink";
        options.theme_advanced_buttons2 = "outdent,indent,separator,forecolor,backcolor,emoticons,separator,formatselect";
        options.theme_advanced_buttons3 = "";

        ////handle edits made in the editor. Updates after an undo point is reached.
        options.setup = function (ed) {
            ed.onChange.add(function (ed, l) {
                if (ko.isWriteableObservable(modelValue)) {
                    modelValue(l.content);
                }
            });
        };

        //handle destroying an editor 
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            setTimeout(function () { $(element).tinymce().remove() }, 0)
        });

        // $(element).tinymce(options);
        setTimeout(function () {
            $(element).tinymce(options);
            //$(element).tinymce({});

        }, 1000);

        el.html(value);

    },
    update: function (element, valueAccessor, allBindingsAccessor, context) {

        var el = $(element)
        var value = ko.utils.unwrapObservable(valueAccessor());
        var id = el.attr('id')

        //handle programmatic updates to the observable
        // also makes sure it doesn't update it if it's the same. 
        // otherwise, it will reload the instance, causing the cursor to jump.
        if (id !== undefined) {
            $(el).tinymce();
            var tinyM = tinyMCE.getInstanceById(id);
            if (tinyM !== undefined) {
                var content = tinyM.getContent({ format: 'raw' })
                if (content !== value) {
                    el.html(value);
                }
            }
        }
    }

};

Now you can bind any text area using data-bind of knockout and don’t forget to assign id to the element e.g. id=”txtPublished”.

<textarea data-bind="tinymce: publishNotes" id="txtPublished" style="height: 150px; width: 550px;"></textarea>