Was curious if there was an easy way to split up the following code into 2 columns via Knockout and HTML. I know how to do it in the CSS but really it's just to split results 1-5 and 6-9. Here's my code. Screenshot attached as well. Thank you
<div class="item summary">
<h3> <?=l(479)?> </h3>
<div data-bind="foreach:$data.summary">
<div>
<span data-bind="text:$data.sequence + '.'"></span>
<span data-bind="text:$data.label + ':'"></span>
<span data-bind="text:$data.value"></span>
</div>
</div>
</div>
If the length isn't going to change you can duplicate the markup for each block and add a slice(). It's not the most elegant but it's probably the easiest.
<!-- ko if: summary && summary.length > 0 -->
<div data-bind="foreach: $data.summary.slice(0,5)">
...
<div data-bind="foreach: $data.summary.slice(5)">
...
<!-- /ko -->
If you want something a little more dynamic you can create a computed function that splits your array into multiple pieces and use a nested foreach instead:
function viewModel(){
var self = this;
this.summary = [
new Summary(1),
new Summary(2),
new Summary(3),
new Summary(4),
new Summary(5),
];
this.summaryBlocks = ko.computed(function(){
if(self.summary && self.summary.length > 0){
var size = self.summary.length / 2;
return [
self.summary.slice(0,size),
self.summary.slice(size)
];
}else{
return [];
}
});
}
function Summary(val){
this.sequence = 'sequence';
this.label = 'label';
this.value = val;
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="item summary">
<h3> <?=l(479)?> </h3>
<div data-bind="foreach: summaryBlocks">
<div style="display:inline-block; vertical-align:top;" data-bind="foreach:$data">
<div>
<span data-bind="text:$data.sequence + '.'"></span>
<span data-bind="text:$data.label + ':'"></span>
<span data-bind="text:$data.value"></span>
</div>
</div>
</div>
</div>
EDIT: Another snippet for dealing with a variable number of columns
function viewModel() {
var self = this;
this.columns = ko.observable(1);
this.summary = [new Summary(1), new Summary(2), new Summary(3), new Summary(4), new Summary(5), new Summary(6), new Summary(7), new Summary(8), new Summary(9)];
this.summaryBlocks = ko.pureComputed(function() {
var result = [];
for (var i = 0; i < self.columns(); i++) result.push([]);
if (self.summary && self.summary.length > 0) {
for (var i = 0; i < self.summary.length; i++) {
var col = i % self.columns();
result[col].push(self.summary[i]);
}
}
return result;
});
}
function Summary(val) {
this.sequence = 'sequence';
this.label = 'label';
this.value = val;
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
columns: <span data-bind="text: columns"></span>
<br/><input type="range" min=1 max=5 data-bind="value: columns" />
<div class="item summary">
<div data-bind="foreach: summaryBlocks">
<div style="display:inline-block; vertical-align:top;" data-bind="foreach:$data">
<div style="border: 1px dashed blue;">
<span data-bind="text:'item ' + value"></span>
</div>
</div>
</div>
</div>
Related
I have a code to search and display the exact field in any column.
I would like to get the code of partial search, such that the input value if we entre is partial, should display all the possible fields.
Eg. Search "Evaluation" in the search field should display all the possible results.
enter image description here
function doGet() {
return HtmlService.createTemplateFromFile('Index').evaluate();
}
/* PROCESS FORM */
function processForm(formObject){
var result = "";
if(formObject.searchtext){//Execute if form passes search text
result = search(formObject.searchtext);
}
return result;
}
//SEARCH FOR MATCHED CONTENTS
function search(searchtext){
var spreadsheetId = '1iG30kufq5MQpd0xmoPhPir5iA3hlz8s7vI_0EWQfg7Q'; //** CHANGE !!!
var dataRage = 'Data!A2:Y'; //** CHANGE !!!
var data = Sheets.Spreadsheets.Values.get(spreadsheetId, dataRage).values;
var ar = [];
data.forEach(function(f) {
if (~f.indexOf(searchtext)) {
ar.push(f);
}
});
return ar;
}
HTML CODE
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js" integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o" crossorigin="anonymous"></script>
<!--##JAVASCRIPT FUNCTIONS ---------------------------------------------------- -->
<script>
//PREVENT FORMS FROM SUBMITTING / PREVENT DEFAULT BEHAVIOUR
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener("load", preventFormSubmit, true);
//HANDLE FORM SUBMISSION
function handleFormSubmit(formObject) {
google.script.run.withSuccessHandler(createTable).processForm(formObject);
document.getElementById("search-form").reset();
}
//CREATE THE DATA TABLE
function createTable(dataArray) {
if(dataArray && dataArray !== undefined && dataArray.length != 0){
var result = "<table class='table table-sm table-striped' id='dtable' style='font-size:0.8em'>"+
"<thead style='white-space: nowrap'>"+
"<tr>"+ //Change table headings to match witht he Google Sheet
"<th scope='col'>MS No</th>"+
"<th scope='col'>Title</th>"+
"<th scope='col'>Status</th>"+
"<th scope='col'>Date</th>"+
"</tr>"+
"</thead>";
for(var i=0; i<dataArray.length; i++) {
result += "<tr>";
for(var j=0; j<dataArray[i].length; j++){
result += "<td>"+dataArray[i][j]+"</td>";
}
result += "</tr>";
}
result += "</table>";
var div = document.getElementById('search-results');
div.innerHTML = result;
}else{
var div = document.getElementById('search-results');
//div.empty()
div.innerHTML = "Data not found!";
}
}
</script>
<!--##JAVASCRIPT FUNCTIONS ~ END ---------------------------------------------------- -->
</head>
<body>
<div class="container">
<br>
<div class="row">
<div class="col">
<!-- ## SEARCH FORM ------------------------------------------------ -->
<form id="search-form" class="form-inline" onsubmit="handleFormSubmit(this)">
<div class="form-group mb-2">
<label for="searchtext">Search MS No</label>
</div>
<div class="form-group mx-sm-3 mb-2">
<input type="text" class="form-control" id="searchtext" name="searchtext" placeholder="Enter MS No">
</div>
<button type="submit" class="btn btn-primary mb-2">Search</button>
</form>
<!-- ## SEARCH FORM ~ END ------------------------------------------- -->
</div>
</div>
<div class="row">
<div class="col">
<!-- ## TABLE OF SEARCH RESULTS ------------------------------------------------ -->
<div id="search-results" class="table-responsive">
<!-- The Data Table is inserted here by JavaScript -->
</div>
<!-- ## TABLE OF SEARCH RESULTS ~ END ------------------------------------------------ -->
</div>
</div>
</div>
<div class="container">
<br>
<div class="row">
<div class="col">
<!-- ## SEARCH FORM ------------------------------------------------ -->
<form id="search-form" class="form-inline" onsubmit="handleFormSubmit(this)">
<div class="form-group mb-2">
<label for="searchtext">Search Title</label>
</div>
<div class="form-group mx-sm-3 mb-2">
<input type="text" class="form-control" id="searchtext" name="searchtext" placeholder="Enter Title">
</div>
<button type="submit" class="btn btn-primary mb-2">Search</button>
</form>
<!-- ## SEARCH FORM ~ END ------------------------------------------- -->
</div>
</div>
<div class="row">
<div class="col">
<!-- ## TABLE OF SEARCH RESULTS ------------------------------------------------ -->
<div id="search-results" class="table-responsive">
<!-- The Data Table is inserted here by JavaScript -->
</div>
<!-- ## TABLE OF SEARCH RESULTS ~ END ------------------------------------------------ -->
</div>
</div>
</div>
</body>
</html>
This will highlight partial substring matches in red:
function searchhilight() {
let hilite = SpreadsheetApp.newTextStyle().setBold(true).setForegroundColor('red').build();
let normal = SpreadsheetApp.newTextStyle().setBold(false).setForegroundColor('black').build();
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const sr = 2;//data start row
const rg = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn());
rg.setFontColor('black').setFontWeight('normal');//set all data to black text with no bold
const dvs = rg.getDisplayValues();
const resp = ui.prompt('Search Text', 'Enter Search String', ui.ButtonSet.OK_CANCEL);
if (resp.getSelectedButton() == ui.Button.OK) {
const st = resp.getResponseText();
dvs.forEach((r, i) => {
r.forEach((c, j) => {
let idx = '';
fidx = '';//from index
iA = [];//index array of matches
do {
idx = c.indexOf(st, fidx);
if (~idx) {
fidx = idx + st.length;
iA.push({ idxs: idx, idxe: idx + st.length })
}
} while (~idx);
let rtv = SpreadsheetApp.newRichTextValue().setText(c);
iA.forEach(obj => rtv.setTextStyle(obj.idxs, obj.idxe, hilite));
sh.getRange(i + sr, j + 1).setNumberFormat('#STRING#');
sh.getRange(i + sr, j + 1).setRichTextValue(rtv.build());
});
});
}
}
This function will also reformat every cell in the data range to plain text.
RichTextValueBuilder
RichTextValue
function search(searchtext){
var spreadsheetId = '1do6G8pScBAz0Z7GOCjbBcXPKJkhbOwlSx5BZyt6S7H8'; //** CHANGE !!!
var dataRage = 'Data!A2:Y'; //** CHANGE !!!
var data = Sheets.Spreadsheets.Values.get(spreadsheetId, dataRage).values;
var ar = [];
data.forEach(function(f) {
if (~f.toString().toLowerCase().indexOf(searchtext.toString().toLowerCase())) {
ar.push(f);
}
});
return ar;
}
I'm using AngularJS
<div ng-repeat="l in kites">
<a ng-click="findit(l.green.num)">color</a>
<span ng-if="c_{{l.green.num}} == 'y'>kites is </span>
</div>
In my controller I have
$scope.cnt=[];
$scope.findit = function(c){
$scope.cnt.push(c);
angular.forEach($scope.cnt, function(value, key){
$scope['c_'+value] = 'y' ;
})
}
My problem is, I'm not getting the span value even the conditions falls true..
means while l.green.num is 5 , c_5 ='y'. But it is not accepted in the if condition. Please help me.
Below is your solution:
var $scope;
var app = angular.module('miniapp', []);
function Ctrl($scope) {
$scope.kites = [{
'green':{
'num':1
}
}]
$scope.cnt = [];
$scope.findit = function(c) {
$scope.cnt.push(c);
angular.forEach($scope.cnt, function(value, key) {
$scope['c_' + value] = 'y';
})
}
};
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="miniapp" ng-controller="Ctrl">
<div ng-repeat="l in kites">
<a ng-click="findit(l.green.num)">color</a>
<span ng-show="{{'c_'+l.green.num}} == 'y'">kites is </span>
</div>
</div>
I'm not sure you can use brackets in ng-if conditions. This won't interpolate the value before evaluation.
What you could do is adding a "selected" value to your kite when clicked.
Example :
Controller
$scope.findit = function(c){
c.selected = true;
}
View
<div ng-repeat="l in kites">
<a ng-click="findit(l.green)">color</a>
<span ng-if="l.green.selected">kites is </span>
</div>
I'm trying to write a small Jquery program with an autocomplete searchform. I would like the user to be able to add items from the autocomplete form to a sort of shopping list. I have difficulties finding a way to calculate and update the total price of all added products, I tried to store the prices, which are in suggestion.data and tot add it recusively to a total as in
total = total + suggestion.data, but this seems not to be the way. Could someone help me to produce and display this total? My jQuery code is as follows:
var Price = {};
var Name = {};
var Totaal = {};
$(function () {
var currencies = [
{value:'TEXT-STRING',data:'5.50)'},
{value:'TEXT-STRING2',data:'3.10)'},
];
$('#autocomplete').autocomplete({
lookup: currencies,
onSelect: function (suggestion) {
Price.fff = suggestion.data;
Name.fff = suggestion.value;
},
});
});
function addListItem() {
var write2 = Price.fff;
var write = $('#autocomplete').val();
var list = $('#itemList');
var item = $('<li><span class="list">' + write + '</span><button
class="delete">X</button></li>');
var autocomplete = $("#autocomplete");
if (write.length === 0 || write.length > 88) {
return false;
}
if (write == Name.fff) {
list.append(item);
list2.append(item2);
$(autocomplete).val('');
}
$(autocomplete).val('');
}
function deleteItem() {
$(this).parent().remove();
}
$(function () {
var add = $('#addItem');
var autocomplete = $('#autocomplete');
var list = $('#itemList');
add.on('click', addListItem);
list.on('click', '.delete', deleteItem);
autocomplete.on('keypress', function (e) {
if (e.which == 13) {
addListItem();
}
}
And my HTML looks like:
<body>
<div id="box-1"> </div>
<div id="box-2"> </div>
<div id="page">
<h1>Shop</h1>
</div>
<div id="main">
<div id="top">
<div id="form">
<div id="searchfield">
<input type="text" id="autocomplete" name="currency" class="biginput" placeholder="ADD ITEMS BELOW">
<input type="submit" id="addItem" value="+">
</div>
<div class="line"> </div>
</div>
<div id="bottom">
<div class="items">
<ul id="itemList">
</ul>
</div>
</div>
Keep added item in array with prices, so you can recalculate total at any time. Don't forget to remove it from array when removing from the list.
Don't keep data in DOM, use DOM only to display info that is in your model.
How can i set pagination for google map cluster infowindow. Iam using gmap3.
$(this).gmap3(
{clear:"overlay"},
{
overlay:{
latLng: cluster.main.getPosition(),
options:{
content: eventlist,
offset: {
x:-46,
y:-73
}
}
}
});
iam having eventlist as
eventlist ='
<div class='infobubble infobulle drive'>
<div class='infobubble_wrap'>
<div class='infobubble_event_text'>
<div class='infobubble_event_names'>arts</div>
<div class='infobubble_date'>Feb 06</div>
<div class='infobubble_event_venue'>Bulldog</div>
</div>
<div class='infobubble_event_image'><img src=thumb.jpg></img></div>
<div class='clear'></div>
</div>
</div>
<div class='infobubble infobulle drive'>
<div class='infobubble_wrap'>
<div class='infobubble_event_text'>
<div class='infobubble_event_names'>Comedy</div>
<div class='infobubble_date'>Jun 13</div>
<div class='infobubble_event_venue'>Our Little Comedy Theater</div>
</div>
<div class='infobubble_event_image'><img src=eventdefault_thumb.jpg></img></div>
<div class='clear'></div>
</div>
</div>
';
How can i get my event 'arts' and 'comedy' in two pages of the infowindow of my gmap3.I am new to gmap3?
I solved it.I wrote a function to get the details of each marker individually and called the function to display the details in the infowindow.Initially i was set to zero.
function infowindowhtml(i)
{
context = global_context_menu;
var markernum = context.length;
var first = next = '';
if (i == 0) {
first = '';
} else {
var j = i - 1;
first = "<div class='prev_info' onclick='showinfodetails(" + j + ");'><<</div>";
}
if (i == (markernum - 1)) {
next = '';
} else {
var j = i + 1;
next = "<div class='next_info' onclick='showinfodetails(" + j + ");'>>></div>";
}
var pagination = '<div>' + first + '<div class="middle_section">' + (i + 1) + '</div>' + next + '</div>';
var datastring = context[i].data;
eventlist=" <div class='infobubble infobubble_cluster infobulle"+(datastring ? " drive" : "") + "'><div class='infobubble_wrap infobubble_wrap_cluster'><div class='infobubble_event_text'><div class='infobubble_event_names'>Comedy</div>" +"<div class='infobubble_date'>Jun 13</div>" +"<div class='infobubble_event_venue'>Our Little</div>" +"<div class='clear'></div>" + pagination + "</div></div>";
return eventlist;
}
in the function showinfodetails i wrote
function showinfodetails(i) {
eventlist = infowindowhtml(i);
$('.infobubble_content').html(eventlist);
}
so on each click on next j will get increased and for each click on prev j will get decreased.This will be passed to the showinfodetails function and the details of that marker will be displayed.
We get context from
mouseover: function(cluster, event, context) {
global_context_menu = context.data.markers;
}
global_context_menu globally declared.
It seems as if the infobubble constructor was treated differently than I remember. I'd do:
var arts_tab="
<div class='infobubble infobulle drive'>
<div class='infobubble_wrap'>
<div class='infobubble_event_text'>
<div class='infobubble_event_names'>arts</div>
<div class='infobubble_date'>Feb 06</div>
<div class='infobubble_event_venue'>Bulldog</div>
</div>
<div class='infobubble_event_image'>
<img src=thumb.jpg></img>
</div>
<div class='clear'></div>
</div>
</div>";
var comedy_tab="<div class='infobubble infobulle drive'>
<div class='infobubble_wrap'>
<div class='infobubble_event_text'>
<div class='infobubble_event_names'>Comedy</div>
<div class='infobubble_date'>Jun 13</div>
<div class='infobubble_event_venue'>Our Little Comedy Theater</div>
</div>
<div class='infobubble_event_image'>
<img src=eventdefault_thumb.jpg></img>
</div>
<div class='clear'></div>
</div>
</div>";
infoBubble = new InfoBubble({
maxWidth: 300
});
infoBubble.addTab('Arts', arts_tab);
infoBubble.addTab('Comedy', comedy_tab);
But if you're using gmaps's wrappers it might be called differently.
This is my knockoutjs code:
$(function () {
function QuizViewModel() {
var self = this;
self.previousQuestions = ko.observableArray([]);
self.questions = ko.observableArray([]);
self.thisQuestion = ko.observable();
self.questionNumber = ko.observable(0);
self.arrPreviousNumbers = ko.observableArray([]);
self.selectedAnswers = ko.observableArray();
self.loadQuestions = function () {
$('#allQuestions').fadeOut('fast');
$.getJSON('./json/quiz.json', function (data) {
$.each(data, function (i, q) {
self.questions.push(q);
});
});
$('#questions').fadeIn('fast');
}
self.getQuestion = function (number) {
$.getJSON('./json/quiz.json', function (data) {
$.each(data, function (i, q) {
if (number == i) {
self.thisQuestion(q);
}
});
});
}
self.nextQuestion = function () {
if (self.arrPreviousNumbers().length == 15) {
$('#allQuestions').fadeIn('fast');
$('#questions').fadeOut('fast');
} else {
var randomnumber = Math.floor(Math.random() * 15)
if (self.arrPreviousNumbers.indexOf(randomnumber) == -1) {
if (self.arrPreviousNumbers().length > 0) {
self.thisQuestion().selectedAnswers = self.selectedAnswers();
alert(self.thisQuestion().selectedAnswers[0]);
self.previousQuestions.push(self.thisQuestion());
self.selectedAnswers.removeAll();
}
self.arrPreviousNumbers.push(randomnumber);
self.getQuestion(randomnumber);
var previousNumber = self.questionNumber();
self.questionNumber(previousNumber + 1);
} else {
self.nextQuestion();
}
}
}
$('#allQuestions').fadeOut('fast');
self.nextQuestion();
}
ko.applyBindings(new QuizViewModel());
});
and this is my html5 page:
...
<div id ="questions" data-bind="with: thisQuestion">
<h2>Question</h2>
<p data-bind="text: question"></p>
<div class="answers"data-bind="foreach: answers">
<p data-bind="with: $data">
<input type="radio" data-bind="checked: $root.selectedAnswers, value: title"/>
<span data-bind="text: title"></span>
</p>
</div>
<p data-bind="text: info"></p>
<button data-bind="click: $root.nextQuestion">
blabla
</button>
</div>
<div id ="allQuestions">
<h2>Correction</h2>
<div class ="answers">
<div data-bind="foreach: previousQuestions">
<p data-bind="text: question"></p>
<div data-bind="foreach: selectedAnswers">
<span data-bind="text: $data"></span>
</div>
<div data-bind="foreach: answers">
<p data-bind="with: $data">
<input type="radio" data-bind="value: title, checked: status=='true'" disabled="true"/>
<span data-bind="text: title"> </span><span data-bind="checked: $parent.selectedAnswers"></span><!--<span data-bind="text: $parent.selectedAnswers"> </span>-->
</p>
</div>
</div>
</div>
</div>
<script type='text/javascript' src='js/libs/knockout-2.0.0.js'></script>
<script defer src="js/plugins.js"></script>
<script src="js/quiz.js"></script>
...
the last part in my kojs file: ko.applyBindings(new QuizViewModel()); has an error: Uncaught ReferenceError: ko is not defined. Can someone help me with this ?
I moved this to a jsfiddle here http://jsfiddle.net/johnpapa/V7Hrt/
Note that I have seen odd errors like this when my javascript references were in the "wrong" order. You may want to move your Knockout reference after your jQuery reference (if you use that) and also make sure you custom script files that refer to Knockout are loaded after KO.