knockout js observe global variable - javascript

I am setting a variable
var extQty;
Then a user inputs some data and submits, I return a json string with extQty. On success I am updating the variable:
success: function (result) {
window.extQty = result.extQty;
}
I need to hide a button if the extQty surpasses the row count of a grid. How can I do this? I tried the below but it is not seeing the variable change as this has already loaded before extQty gets updated.
<!-- ko if: pagedList().length < extQty -->
If I change extQty in the above to say 3 it works...

Make your variable a ko.observable and make it part of your view model.
function viewModel() {
this.extQty = ko.observable(0);
this.pagedList = ko.observableArray();
//....
}
var myViewModel = new viewModel();
ko.applyBindings(myViewModel);
Then in your AJAX success:
success: function (result) {
myViewModel.extQty(result.extQty);
}

Tried updating your fiddle:-
Your Fiddle Updated
<div data-bind="style: { display: (pagedList().length < extQty()) ? 'block' : 'none' }">
<p class="pull-right">
<a class="btn btn-primary" data-bind="click: $root.add" href="#" title="edit"><i class="icon-plus"></i>Add Extension</a>
</p>
</div>
Correct me if i am wrong, when your use if binding, it allows the markup to appear or dissapear from your document based on condition as true or false. When your condition is false, the element would be commented out code when you see in html. Which cannot be changed unless you refresh the page. So, use visible binding, the markup stays in dom and you can play with the Visible binding to hide it or show it...
Have a look at Knockout If Binding documentation

Related

Save toggleClass state of dynamically generated divs and store with Cookie/LocalStorage

I am trying to save my toggleClass state of multiple dynamically generated divs and store them with LocalStorage, so they're available either on page refresh or re-visiting the page. I stuck in the middle of my code and have no more ideas.
Any solutions i found here, will not work, either they refer to a single element or they use a mix of addClass/removeClass and save state.
Saving with Cookie would be an option too.
Html:
<div id="row_parent_41" class="parent">
<div id="page_41">
<span class="showhide" id="more_2"><img src="plus.png" /></span>
</div>
<div id="holder_41" class="child">stuff goes here</div>
jQuery:
var inactiveHolder = localStorage.getItem('child') == 'true';
$(".showhide").on('click', function() {
$(this).closest(".parent").find(".child").slideToggle().toggleClass('inactiveHolder');
$('.child').toggleClass('clicked', inactiveHolder );
return false;
});
Does this code work for you?
var inactiveHolder = localStorage.getItem('child');
// set initial state
if (inactiveHolder == 'true') {
$('.child').addClass('clicked');
}
// change localstorage and class
$('.showhide').on('click', function() {
var element = $(this).closest(".parent").find(".child");
$(element).slideToggle().toggleClass('clicked');
localstorage.setItem('child', $(element).hasClass('clicked'));
return false;
});

Get the data-url correctly using javascript function

I am creating a delete comment function and
this are the pieces of html for a delete comment functionality.
<div id="comment-area">
<div class="list-border">
<small class="mdl-button delete-comment js-delete-comment pull-right" data-url="http://myproject.com/comment_controller/delete_comment/12/6">
x
</small>
</div>
<div class="list-border">
<small class="mdl-button delete-comment js-delete-comment pull-right" data-url="http://myproject.com/comment_controller/delete_comment/13/6">
x
</small>
</div>
</div>
And this is the function for delete a comment, which is automatically loaded when document is ready,
function delete_comment () {
$('.delete-comment').click( function (e) {
var url = $('.delete-comment').attr('data-url');
$.ajax({
url: url,
type: 'get',
dataType: 'json',
success: function (data) {
$('#comment-area').html(data.comments);
delete_comment();
},
error: function () {
alert('error');
}
});
});
}
The problem with this function from the given html above, if I'm going to click on .delete-comment with
data-url="http://myproject.com/comment_controller/delete_comment/13/6" take note on this "13/6", the javascript function delete_comment(),
in this line
var url = $('.delete-comment').attr('data-url');
chose to get the data-url="http://myproject.com/comment_controller/delete_comment/12/6" take note also this "12/6", to see the difference, instead of the data-url of .delete-comment I clicked.
In short, this function always chose the first-child div small, whatever small.delete-comment I'm going to click.
What was the best solution for this?
Try
var url = $(this).attr('data-url');
You are getting the attribue value by className and multiple elements have same class so the attribute value for first element found in dom is being returned.
So you need to get the value of element which is clicked and you can use this to get the value of clicked element.
A more robust way to approach this would be to use the jQuery .data() function.
var url = $(this).data('url');
This will pick up any dynamically added/updated url that may have changed via JavaScript, but not updated in the DOM attribute.

