Enable binding with multiple boolean observable flags - javascript

Trying to bind enable state using data-bind based on two flags. We need to enable a input box if flagA is true and also flagB is false.
var viewModel = function () {
var self = this;
self.flagA = ko.observable(true);
self.flagB = ko.observable(false);
self.changeState = function () {
self.flagA(false);
}
}
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type='text' data-bind='enable: flagA && !flagB' />
<button data-bind='click:changeState'>changeState</button>
Can any one help me find out why it is not working?
I've tried using a function like enable:function(){flagA && !flagB} to make this work. But it's not working: it does not observe when I change the state using a button.

Because flagA and flagB are observables (which are functions) you need to call them without any argument to get there values if you are using them in an expression:
<input type='text' data-bind='enable: flagA() && !flagB()' />
Demo JSFiddle.

Try to avoid putting logic in your views, it's a bad practice. In order to do this add computed variable
self.isEnabled = ko.computed(function() {
return this.flagA() && !this.flagB()
}, this);
and bind it as usual:
<input type='text' data-bind='enable: isEnabled' />
See fiddle

Related

2 way data binding in JavaScript

Two-way data binding refers to the ability to bind changes to an object’s properties to changes in the UI, and vice-versa.
Can we achieve 2-way data-binding with JavaScript?
Especially 2 Way Data Binding without Frameworks.
When an input is changed update the value, add a setter to the value which sets the inputs content. E.g this element:
<input id="age">
And some js:
var person = (function(el){
return {
set age(v){
el.value = v;
},
get age(){
return el.value;
}
};
})(document.getElementById("age"));
So you can do:
person.age = 15;
And the input will change. Changing the input changes person.age
Yes, we can achieve the two way data binding using pure javascript.
twoWay=function(event) {
var elem = document.getElementsByClassName(event.currentTarget.className);
for(var key in elem){
elem[key].value=event.currentTarget.value;
}
}
You can check the jsfiddle.
Simple and working approach to two-way binding, only using vanilla JS.
<!-- index.html -->
<form action="#" onsubmit="vm.onSubmit(event, this)">
<input onchange="vm.username=this.value" type="text" id="Username">
<input type="submit" id="Submit">
</form>
<script src="vm.js"></script>
// vm.js - vanialla JS
let vm = {
_username: "",
get username() {
return this._username;
},
set username(value) {
this._username = value;
},
onSubmit: function (event, element) {
console.log(this.username);
}
}
JS Getters and Setters are quite nice for this - especially when you look at the browser support.
Yes indeed.
There are frameworks like angular Js which provides full support for two way data binding.
And if you want to achieve the same in vanilla js you can bind value into view
Eg. document.getElementById('test').value="This is a Test"
And to bind view value to the controller you can trigger onchange event in html.
<Input type="text" id="test" onchange="Func()">
LemonadeJS is another micro-library (4K), with no dependencies worth looking at.
https://lemonadejs.net
https://github.com/lemonadejs/lemonadejs
Adding a little elaboration to Jonas Wilms answer, here's a sample without currying and also adds event binding for a full two way bind.
// Property binding
var person = {
set name(v) {
document.getElementById('name').value = v;
},
get name() {
return document.getElementById('name').value;
},
set age(v) {
document.getElementById('age').value = v;
},
get age() {
return document.getElementById('age').value;
}
};
// You can now set values as such
person.name = 'Cesar';
person.age = 12;
// Event binding completes the two way
function logToConsole(event) {
console.log(event.target.value);
}
// You can set person.name or person.age in console as well.
<label for="name">Name: </label><input id="name" onkeyup="logToConsole(event)">
<label for="age">Age: </label><input id="age" onkeyup="logToConsole(event)">
Would you mind if it would be a small component for databinding tasks that provides enough convenient databinding definition commands. I did it with databindjs. e.g.
// Lets assume that there is just simple form (target)
var simpleForm = {
input: $('.simple .input-value'),
output: $('.simple .output-value')
};
// And here is the simple model object (source)
var model = {
text: 'initial value'
};
// Lets set two directional binding between [input] <-> [text]
var simpleBinding = bindTo(simpleForm, () => model, {
'input.val': 'text', // bind to user input
'output.text': 'text' // simple region that will react on user input
});
// This command will sync values from source to target (from model to view)
updateLayout(simpleBinding);
subscribeToChange(simpleBinding, () => {
$('.simple .console').html(JSON.stringify(model));
});
// Just initialize console from default model state
$('.simple .console').html(JSON.stringify(model));
The full solution here.
You can check the full implementation of the databinding core on github

