I am using knockout js with bootstrap DateTimePicker from here http://eonasdan.github.io/bootstrap-datetimepicker/Installing/#knockout, everything works fine, but the problem is at submission stage that is when I submit the form I can't get the updated value of datetimepicker input field:
Here is my working HTML:
<div data-bind="foreach: params">
<input class="form-control" type="text" data-bind="attr: { name: label}, value: value, dateTimePicker: 'date_field'" />
<button data-bind="event: { click: addParameters } , attr: { class: btn btn-success', href: 'javascript:void(0)'}">Save Settings</button>
</div>
Here is my ViewModel:
function viewModel(data)
{
// date field observable
self.date_field = ko.observable(new Date('2012/12/12'));
// Observable to track the html form
self.params = ko.observableArray();
// when user click by Save Settings button
self.addParameters = function(options)
{
var dataparams = self.params();
console.log(dataparams);
}
}
ko.applyBindings(new viewModel);
Could anyone please guide me what I am doing wrong? Thank you an advance.
I use this datepicker as well. The module doesn't send the update event to knockout observables. I do this for each datepicker after initializing it:
picker.on('dp.change', function (e) {
date_field(e.date);
});
I had the same problem in knockout.js v 3.3.1
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).data('DateTimePicker').setDate(makeStringStandard(value));
$(element).on('dp.change', function (e) {
debugger;
valueAccessor(e.date);
});
}
Related
could you please tell me how to open bootstrap datepicker in angular js .I have two field name and data.I want to open datepicker on click of date field .here is my code
http://plnkr.co/edit/elrOTfEOMmUkPYGmKTdW?p=preview
var app = angular.module('plunker', ['angularMoment']);
app.controller('MainCtrl', function($scope, moment) {
$scope.name = 'World';
console.log(moment)
var d = new Date(613938600000);
$scope.c = {
name: {
name: 'abc'
},
date: {
name: moment(d).format('DD-MMM-YYYY')
}
};
$scope.onclick = function() {
if (!moment($scope.c.date.name).isValid()) {
alert('Everything is wrong dude');
} else {
alert('Everything goood');
}
}
});
any update
Please replace the input field with this.
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="x.name" is-open="open" data-ng-click="open = true" datepicker-options="dateOptions" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />
I have added these directives at the input
<... is-open="open" data-ng-click="open = true" ...>
Add these to directives to your datepicker inputs.
This is should work. Try in your plunker
Not sure if you want to go this route, but my suggestion would be to leverage UI Bootstrap. Opening a datepicker is made fairly easy with this library, along with other bootstrap functionality without having to port bootstrap entirely.
I trying to develop a little app.
I have made a login view and it works, I am able to bind the data written by the user. After this, I show two select box. The first is binded with a list (correctly) and the second has to be bind with a list populated after a value is selected from the first.
I'm not able to read the value selected from the first list.
I have this html:
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">Rete</div>
<div class="dx-field-value"
data-bind="dxLookup: { dataSource: is_retistiSource, value: rete, displayExpr: 'NOME', title: 'Retisti associati', placeholder: 'Selezionare rete',
onSelectionChanged:setRete }" />
</div>
<div class="dx-field">
<div class="dx-field-label">Impianto</div>
<div class="dx-field-value"
data-bind="dxLookup: { dataSource: is_impiantiSource, value: impianto, displayExpr: 'NOME', title: 'Impianti associati', placeholder: 'Selezionare impianto' }" />
</div>
</div>
and this javascript:
OverviewAPP.afterLogin = function (params) {
var isReady = $.Deferred();
var viewModel = {
rete: ko.observable(""),
impianto: ko.observable(""),
is_retistiSource: OverviewAPP.listaReti,
is_impiantiSource: OverviewAPP.listaImpianti,
setRete: function () {
console.log(viewModel.rete);
var nRetisti = OverviewAPP.listaRetiImpianti.length;
for (i = 0; i < nRetisti; i++) {
if (OverviewAPP.listaRetiImpianti[i]["retista"]["NOME"] == this.rete)
{
OverviewAPP.listaImpianti = listaRetiImpianti[i]["listaImpianti"];
break;
}
}
is_impiantiSource = OverviewAPP.listaImpianti;
},
close: function () {
OverviewAPP.app.back();
}
};
return viewModel;
};
In the setRete function, with the line "console.log(viewModel.rete);", I see this output:
d(){if(0<arguments.length)return d.Wa(c,arguments[0])&&(d.X(),c=arguments[0],d.W()),this;a.k.Ob(d);return c}
Why? How can I bind and read the selected value?
UPDATE: I've done in this way, it works:
setRete: function (e) {
OverviewAPP.IDrete = e.value;
var nRetisti = OverviewAPP.listaRetiImpianti.length;
for (i = 0; i < nRetisti; i++) {
if (OverviewAPP.listaRetiImpianti[i]["retista"]["NOME"] == e.value["NOME"])
{
OverviewAPP.listaImpianti = OverviewAPP.listaRetiImpianti[i]["listaImpianti"];
break;
}
}
//ko.applyBindings(viewModel);
},
But I don't know how update my second list, "is_impiantiSource".
Observables are functions. That's why you get a function in the console. Call the rete function to get its value:
viewmodel.rete();
Also see the Knockout: Observables help topic that describes this (under "Reading and writing observables").
This is how you can obtain a new value. Then, you need to update the dependent lookup data source. For this, make the is_impiantiSource property an observable array:
is_impiantiSource: ko.observableArray(OverviewAPP.listaImpianti),
After this, modify it in setRene like:
viewModel.is_impiantiSource(OverviewAPP.listaImpianti)
Also see Observable Arrays for how to work with arrays in Knockout
In my current project I'm using this calendar.js javascript library together AngularJS.
I'm bind calendar.js widget and AngularJS ng-model to my input field in this way:
<input class="vDateField" type="text" ng-model="date" ng-init="date='{{ form.date.value | default_if_none:"" }}'"
vDateField is the class that bing calendars and {{ }} are backend template engine tags.
My problem is when I select the date with calendarjs picker, my input fied is updated but ng-model "date" doesn't.
Is there any solutions without modify calendarjs widget??
Here the rendered input tag:
<div>
<label class="required" for="id_date">Data contratto</label>
<input class="vDateField ng-pristine ng-valid ng-touched" type="text" ng-model="date" ng-init="date=''" name="date" placeholder="02/02/2015">
<span class="datetimeshortcuts"> Oggi | <img src="/static/admin/img/icon_calendar.gif" alt="Calendario"></span>
</div>
Here the javascript sources of the picker. DateTimeShortCuts.js contains the code for display mini link to setup current date time to my input field and a mini calendar icon to open up calendar widget.
I register the widget by adding class="vDateField" to my input tag as shown below.
DateTimeShortCuts.js
calendar.js
Had this same problem, solved it like this (example with jquery datepicker)
$('#myDateInput').datepicker({
dateFormat: 'yy-mm-dd',
onSelect: function (dateText) {
updateDate(dateText);
},
onClose: function (dateText) {
updateDate(dateText);
}
});
function updateDate(dateText) {
var date = angular.element('#myDateInput').val();
angular.element('#myDateInput').val(date);
if (dateText) {
$scope.object.date = dateText;
}
$scope.object.dateReadable = date;
$scope.safeApply(function (childScope) { });
}
// safely apply changes to the $scope
$scope.safeApply = function (fn) {
var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if (fn && (typeof (fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
Set date on page load
$("#myDateInput").datepicker("setDate", $scope.dateReadable);
HTML
<div class="form-group">
<label for="date" class="control-label">Date:</label>
<input type="date" name="date" id="myDateInput" class="form-control" />
</div>
The important thing here is that I use SafeApply to apply the changes to the $scope. Hope the example helps even tough it's a working sample for me.
Why do I have to do this? Well angular isn't aware of what happens outside its $scope, in this case jQUery. So we will have to tell it manually.
EDIT:
It might be a bit confusing with date and dateReadable but ignore the details and look at the safeApply that binds the changes to the $scope.
It will be helpful if you ask your question in more detail. However have you tried ng-init="date='{{ form.date.value | default_if_none:"" }}'" before input element?
I've solved with this directive and by set $('widget_element').trigger('change') on a callback function of 3d-part widget.
In this case, the widget is default Django Admin date widget.
contract_form_app.directive('vDateField', [function() {
return {
require: "ngModel",
restrict: "A",
link: function(scope, element, attrs, ngModelCtrl) {
element.addClass("vDateField");
var updateModel = function (dateText) {
scope.$apply(function () {
ngModelCtrl.$setViewValue(dateText);
});
};
django.jQuery('input').on('change', function() {
updateModel(element.val());
});
}
};
}]);
I have a list of Admins with a check box. I want to be able to select only one Admin.
HTML:
<tbody data-bind="foreach: people">
<tr>
<td>
<input type="checkbox" data-bind="attr: { value: id }, checked: $root.selectedAdmin">
<span data-bind="text: name"/>
</td>
</tr>
</tbody
JS:
function Admin(id, name) {
this.id = id;
this.name = name;
}
var listOfAdmin = [
new Admin(10, 'Wendy'),
new Admin(20, 'Rishi'),
new Admin(30, 'Christian')];
var viewModel = {
people: ko.observableArray(listOfAdmin),
selectedAdmin: ko.observableArray()
};
ko.applyBindings(viewModel);
For Example if Admin id 10 is selected the other admins should be deselected.
Is that Possible to do with Knockout?
You should really use radio buttons if you only want to allow multiple selection.
However if you still want to use checkboxes then on solution would be to combine the checked and the click binding:
Use the checked to check only when the current id equal to the selectedAdmin property and use the click binding to set the selectedAdmin.
So you HTML should look like this:
<input type="checkbox" data-bind="attr: { value: id },
checked: $root.selectedAdmin() == id,
click: $parent.select.bind($parent)" />
And in your view model you just need to implement the select function:
var viewModel = {
people: ko.observableArray(listOfAdmin),
selectedAdmin: ko.observableArray(),
select: function(data) {
this.selectedAdmin(data.id);
return true;
}
};
Demo JSFiddle.
Notes:
the return true; at the end of the select function. This is required to trigger the browser default behavior in this case to check the checkbox.
the .bind($parent) is needed to set the this in the select function to be the "parent" viewModel object.
This example of knockout js works so when you edit a field and press TAB, the viewmodel data and hence the text below the fields is updated.
How can I change this code so that the viewmodel data is updated every keypress?
<!doctype html>
<html>
<title>knockout js</title>
<head>
<script type="text/javascript" src="js/knockout-1.1.1.debug.js"></script>
<script type="text/javascript">
window.onload= function() {
var viewModel = {
firstName : ko.observable("Jim"),
lastName : ko.observable("Smith")
};
viewModel.fullName = ko.dependentObservable(function () {
return viewModel.firstName() + " " + viewModel.lastName();
});
ko.applyBindings(viewModel);
}
</script>
</head>
<body>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>
</html>
<body>
<p>First name: <input data-bind="value: firstName, valueUpdate: 'afterkeydown'" /></p>
<p>Last name: <input data-bind="value: lastName, valueUpdate: 'afterkeydown'" /></p>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>
From the documentation
Additional parameters
valueUpdate
If your binding also includes a parameter called valueUpdate, this
defines which browser event KO should use to detect changes. The
following string values are the most commonly useful choices:
"change" (default) - updates your view model when the user
moves the focus to a different control, or in the case of
elements, immediately after any change
"keyup" - updates your view model when the user releases a key
"keypress" - updates your view model when the user has typed a
key. Unlike keyup, this updates repeatedly while the user holds a key
down
"afterkeydown" - updates your view model as soon as the user
begins typing a character. This works by catching the browser’s
keydown event and handling the event asynchronously.
Of these options, "afterkeydown" is the best choice if you want to
keep your view model updated in real-time.
In version 3.2 you can simply use textinput binding.:
<input data-bind="textInput: userName" />
It does two important things:
make immediate updates
handles browser differences for cut, drag, autocomplete ...
So no need for additional modules, custom controls and other things.
If you want it to do updates on afterkeydown "by default," you could inject the valueUpdate binding in the value binding handler. Just supply a new allBindingsAccessor for the handler to use that includes afterkeydown.
(function () {
var valueHandler = ko.bindingHandlers.value;
var getInjectValueUpdate = function (allBindingsAccessor) {
var AFTERKEYDOWN = 'afterkeydown';
return function () {
var allBindings = ko.utils.extend({}, allBindingsAccessor()),
valueUpdate = allBindings.valueUpdate;
if (valueUpdate === undefined) {
return ko.utils.extend(allBindings, { valueUpdate: AFTERKEYDOWN });
} else if (typeof valueUpdate === 'string' && valueUpdate !== AFTERKEYDOWN) {
return ko.utils.extend(allBindings, { valueUpdate: [valueUpdate, AFTERKEYDOWN] });
} else if (typeof valueUpdate === 'array' && ko.utils.arrayIndexOf(valueUpdate, AFTERKEYDOWN) === -1) {
valueUpdate = ko.utils.arrayPushAll([AFTERKEYDOWN], valueUpdate);
return ko.utils.extend(allBindings, {valueUpdate: valueUpdate});
}
return allBindings;
};
};
ko.bindingHandlers.value = {
// only needed for init
'init': function (element, valueAccessor, allBindingsAccessor) {
allBindingsAccessor = getInjectValueUpdate(allBindingsAccessor);
return valueHandler.init(element, valueAccessor, allBindingsAccessor);
},
'update': valueHandler.update
};
} ());
If you're not comfortable "overriding" the value binding, you could give the overriding custom binding a different name and use that binding handler.
ko.bindingHandlers.realtimeValue = { 'init':..., 'update':... };
demo
A solution like this would be suitable for Knockout version 2.x. The Knockout team has fleshed out a more complete binding for text-like inputs through the textInput binding in Knockout version 3 and up. It was designed to handle all text input methods for text inputs and textarea. It will even handle real time updating which effectively renders this approach obsolete.
Jeff Mercado's answer is fantastic, but unfortunately broken with Knockout 3.
But I found the answer suggested by the ko devs while working through Knockout 3 changes. See the bottom comments at https://github.com/knockout/knockout/pull/932. Their code:
//automatically add valueUpdate="afterkeydown" on every value binding
(function () {
var getInjectValueUpdate = function (allBindings) {
return {
has: function (bindingKey) {
return (bindingKey == 'valueUpdate') || allBindings.has(bindingKey);
},
get: function (bindingKey) {
var binding = allBindings.get(bindingKey);
if (bindingKey == 'valueUpdate') {
binding = binding ? [].concat(binding, 'afterkeydown') : 'afterkeydown';
}
return binding;
}
};
};
var valueInitHandler = ko.bindingHandlers.value.init;
ko.bindingHandlers.value.init = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
return valueInitHandler(element, valueAccessor, getInjectValueUpdate(allBindings), viewModel, bindingContext);
};
}());
http://jsfiddle.net/mbest/GKJnt/
Edit
Ko 3.2.0 now has a more complete solution with the new "textInput" binding. See SalvidorDali's answer