updating text using knockout computed function - mvvm

In a table I have a checkbox bound to a bool in an observable array.
If any of the checkboxes in the table are checked / unchecked I want to update some text with the total checked.
I cannot get the computed function to fire, I have tried using ko.utils.unwrapObservable on both the array and location.isSelected in the 'if' statement below, am I just using it in the wrong place?
<input type="checkbox" data-bind="checked: isSelected"/>
<span class="text-left h5 ">Total Selected:</span><span data-bind="text: totalSelected" />
self.totalSelected = ko.computed(function () {
var selected = 0;
ko.utils.arrayForEach(self.SelectedLocations(), function (location) {
if (location.isSelected == true) {
selected = (+selected) + 1;
}
});
return selected;
}, self).extend({ notify: 'always' });

One of the issues is that isSelected is treated like a variable inside the computed: location.isSelected == true. However, if you intend to bind a checkbox to it, it must be an observable.
So, I have declared a function to create the children of self.SelectedLocations as:
var locationObservable = function() {
var self = this;
self.isSelected = ko.observable(false);
};
Then, you could change the counting in the computed variable as follows:
if (loc.isSelected()) {
selected++;
}
var locationObservable = function(selected) {
var self = this;
self.isSelected = ko.observable(selected);
};
var model = function() {
var self = this;
self.SelectedLocations = ko.observableArray();
self.SelectedLocations.push(new locationObservable(false)); // Set the state of the checkbox here.
self.SelectedLocations.push(new locationObservable(true));
self.SelectedLocations.push(new locationObservable(false));
self.totalSelected = ko.computed(function() {
var selected = 0;
ko.utils.arrayForEach(self.SelectedLocations(), function(loc) {
if (loc.isSelected()) {
selected++;
}
});
return selected;
}, self);
};
var vm = new model();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: SelectedLocations">
<input type="checkbox" data-bind="checked: isSelected" />
</div>
<span class="text-left h5 ">Total Selected:</span><span data-bind="text: totalSelected" />

Related

Infinite scroll is not triggered framework7