Two way binding using value set by AJAX - Observable doesn't notify when setting control's value

So, I have a simple page below where the rate field is set by AJAX. However the other calculated fields do not seem to get updated in the view model by KnockoutJS unless I type in the actual value for rate. How can this be resolved?
HTML:
<div>
Date: <input type="text" data-bind="value: date" id="txtDate" /><br />
Enter Amount: <input type="text" data-bind="value: amount" /><br />
Rate: <input type="text" data-bind="value: rate" id="txtRate" /><br />
VAT: <input type="text" data-bind="value: vat" /><br />
Total Amount: <input type="text" data-bind="value: totalAmount" />
</div>
JS
<script type="text/javascript">
$(function(){
$('#txtDate').change(function(){
$.getJSON('/api/Common/GetRate', { param: $(this).val() }).success(function (data) {
$('#txtRate').val(data);
});
});
});
function AppViewModel() {
var self = this;
self.date = ko.observable('');
self.amount = ko.observable(0);
self.rate = ko.observable(0);
self.vat = ko.pureComputed(function () {
return self.amount() * 0.1;
}, this);
self.totalAmount = ko.pureComputed(function () {
return self.amount() + self.vat();
}, this);
}
ko.applyBindings(new AppViewModel());
</script>
You're mixing up jQuery and Knockout, which is always a bad idea.
On the ajax call success, update the observable value, which is the KO way. To do so you need to create the view model before doing the ajax call, and store it in a variable, for example:
var vm = AppViewModel();
Then, on the AJAX call success callback, update the observable value, i.e.:
vm.rate(data);
You should always do it in this way: update the bound value, and the control will reflect the changes. As you've seen trying to do it the other way round is problematic, apart from more difficult.
It doesn't matter if you apply the binding before of after the callback.
And a final note: when you define computed observable the second parameter is to bind the callback function to it. I.e. it will be the value of this inside the function. So, it would make sense to do this:
self.vat = ko.pureComputed(function () {
return this.amount() * 0.1;
}, self);
but what you're doing is pointless. You can omit that second parameter if you don't use this inside the computed function.
NOTE: apart from being a bad idea, the reason why updating the value using jQuery doesn't work is because knockout bindings use events to be able to update observables. When you use jQuery to set the value, no events are triggered, so the observable isn't updated. Please, see the docs for value binding, and pay special attention to valueUpdate parameter description.

How do I reset a form in angularjs?

