How to call a function when a visibility binding is true? - javascript

for an example how to execute a javascript function when the binding is visible:true meaning BooleanIndicator() have returned true, assuming that the function called has e as a parameter, whereby e is an event.
<div data-bind="visible: shouldShowMessage">
You will see this message only when "shouldShowMessage" holds a true value.
</div>
<div >
Also show this div when the above div is visble
<div>

Binding is dependent on the data in your view model. If BooleanIndicator() is an observable property of your viewmodel you can create a computed function that should get called whenever the BooleanIndicator() changes
self.ComputedFunction = ko.computed(function() {
if (self.BooleanIndicator()){
//Do something - I'm visible
} else {
// Do something else
}
});

Related

trying to update object attribute when checkbox is selected

I have an object with many attributes, one of them is a boolean called "is_mandatory". Whenever an object of this sort is instantiated, "is_mandatory" is initially set to false.
I want to set this attribute to true/false whenever a certain checkbox is clicked.
objectID.is_mandatory = (function() {
$("#checkboxID").change(function() {
if ($("#checkboxID").prop("checked")) {
return true;
} else {
return false;
}
});
})();
I'm new to JavaScript and jQuery. I'm new to front-end development altogether. I've tried many variations of the above code, can't seem to get this work.
use the on change event to update your object.
run snippet below
let myObject = {is_mandatory: false};
$( document ).ready(function() {
render();
$('#container').on('change', '#checkboxID', () => {
myObject.is_mandatory = $('#checkboxID:checked').length ? true : false;
render();
});
});
function render(){
$('#container').empty().append(`<input type="checkbox" id="checkboxID" ${myObject.is_mandatory ? 'checked' : ''}/>`)
console.log(myObject);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
</div>
Have you tried
objectID.is_mandatory = $("#checkboxID").prop("checked");
$(document).on('change','#checkboxID',function(){
var cb = document.getElementById("checkboxID");
myObject.is_mandatory = cb.checked;
});
Basically, "$" is a function that return an jquery object, jquery has several methods in your case "change" method.
"change" method most likely return void since it is actually a shortform of addEventListener("change").
Thus your code need to be modified. instead of returning value. It should access myObject, and set the property manually instead of returning.
or use other method like "prop" which return the value of the DOM Element.

Binding div if function return true

How to bind element only if the function return true;
<label class="hello" data-bind="if: myFunction">Hello World</label>
function myFunction(n){
if(n==2) return true;
}
it is visible also function return false
The if binding applies to the content of the element. If you want the element itself to disappear rather than just the text within it you'll need to wrap the label inside of something else and apply the binding to the wrapper. You can also use the virtual binding for this.
<--ko if: myFunction-->
<label class="hello">Hello World</label>
<!--/ko-->
Additionally, for your function to update properly it will have to be a computed property rather than a normal function, and n needs to be an observable.
var n = ko.observable();
myFunction = ko.computed(function(){
if(n()==2) return true;
});

Knockout.js - Prevent change event binding to trigger when setting the value through the observable

I have a DropDownList with the following bindings:
<select data-bind="value: DropDownValue, event: { change: OnChange }">
<option value="1">Val 1</option>
/* and more */
</select>
The OnChange event is fired correctly when the user select a different value from the DropDownList.
The event is also fired when updating the value of the observable property using viewModel.DropDownValue(1).
What I'm trying to achieve, is to trigger the change event ONLY when the user sets the value through the UI.
Is it possible to block the change event when updating the value through the observable?
This is the JSFiddle example: https://jsfiddle.net/5ex5j7jL/3/
Looks like one way to do it is to use the isTrusted property of the event object (true when the event was generated by a user action, false when generated by a script):
self.OnChange = function(viewModel, event) {
if(event.isTrusted) {
console.log("from dropdown");
return;
} else {
console.log("NOT from dropdown");
// do something
}
};
See updated fiddle
EDIT
Of course, you have to implement some king of mechanism if you want to prevent the user from changing the dropdown via the UI:
function ViewModel() {
var self = this;
self.DropDownValue = ko.observable();
self._original = null;
self.OnChange = function(viewModel, event) {
if(event.isTrusted) {
// rollback the viewModel value to the old one
viewModel.DropDownValue(self._original)
return false
} else {
// keep a reference to the latest value for later rollback
self._original = ko.unwrap(viewModel.DropDownValue)
}
};
};
See this updated fiddle

Delegating Draggable Events to Parent Elements

I have draggable li elements nested in a ul in turn nested in a div, as seen below:
<div class='group'>
<div class='header'>
// some stuff here
</div>
<ul>
<li draggable='true'>
Stuff I want to drag and drop to another div.group
</li>
</ul>
</div>
There are multiple of these div elements and I am trying to implement a drag & drop functionality to move the li elements of one div group to another.
I have hooked up the ondragenter, ondragleave callbacks here:
// controller using mithril.js
ctrl.onDragLeave = function () {
return function (event) {
var target;
// Using isDropzone to recursively search for the appropriate div.group
// parent element, as event.target is always the children inside it
if ((target = isDropzone(event.target)) != null) {
target.style.background = "";
}
}
};
ctrl.onDragEnter = function () {
return function (event) {
var target;
if ((target = isDropzone(event.target)) != null) {
target.style.background = "purple";
}
};
};
function isDropzone(elem){
if(elem == null){
return null;
}
return elem.className == 'group' ? elem: isDropzone(elem.parentNode)
}
The problem comes when the event.target of the callbacks are always the nested child elements inside the div, such as li, and thus the callbacks are constantly fired. In this case I'm changing the color of the div.group with my callbacks, resulting in the div.group blinking undesirably.
Is there a way to delegate events and only allow the div grand parent of li to handle the events? Or any other way to work around this?
EDIT: Would still love to find out if there's a way to do this, but right now I'm using the workaround I found here.
So this is going to fit into the "you need to approach this from a different angle" category of answers.
Avoid- as much as possible- manipulating the DOM from event.target/event.currentTarget in your attached handlers.
A couple things differently:
Your ondragleave and ondragenter handlers should simply set some appropriate "state" attributes in your controller/viewModel/stores
When the handler is resolved, this generally triggers a redraw in Mithril. Internally m.startComputation() starts, your handler is called, then m.endComputation()
Your "view function" runs again. It then reflects the changed models. Your actions don't change the views, your views call actions which affect the models, and then react to those changes. MVC, not MVVM
Model
In your controller, set up a model which tracks all the state you need to show your drag and drop ui
ctrl.dragging = m.prop(null)
ctrl.groups = m.prop([
{
name: 'Group A',
dragOver: false,
items: ['Draggable One', 'Draggable Two']
},
...
// same structure for all groups
])
View
In your view, set up a UI that reflects your models state. Have event handlers that pass sufficient information about the actions to the controller- enough that it can properly respond to the actions an manipulate the model accordingly
return ctrl.groups.map(function (group, groupIdx) {
return m('.group',[
m('.header', group.name),
m('ul',
{
style: { background: (group.dragOver ? 'blue' : '')},
ondragleave: function () {ctrl.handleDragLeave(groupIdx)},
ondragenter: function () {ctrl.handleDragEnter(groupIdx)},
ondrop: function () {ctrl.handleDrop(groupIdx)},
ondragover: function (e) {e.preventDefault()}
},
group.items.map(function (item, itemIdx) {
return m('li',
{
draggable: true,
ondragstart: function () {ctrl.handleDragStart(itemIdx, groupIdx)}
},
item
})
)
])
})
Now its set up so that the group can properly display by reacting to state/model changes in your controller. We don't need to manipulate the dom to say a group has a new item, a group needs a new background color, or anything. We just need to attach event handlers so the controller can manipulate your model, and then the view will redraw based on that model.
Controller
Your controller therefore can have handlers that have all the info from actions needed to update the model.
Here's what some handlers on your controller will look like:
ctrl.handleDragStart = function (itemIdx, groupIdx) {
ctrl.dragging({itemIdx: itemIdx, groupIdx: groupIdx})
}
ctrl.handleDragEnter = function (groupIdx) {
ctrl.groups()[groupIdx].dragOver = true
}
ctrl.handleDragLeave = function (groupIdx) {
ctrl.groups()[groupIdx].dragOver = false
}
ctrl.handleDrop = function (toGroupIdx) {
var groupIdx = ctrl.dragging().groupIdx
var itemIdx = ctrl.dragging().itemIdx
var dropped = ctrl.groups()[groupIdx].items.splice(itemIdx, 1)[0]
ctrl.groups()[toGroupIdx].items.push(dropped)
ctrl.groups()[toGroupIdx].dragOver = false
ctrl.dragging(null)
}
Try to stick with Mithril's MVC model
event handlers call actions on your controller, which manipulates the model. The view then reacts to changes in those models. This bypasses the need to get entangled with the specifics of DOM events.
Here's a full JSbin example showing what you're trying to get to:
https://jsbin.com/pabehuj/edit?js,console,output
I get the desired effect without having to worry about event delegation at all.
Also, notice that in the JSbin, the ondragenter handler:
ondragenter: function () {
if (ctrl.dragging().groupIdx !== groupIdx) {
ctrl.handleDragEnter(groupIdx)
}
}
This is so the droppable area doesn't change color on its own draggable, which is one of the things I think you're looking for in your answer.

plugin save var inside each returned click

You guys mind checking out this jsfiddle I made to help you understand my issue. http://jsfiddle.net/kr1zmo/DqbeX/8/:
item
item 2
item 3
item 4
<p id="result"></p>
<script language="javascript" type="text/javascript">
(function($) {
$.fn.liveBindTest = function() {
return this['live']('click', function() {
var savedvar;
if (!savedvar || savedvar == 0) {
// is false, do false things.
savedvar = 1;
jQuery('#result').append(savedvar);
} else {
// is true, do true things.
jQuery('#result').append(savedvar);
savedvar = 0;
}
return false;
});
};
})(jQuery);
jQuery(document).ready(function() {
$('a.cref').liveBindTest();
});
</script>
I want to save a variable for each click.
Take a look at this example.
Did you want to toggle which bit of code to execute? If you want to hold the value in a closure, you'll need to declare it outside of the live event handler function.
If the value needs to be held for each element matched by the selector, then you could use $(elem).data() to store the value like in this example.
You declared your variable inside the event handler, creating a separate local variable for each handler.
You need to declare the variable outside the function.
If you want a separate variable for each element, you can declare the variable and add the handler in an each call, or use jQuery's .data function.

Categories