How to stop the multiple binding in KnockOut.js

I have a view model like this :
function ImageViewModel()
{
var self = this;
self.totalRecordCount = ko.observable();
self.currentRecordCount = ko.observable();
self.images = ko.observableArray();
// fetching all available images
getAvailableImages(self, imageGalleryId, 1);//1 is page number for paging
}
I have html as followes:
<div id="available Images" class="available-images" data-bind="foreach:images">
<div c`enter code here`lass="available-image">
<div class="col-sm-4 thumbnail">
<asp:CheckBox ID="cbxImage" runat="server" CssClass="checkbox" />
<img alt="" data-bind="attr: { 'src': ImagePath, id: 'img_' + ImageId, 'data-id': ImageId }"
style="border: none;" />
</div>
</div>
</div>
i have java script at the page bottom as :
$('.galleryfooter').click(function () {
$(this).attr('data-target', '#imageModal');
$(this).attr('data-toggle', 'modal');
ko.applyBindings(new ImageViewModel(), document.getElementById("imageGallery"));
});
when i first Clicked the images are bind properly but when i clicked the button again then images are get multiplied.Means if i have 5 images in database it displays 25 images.So what should i do?
Stop using jQuery to handle events. Knockout has bindings for that. The agreement you have with Knockout is that it controls the DOM and you only manipulate the viewmodel.
See the click binding and the attr binding. Also, if you have not gone through the Knockout tutorial, I highly recommend it. It will help you let go of the DOM.
I agree with Roy. You need to separate your concerns better. That is the whole point of having a controller. As it stands with every click you are reapplying the binding. The binding should only be applied once. It should be more like this.
function ImageViewModel()
{
var self = this;
self.totalRecordCount = ko.observable();
self.currentRecordCount = ko.observable();
self.images = ko.observableArray();
// fetching all available images
getAvailableImages(self, imageGalleryId, 1);//1 is page number for paging
this.clickThis = function()
{
//doStuff
};
}
ko.applyBindings(new ImageViewModel(), document.getElementById("imageGallery"));
I don't know where your gallery footer is supposed to go. But here is a guess.
<div class="galleryfooter" data-bind="click: function(e){$root.clickThis();}"></div>
Also use knockout for attribute binds instead of jquery.

Dynamically Inserting a Div Breaks KnockoutJS DataBinding

I have created a KnockoutJS application, and I also must use some third-party stuff with it. My third-party stuff uses vanilla Javascript to insert a div into the markup rendered by Knockout. Once this happens, Knockout stops working.
Here's a fiddle that encapsulates the problem: http://jsfiddle.net/p5o8842w/1/
HTML:
<div style="margin-bottom:50px;">
<button onclick='BindVM();' >Bind</button>
<button onclick='ThrowWrench();'>Throw a Wrench Into It</button>
</div>
<div id="ViewModel" data-bind="template: 'Template'">
</div>
<script type="text/html" id='Template'>
<div style="margin-bottom:20px;">
<span data-bind="text: Name"></span>
</div>
<div id="infoDiv">
<input type="text" data-bind="text: Name, value: Name, valueUpdate: 'keyup'" />
</div>
</script>
JavaScript:
function BasicVM () {
var self = this;
self.Name = ko.observable('The Name');
self.Title = ko.observable('The Title');
}
function BindVM() {
var vm = new BasicVM();
var element = document.getElementById('ViewModel');
ko.cleanNode(element);
ko.applyBindings(vm, element);
}
function ThrowWrench() {
var element = document.getElementById('infoDiv');
element.innerHTML = "<div class='container'>" + element.innerHTML + '</div>';
}
First, click 'Bind.' Notice that the textbox is bound to the span; change the box, you change the span.
Then, click 'Throw a Wrench Into It.' Now, the textbox is no longer data-bound to the ViewModel, and typing into it doesn't impact the span.
Things I can't do:
Take the third-party code and refactor/integrate it into my Knockout stuff.
Run the third-party code before I render with Knockout (which I think would help).
Call ko.applyBindings again after running the third-party code. I can do this, but then Knockout destroys what the third-party code did, so I'd have to run it again, which would cause the same problem again.
Is there any way around this?
Because replacing element.innerHTML it's losing is binding. In order to overcome this. Two method are available:
1- Rebind the new element
2- Else
var element = document.getElementById('infoDiv');
var parent = element.parentNode;
var wrapper = document.createElement('div');
parent.replaceChild(wrapper, element);
wrapper.appendChild(element);
This is updated url: http://jsfiddle.net/p5o8842w/5/

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Categories