See Fiddle: http://jsfiddle.net/hejado/7bqjqc2w/
I'm trying to form.reset() my form using angular.
HTML:
<div ng-controller="formCtrl">
<form name="resetme" id="resetme">
<input ng-model="text" type="text" />
<input file-model="file" type="file" />
<button type="button" ng-click="resetForm()">reset</button>
</form>
</div>
JS:
.controller('formCtrl', function($scope) {
$scope.resetForm = function() {
//$scope.resetme.reset();
document.getElementById('resetme').reset();
};
});
Please note: I'm using this kind of form to ajax-upload a file. The page is not refreshing and I don't want to use any reset-buttons. (I'm using one in the fiddle for simplicity.) I want to call the reset-function after the fileupload is finished (via http success).
I'm using
<input type="file" />
so I can't reassign empty values to all my inputs, because file inputs are readonly.
Calling the reset() function on the DOM element works, but I was told talking to the DOM in angular would be evil, so...
I'd like to know, how this would be done the angular way. I tried naming the form and referencing it via $scope.formname but I'm not able to call Web API functions... (commented line)
How can I achieve this?
UPDATE
After reading some of the answers, I should make clear, that I am using ngModel and a custom directive fileModel to get a hold of the file-object.
Some of the solutions worked in resetting the value of the input field, but the model is not cleared (neither file, nor text). Custom directives are the answer to that, but this kinda exceeds the scope of this question.
I wrote about this topic a couple years ago. I don't know if the Angular team has yet implemented a native form reset directive but you can do so yourself. There are a couple caveats to this implementation: it only works for one model (if you need to support more see the followup post) and the issue of when to initialize the original values. Also, I never tested this with file inputs so I am not sure it would work with those.
There was an issue for this but it was closed due to inactivity. :/
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', ['$scope',
function($scope) {
$scope.myModel = {
foo: 'Boop',
bar: 'Beep'
};
$scope.myModelCopy = angular.copy($scope.myModel);
}
]);
myApp.directive('resetDirective', ['$parse',
function($parse) {
return function(scope, element, attr) {
var fn = $parse(attr.resetDirective);
var masterModel = angular.copy(fn(scope));
// Error check to see if expression returned a model
if (!fn.assign) {
throw Error('Expression is required to be a model: ' + attr.resetDirective);
}
element.bind('reset', function(event) {
scope.$apply(function() {
fn.assign(scope, angular.copy(masterModel));
scope.form.$setPristine();
});
// TODO: memoize prevention method
if (event.preventDefault) {
return event.preventDefault();
} else {
return false;
}
});
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<form reset-directive="myModel" name="form">
<input type="text" ng-model="myModel.foo" />
<input type="text" ng-model="myModel.bar" />
<input type="reset" value="Reset" />
<pre>myModel: {{ myModel | json }}</pre>
<pre>myModelCopy: {{ myModelCopy | json }}</pre>
<pre>form pristine: {{ form.$pristine }}</pre>
</form>
</div>
</body>
You can try :
reset
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
form.fieldA = null;
form.fieldB = null;
///more fields
}
Or
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
angular.copy({},form);
}
See Demo
You'd want to attach ng-model to each of your input fields then null them out via $scope. Either that or make a custom directive
I've just had a similar problem with forms not resetting. Here's what I would do:
In your resetform() function, I would include statements that set both of your ng-models in your input to "". For example:
**HTML**
<input ng-model="text" type="text" />
<input file-model="file" type="file" />
**JS**
.controller('formCtrl', function($scope) {
$scope.resetForm = function() {
$scope.text = "";
$scope.file = null;
};
});
Not certain if this will work for file-models but I'm certain it will remove the text. Best of luck!
If you don't want to use ng-model and proper reset type of button you can use this code, however this is not proper angular way to reset the form but it will work
$scope.reset = function(){
$('form').children('*').each(function(){
$(this).val('');
});
}
Here's the Plunker
To reset the form data use following code :
$scope.resetEmployeeData = function() {
$scope.employeeCred.userName = '';
$scope.employeeCred.employeeNo = '';
$scope.employeeCred.sapNo = '';
$scope.employeeCred.emailD = '';
$scope.employeeCred.mobileNo = '';
**this**.createEmployee.$setPristine();
**this**.createEmployee.$setUntouched();
};
use this rather than $scope.

Angularjs form reset

I have a reset function in angular to clear all the fields in a form. If I do something like:
reset
$scope.resetForm = function() {
$scope.someForm = {};
}
Everything works fine. But I want to use this function for multiple forms on the site. If I pass the form object in like:
reset
$scope.resetForm = function(form) {
$scope.form = {};
}
Then it won't work. Can someone explain to me why this would be happening?
You have 2 problems:
You're not accessing the passed in variable, still access the someForm of current scope.
When you pass parameter to the function, it's passed by reference. Even when you use form = {}, it does not work because it only changes the reference of the parameter, not the reference of the passed in someForm.
Try:
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
form.fieldA = null;
form.fieldB = null;
///more fields
}
Or
$scope.resetForm = function(form) {
//Even when you use form = {} it does not work
angular.copy({},form);
}
instead of:
$scope.resetForm = function(form) {
$scope.form = {};
}
In your plunk, I see that you're not separating the view from the model. You should do it for separation of concerns and to avoid problems that might happen when you clear all the fields (including DOM form object's fields).
<form name="form2" ng-controller="SecondController">
<label for="first_field">First Field</label>
<input ng-model="form2Model.first_field" />
<br />
<label for="second_field">Second Field</label>
<input ng-model="form2Model.second_field" />
<br />
Reset the form
</form>
http://plnkr.co/edit/x4JAeXra1bP4cQjIBld0?p=preview
You can also do:
form.fieldA = undefined;
It works great for radio buttons and checkboxes.
you can try this :
Deploy your function inside form button reset , in this way ...
<input type ="button" ng-click="Object.field1 = null; ObjectN.fieldN = null;" value="Reset" />

