JQuery & Knockout - A Simple Example
Hi All,
I have been assigned to do some client side stuff. In this case, we have some FAQ contents in CRM back-end and I want to grab those content and display it in a user control.
End result will look like this:
We extensively use Knockout.js and jQuery in terms of displaying simple content. The advantage of this is that it renders much faster than using server controls. To grab the data we can use POST request to get JSON from the asmx webservice.
Here is an example of displaying FAQCategory. Note that:
1. UPDATE - updated to knockout 2.1.0 . This will use native templating.
2. Grouping is achieved by creating pivot 'associative' array object :)
3. Notice multiple synchronous request? This is because the webservice is using RetrieveMultiple request (CRM 4.0) thus I need to grab attribute from related entity. Then I append the attributes to my result object. (can use FetchXML but more cumbersome in terms of parsing + slower than QueryExpression. Also, you can use jQuery Ajax function as well if you wish to :))
4. It is displayed using collapsible behavior of jQuery :)
Template:
1 2 3 4 5 6 7 8 9 | < div data-bind = "foreach: Categories" > < div data-bind = "visible: $root.Questions()[$data]" > < div class = 'FAQCategory' >< h3 data-bind = "text: $data" ></ h3 ></ div > < div data-bind = "foreach: $root.Questions()[$data]" > < div class = "FAQQuestion" data-bind = "html: cc_questiontext" ></ div > < div class = "FAQAnswer" data-bind = "html: answertext" ></ div > </ div > </ div > </ div > |
1 2 3 4 5 6 7 | < h1 id = "FAQTitle" ></ h1 > < p >FAQ Category: < select data-bind = "options: Categories, value: selectedCategory, optionsCaption: 'All Categories'" ></ select ></ p > < br /> < div id = "FAQContainer" > < div data-bind = "template: {name: 'crmFAQ_Tmpl'}" ></ div > </ div > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | $( function () { //Get FAQ Categories var categories = GetSynchronousJSONResponse( "/_controltemplates/MigrationPortal/Services/FAQService.asmx/GetCategories" , null ); categories = eval( '(' + categories + ')' ); viewModel.Categories = ko.mapping.fromJS(categories.d); viewModel.Questions = ko.observableArray([]); viewModel.selectedCategory = ko.observable(); //Get FAQ Questions and Answers viewModel.selectedCategory.subscribe( function (faqCategory) { if (faqCategory == null ) { faqCategory = '' ; } var result = GetSynchronousJSONResponse( "/_controltemplates/MigrationPortal/Services/FAQService.asmx/GetQuestions" , '{"category":"' + faqCategory + '"}' ); result = eval( '(' + result + ')' ); $.each(result.d, function (index, value) { var categoryName = GetSynchronousJSONResponse( "/_controltemplates/MigrationPortal/Services/FAQService.asmx/GetCategoryName" , '{"categoryId":"' + value.cc_subjectid + '"}' ); categoryName = eval( '(' + categoryName + ')' ); value.categoryName = categoryName.d; var answer = GetSynchronousJSONResponse( "/_controltemplates/MigrationPortal/Services/FAQService.asmx/GetAnswer" , '{"cc_careeradviceanswerid":"' + value.cc_careeradviceanswerid + '"}' ); answer = eval( '(' + answer + ')' ); value.answertext = answer.d.cc_answertext; }); //Create pivot for grouping var categories = {}; $.each(result.d, function (i, q) { if (!categories[q.categoryName]) categories[q.categoryName] = [q]; else categories[q.categoryName].push(q); }); viewModel.Questions(categories); applyCollapsible(); }); ko.applyBindings(viewModel); if (getUrlVars()[ "category" ] != null ) { viewModel.selectedCategory(decodeURIComponent(getUrlVars()[ "category" ])); } else { viewModel.selectedCategory( '' ); } }); function applyCollapsible() { $( ".FAQAnswer" ).hide(); $( "<span class='FAQplus'>+</span>" ).prependTo( ".FAQQuestion" ); $( ".FAQQuestion" ).each( function () { var state = false $( this ).click( function () { state = !state; $( this ).next( ".FAQAnswer" ).slideToggle(500); $( this ).toggleClass( 'active' , state); }); }); } function GetSynchronousJSONResponse(url, postData) { var xmlhttp = null ; if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); else if (window.ActiveXObject) { if ( new ActiveXObject( "Microsoft.XMLHTTP" )) xmlhttp = new ActiveXObject( "Microsoft.XMLHTTP" ); else xmlhttp = new ActiveXObject( "Msxml2.XMLHTTP" ); } // to be ensure non-cached version of response url = url + "?rnd=" + Math.random(); xmlhttp.open( "POST" , url, false ); //false means synchronous xmlhttp.setRequestHeader( "Content-Type" , "application/json; charset=utf-8" ); xmlhttp.send(postData); var responseText = xmlhttp.responseText; return responseText; } function getUrlVars() { var vars = [], hash; var hashes = window.location.href.slice(window.location.href.indexOf( '?' ) + 1).split( '&' ); for ( var i = 0; i < hashes.length; i++) { hash = hashes[i].split( '=' ); vars.push(hash[0]); vars[hash[0]] = hash[1]; } return vars; } |
Hope this helps,
Andreas
Comments
Post a Comment