I am using framework7.In AJAX success function I am loading first 20 people and remaining to be loaded by infinite scroll.
This is the div element
<div class="page-content infinite-scroll" data-distance="50">
<div class="searchbar-backdrop"></div>
<div class="entry_content">
<div class="members_list list searchbar-found list-block">
<ul class="row22">
</ul>
</div>
<div class="block searchbar-not-found">
<div class="block-inner">Nothing found</div>
</div>
</div>
Also this is the script
$on('pageInit', () => {
app.request({
url: base_url+api_path+'/members_directory.php',
method: "POST",
timeout: 0,
dataType: "json",
beforeSend: function () {
app.preloader.show();
},
success: function(data) {
app.preloader.hide();
console.log(data);
for(var i=0;i<20;++i){
// for(var i=0;i<data.getEmployees.length;++i){
var user_id = data.getEmployees[i].user_id;
var username = data.getEmployees[i].username;
var profile_photo = data.getEmployees[i].profile_photo;
var employee_name = data.getEmployees[i].employee_name;
var employee_id = data.getEmployees[i].employee_id;
$('.page_members_dir .members_list ul').append(
'<li class="item-content member_item"><div class="item-title member_name">'+employee_name+'</div></li>'
);
};
/*infinite*/
var loading = false;
// Last loaded index
var lastIndex = $('.list-block li').length;
// Max items to load
var maxItems = 60;
// Append items per load
var itemsPerLoad = 20;
// Attach 'infinite' event handler
$('.infinite-scroll').on('infinite', function () {
// app.attachInfiniteScroll($('.infinite-scroll'));
console.log("inside");
// Exit, if loading in progress
if (loading) return;
// Set loading flag
loading = true;
// Emulate 1s loading
setTimeout(function () {
// Reset loading flag
loading = false;
if (lastIndex >= maxItems) {
// Nothing more to load, detach infinite scroll events to prevent unnecessary loadings
app.detachInfiniteScroll($('.infinite-scroll'));
// Remove preloader
$('.infinite-scroll-preloader').remove();
return;
}
// Generate new items HTML
var html = '';
for (var i = lastIndex + 1; i <= data.getEmployees.length; i++) {
var user_id = data.getEmployees[i].user_id;
var username = data.getEmployees[i].username;
var profile_photo = data.getEmployees[i].profile_photo;
var employee_name = data.getEmployees[i].employee_name;
var employee_id = data.getEmployees[i].employee_id;
html += '<li class="item-content member_item"><div class="item-title member_name">'+employee_name+'</div></li>';
}
console.log(html);
// Append new items
$('.list-block ul').append(html);
// Update last loaded index
lastIndex = $('.list-block li').length;
}, 1000);
});
},
error: function(data) {
//console.log('error');
console.log(data);
}
});
});//pageInit
return $render;
}
I am not getting any errors in console.
The list data is getting from ajax page.
If I console after $('.infinite-scroll').on('infinite', function () nothing is displayed.
Is this right way to infinite scroll use in AJAX success function.
Please help

Autocomplete for Two <Input>

I get a code from Google to autocomplete address fields. It works well for one "Input type=text". I use it for "Departure"
Problem is when i duplicate these code to have a second "Input type=text" as "Arrival" : it only works for one of them.
I Tried to change input id="autocomparr" and some var without success.
Regards
<input id="autocompdep"
placeholder="Enter your address"
onFocus="geolocate()"
type="text"/>
<script>
var placeSearch, autocomplete;
var componentForm = {
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'
};
var options = {
componentRestrictions: {country: 'fr'}
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search predictions to
// geographical location types.
autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autocompdep'), options, {types: ['geocode']});
// Avoid paying for data that you don't need by restricting the set of
// place fields that are returned to just the address components.
autocomplete.setFields(['address_component']);
// When the user selects an address from the drop-down, populate the
// address fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details,
// and then fill-in the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle(
{center: geolocation, radius: position.coords.accuracy});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=***&libraries=places&callback=initAutocomplete"
async defer></script>````
Answer to myself (and for others that will have this problem)
It is now working for 2 input text field on the same page. Geolococate is set for France and French Riviera to be more accurate for my needs.
Regards
<html>
<head>
<script src="https://maps.googleapis.com/maps/api/js?key=****&sensor=true&libraries=places"></script>
</head>
<body>
<label for="locationTextField1">Departure</label>
<input id="locationTextField1" type="text" size="50" >
<script>
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(43.093595, 6.153839),
new google.maps.LatLng(43.467646, 6.237595));
var options = {
bounds: defaultBounds,
componentRestrictions: {country: 'fr'}
};
function initDep() {
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById('locationTextField1'), options, {types: ['geocode']});
}
google.maps.event.addDomListener(window, 'load', initDep);
</script>
<br />
<br />
<label for="locationTextField2">Arrival</label>
<input id="locationTextField2" type="text" size="50">
<script>
function initArr() {
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById('locationTextField2'), options, {types: ['geocode']});
}
google.maps.event.addDomListener(window, 'load', initArr);
</script>
<script>
</script>
</body>
</html>

Knockout event binding with condition

I want to bind some events to an element , using the knockout "event" binding
But I want all of the listed events to be bound only with a specific case.
The viewmodel:
function vm(){
var self = this;
self.isEditMode = ko.observable(false);//can be changed to true
self.events = ko.observable({
down: function () {
console.log("down")
},
up: function () {
console.log("up")
},
hover: function () {
console.log("hover")
}
});
}
and the Html:
<div style="border:1px solid red;width:50px;height:50px"
data-bind="event:{mousedown:events().down,mouseup:events().up,mouseover:events().hover}:null"></div>
<button data-bind="click:function(){isEditMode(!isEditMode())}">change </button>
I tried:
<div data-bind="event:isEditMode()?{mousedown:events().down,mouseup:events().up,mouseover:events().hover}:null"></div>
But it did not work for me.
I think the best way to do it is by using custom bindingHandlers, but I dont know how.
Thank you very much for your help!
You can simplify the the binding by moving some logic into the view model
<div style="border:1px solid red;width:50px;height:50px"
data-bind="event: {
mousedown: down,
mouseup:up,
mouseover:hover }" > </div>
and view model like this
function vm() {
var self = this;
this.isEditMode = ko.observable(true);
down = function() {
if(this.isEditMode())
{
console.log("down")
}
};
up = function() {
if(this.isEditMode())
{
console.log("up")
}
};
hover = function() {
if(this.isEditMode())
{
console.log("hover")
}
};
}
var viewModel = new vm();
ko.applyBindings(viewModel);
Another option is to place the condition in the markup itself as two separate blocks using an "if" binding to determine which ones gets shown and bound.
function vm() {
var self = this;
self.isEditMode = ko.observable(false); //can be changed to true
self.events = ko.observable({
down: function() {
console.log("down");
},
up: function() {
console.log("up");
},
hover: function() {
console.log("hover");
}
});
}
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!--ko if: isEditMode()-->
<div style="border:1px solid red;width:50px;height:50px" data-bind="event:{
mousedown:events().down,
mouseup:events().up,
mouseover:events().hover
}">
Edit Mode
</div>
<!--/ko-->
<!--ko if: !isEditMode()-->
<div style="border:1px solid red;width:50px;height:50px">
Read Only
</div>
<!--/ko-->
<button data-bind="click:function(){isEditMode(!isEditMode())}">change </button>