knockout js if statement to Display value based on boolean data type

I am trying to Display a value based on a table value of True or False. For example if the Value is True then I want it to Say Supported and If it's False then I want it to Say Not Supported! This is my html code
<p><input type="text" data-bind="value: Support" /></p>
Java script Code
$(function() {
dm.viewModel = function() {
var clients = ko.observableArray(),
selectedClient = ko.observable(),
clientChanged = function() {
$.getJSON(dm.WebServices + "/dm/get/clientinfo?client=" + encodeURIComponent(selectedClient()), function(data) {
if (data != null) {
dm.viewModel.Name(selectedClient());
dm.viewModel.Support(data[0]['Support']);
}
})
$('#divClientData').show();
},
LoadClients = function() {
$('#divClientData').hide();
$.getJSON(dm.WebServices + "/dm/get/clientlist", function(data) {
$.each(data, function(key, val) {
clients.push(val);
});
});
},
Name = ko.observable(),
Support = ko.observable(),
return {
Name: Name,
Support: Support
};
}();
ko.applyBindings(dm.viewModel);
dm.viewModel.LoadClients();
})
In this kind of case you can evaluate the property and render based on the value. Even a function can be provided inside the binding. You can use this:
<input type="text" data-bind="value: Support() ? 'Supported' : 'Not Supported'" />
You can do that with the if binding
See documentation here
Example from the docs:
<label><input type="checkbox" data-bind="checked: displayMessage" /> Display message</label>
<div data-bind="if: displayMessage">Here is a message. Astonishing.</div>
So for you
<div data-bind="if: Support">Supported</div>
<div data-bind="ifnot: Support">Not Supported</div>
Edit: The other answers suggesting using the value binding with a ternary condition are probably a better way to accomplish this. I'll keep this up as a reference, but I recommend that solution.
What you're looking for, in this case, is ko.computed().
EDITED: (Support appears to be in-use as a value from the data set)
Add a new value to your ViewModel, something like this:
IsSupported = ko.computed(function(){
if(this.Support() == true){
return "Supported";
} else {
return "Not Supported";
}
}, this)
Then, in your markup, you will have to change your data-bind just slightly:
<p><input type="text" data-bind="value: IsSupported" /></p>
Alternatively, if you don't want to change your Support field, you'll have to do something like this in your HTML, as suggested by other commenters:
<p><input type="text" data-bind="value: (Support() ? 'Supported' : 'Not Supported')" /></p>
I'd recommend the former, however, as really, you should keep that logic tucked away inside your ViewModel.
(See the KO docs for more info on computed: http://knockoutjs.com/documentation/computedObservables.html)
In my work I use KO boolean conditions like this:
<div id="bla" data-bind="visible: position != value"></div>
KO is very useful for those types of problems.

Categories

Resources