Populate dropdown based on logged in user created records

How do I populate a dropdown with records/documents created by the logged in user? I have a collection that many users can create schools. I want it to be that if a user that have created more than a school the dropdown should show populated with the number of schools. If it is only one, the _id of the school should just be passed to a hidden field.
This is the school collection:
if (Meteor.isServer) {
Meteor.methods({
SchoolRegister: function (newschoolname, newschoolmotto, newschholvision, sellschool, schooltype,
schoolfeerange, schoolcurriculum) {
if (!Meteor.userId()) {
throw new Meteor.Error('Not authorized', 'You must be logged in to add a new school!');
return false;
}else{
var regUsername = Meteor.user().username;
var year = new Date().getFullYear();
var month = new Date().getMonth() + 1;
var day = new Date().getDate();
var date = (month +"/" +day +"/" +year).toString();
var schoolId = NewSchoolDB.insert({
newschoolnamevar: newschoolname,
newschoolmottovar: newschoolmotto,
newschholvisionvar: newschholvision,
sellschoolvar: sellschool,
schooltypevar: schooltype,
schoolfeerangevar: schoolfeerange,
schoolcurriculumvar: schoolcurriculum,
createdAt: new Date(),
author: regUsername,
authorId: Meteor.userId()
});
return schoolId;
}
},
UserRecords: function () {
if (!Meteor.userId()) {
throw new Meteor.Error('Not authorized', 'You must be logged in to add a new school!');
return false;
}else{
//var UserDocuments = NewSchoolDB.find({authorId: Meteor.userId()}).count();
return NewSchoolDB.find({authorId: Meteor.userId()});
}
}
});
}
I'm using this to call the method:
Template.SchoolContactLayout.helpers({
UsersDocuments: function () {
return Meteor.call('UserRecords');
console.log(Meteor.call('UserRecords'));
}
});
This is the template file
{{# if UsersDocuments == 1 }}
<input type="hidden" name="schoolId" value="{{ UsersDocuments._id }}">
{{else}}
<div class="form-group">
<select class="form-control" name="schoolnames" id="schoolnames" required style="color: black; width: 100%; font-family: 'candara'">
<option value="{{UsersDocuments._id}}">{{UsersDocuments.newschoolnamevar}}</option>
</select>
</div>
{{/if}}
All in all, no error was returned and it did not work.
Meteor.call('UserRecords') is Async function you have to wait for it in a callback and use session instead.
Meteor.call('UserRecords', function(err,res){
// res will be your return value you can set your session here
});
or you can use package for example you can search atmosphere for reactive method

.each() not working in IE

I want to get all labels inside a div, the blow piece of code works in Firefox and not working IE. Any idea. Thanks in advance.
<div id='discounts'>
<label id="discount1"> discount 1</label>
<label id="discount2"> discount 2 </label>
<input type="text" id="discountmisc" value="" />
</div>
var selectLabels = {
getLabels: function() {
$('#discounts > label').each(function(index, item) {
alert(index + $(item).attr('id'));
});
}
};
selectLabels.getLabels();
Are you wrapped in DOM Ready functions? i.e.
$(function () {
var selectLabels = {
getLabels: function() {
$('#discounts > label').each(function(index, item) {
alert(index + $(item).attr('id'));
});
}
};
selectLabels.getLabels();
});
or alternately:
var selectLabels = {
getLabels: function() {
$('#discounts > label').each(function(index, item) {
alert(index + $(item).attr('id'));
});
}
};
$(selectLabels.getLabels);
or finally (because you don't care about the return value):
var selectLabels = {
getLabels: function() {
$(function () {
$('#discounts > label').each(function(index, item) {
alert(index + $(item).attr('id'));
});
});
}
};
selectLabels.getLabels();
Tell me, and if so, I'll change my answer.