how to iterate over objects of objects and display in mustache template - node.js

I have the following data structure
{ 'the-subtle-art-of-not-givng-a-f': { "id: "test", "title": "test"}, "the-hamset": {"id": "test", "title": "test"}}
how can I display the title in a mustache template?

jQuery
HTML:
<div id="output">
</div>
<script type="text/template" id="titles">
<ul>
{{#titles}}
<li>{{title}}</li>
{{/titles}}
</ul>
</script>
JS:
$(document).ready(function(){
const template = $('#titles').html();
const output = $('#output');
const object = {
'the-subtle-art-of-not-givng-a-f': {
"id": "test", "title": "test1"
},
"the-hamset": {
"id": "test", "title": "test2"
}
};
let titles = [];
for (const property in object) {
if (object.hasOwnProperty(property)) {
if (object[property].title) {
titles.push( { title: object[property].title });
}
}
}
const data = { titles };
const result = Mustache.render(template, data);
output.append(result);
});
Example
Vanilla JS:
HTML:
<div id="output">
</div>
<script type="text/template" id="titles">
<ul>
{{#titles}}
<li>{{title}}</li>
{{/titles}}
</ul>
</script>
JS:
const callback = function() {
const template = document.querySelector('#titles').innerHTML;
const output = document.querySelector('#output');
const object = {
'the-subtle-art-of-not-givng-a-f': {
"id": "test", "title": "test1"
},
"the-hamset": {
"id": "test", "title": "test2"
}
};
let titles = [];
for (const property in object) {
if (object.hasOwnProperty(property)) {
if (object[property].title) {
titles.push( { title: object[property].title });
}
}
}
const data = { titles };
const result = Mustache.render(template, data);
output.innerHTML = output.innerHTML + result;
};
if (
document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
Example

Related

Apollo Server Resolver not returning all data (returned data is not complete)

My setup: Apollo server with express.js
MongoDB with Mongoose
My problem: When I run a query, my resolver is not fetching all of the data, just part of it.
Here is my resolver code:
getMarsContentForScreen: async (_, { screen, token }, context) => {
if (!context.screen) return {};
console.log(screen, token);
const contentOut = {};
const screenExist = await MarsScreen.findOne({
name: screen,
token: token,
});
if (screenExist) {
const content = await MarsContent.findOne({
screens: { $in: screenExist.id },
});
if (content) {
// ID
contentOut.id = content.id;
// NAME
contentOut.name = content.name;
// ENTRY
contentOut.entry = [{ entryVideos: [] }];
content.entry.map(async (val) => {
let file = await Asset.findById(val, 'uri');
if (file && file.uri) {
contentOut.entry[0].entryVideos.push(
file.uri.split('/').slice(-1)[0]
);
}
});
// EQUIPMENT
contentOut.equipment = [];
content.equipment.map(async (val) => {
let equipment = await MarsEquipment.findById(
val.id,
'name thumbnail background'
);
if (equipment) {
contentOut.equipment.push({
id: val.id,
name: equipment.name,
panelImage: equipment.thumbnail.split('/').slice(-1)[0],
productImage: equipment.background.split('/').slice(-1)[0],
});
}
});
// EXERCISES
contentOut.exercises = [];
content.exercises.map(async (val, index) => {
contentOut.exercises.push({
equipment: val.equipment,
content: [],
});
val.content.map(async (valC) => {
let exercise = await MarsExercise.findById(
valC.id,
'name level text thumbnail video'
);
if (exercise) {
let instructions = [];
for (const [key, value] of Object.entries(
JSON.parse(exercise.text)
)) {
instructions.push(value);
}
contentOut.exercises[index].content.push({
id: valC.id,
position: valC.position,
name: exercise.name,
level: exercise.level,
instructions: instructions,
panelImage: exercise.thumbnail.split('/').slice(-1)[0],
programVideo: exercise.video.split('/').slice(-1)[0],
});
}
});
});
// OPTIONS
contentOut.options = [];
let bgImage = await Asset.findById(content.options[0].bgImage, 'uri');
bgImage = bgImage.uri.split('/').slice(-1)[0];
contentOut.options = [
{
bgImage: bgImage,
cards: [],
},
];
content.options[0].cards.map(async (val, index) => {
let cardImg = await Asset.findById(val.panelImage, 'uri');
if (cardImg) {
contentOut.options[0].cards.push({
name: val.name,
panelImage: cardImg.uri.split('/').slice(-1)[0],
subheading: val.subheading,
action: val.action,
});
if (val.overlay) {
contentOut.options[0].cards[index].overlay = val.overlay;
}
if (
val.externalApp &&
val.externalApp.appName &&
val.externalApp.playStoreId
) {
contentOut.options[0].cards[index].externalApp = {
appName: val.externalApp.appName,
playStoreId: val.externalApp.playStoreId,
};
}
}
});
// WORKOUTS
contentOut.workouts = [];
content.workouts.map(async (val) => {
let workout = await MarsWorkout.findById(
val.id,
'name thumbnail video text required'
);
if (workout) {
contentOut.workouts.push({
id: val.id,
position: val.position,
name: workout.name,
panelImage: workout.thumbnail.split('/').slice(-1)[0],
programVideo: workout.video.split('/').slice(-1)[0],
instructions: workout.text,
required: workout.required,
});
}
});
// FILES
contentOut.files = [];
content.files.map(async (val) => {
let file = await Asset.findById(val, 'uri updated_at');
if (file) {
contentOut.files.push({
id: val,
uri: file.uri,
filename: file.uri.split('/').slice(-1)[0],
timestamp: file.updated_at,
});
}
});
return contentOut;
} else {
return {};
}
}
}
Here is the query I'm running in the Playground:
query {
getMarsContentForScreen(screen: "GS123123123123", token: "token-here") {
id
name
entry {
entryVideos
}
equipment {
id
name
position
panelImage
productImage
}
exercises {
equipment
content {
id
position
name
level
panelImage
programVideo
instructions
}
}
options {
bgImage
cards {
name
panelImage
subheading
action
overlay
externalApp {
appName
playStoreId
}
}
}
workouts {
id
position
name
panelImage
programVideo
required
instructions
}
files {
id
filename
uri
timestamp
}
}
}
And here is the output of what I'm getting:
{
"data": {
"getMarsContentForScreen": {
"id": "6203d63f54a0bd82832288c5",
"name": "sdfgsdfg",
"entry": [
{
"entryVideos": [
"6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"9b1628af-e69e-4d0e-9d53-b472a963a1ec.mp4",
"830b0258-70f1-4206-b07b-fb60508e33c5.mp4"
]
}
],
"equipment": [
{
"id": "62025aa4237005069c569d63",
"name": "dsfgsdfg",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
},
{
"id": "62025afa237005069c569d99",
"name": "sdfgsdfgsdfgsdfgsdfgsdfgweqqwerwr",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
},
{
"id": "62025af4237005069c569d92",
"name": "sdfgsdfgsdfgdsf",
"position": null,
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"productImage": "da245241-335e-4021-929c-d177a851c2ea.jpg"
}
],
"exercises": [
{
"equipment": "dsfgsdfg",
"content": [
{
"id": "62025b27237005069c569dc0",
"position": 1,
"name": "sdfgsdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfgsdfg",
"sdfgsdfg",
"sdfg"
]
},
{
"id": "62025b30237005069c569dc7",
"position": 2,
"name": "sdfgsdfgsdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfgsdfg",
"sdfg",
"hgfjgh"
]
}
]
},
{
"equipment": "sdfgsdfgsdfgdsf",
"content": [
{
"id": "62025b80237005069c569e13",
"position": 1,
"name": "sdfg",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"sdfg",
"sdfgsdfg",
"sdfgdf"
]
}
]
},
{
"equipment": "sdfgsdfgsdfgsdfgsdfgsdfgweqqwerwr",
"content": [
{
"id": "62025b88237005069c569e1a",
"position": 1,
"name": "uitytyui",
"level": "Intermediate",
"panelImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"programVideo": "6bb847e5-8b9a-477b-bfd1-68a109b3c707.mp4",
"instructions": [
"ytuityui",
"tyui",
"tyuityuityui"
]
}
]
}
],
"options": [
{
"bgImage": "da245241-335e-4021-929c-d177a851c2ea.jpg",
"cards": []
}
],
"workouts": [],
"files": []
}
}
}
As you can see, everything from "options" : [{"cards"}] is empty, but it shouldn't be, as there is the data in the database for it.
What is even more interesting, is that when I console.log the contentOut object inside the last .map function (content.files.map()) I'm getting the full response.
Basically it looks like my resolver is returning the content before all of it is gathered.
If I add some if statement to check if all of my content is in the contentOut object, I'm getting empty response, just like the resolver couldn't be bothered to wait for all of the content...
Any ideas?
Many thanks in advance!
Ok, so after more Googling and fighting with it, I've re-write the whole code and use Promise.all for each part of the function in order to make sure that it will wait for the outcome of each await, before returning the value.
Now the code looks like this:
getMarsContentForScreen: async (_, { screen, token }, context) => {
if (!context.screen) return {};
console.log(screen, token);
const contentOut = {};
const screenExist = await MarsScreen.findOne({
name: screen,
token: token,
});
const getEntryVideos = async (content) => {
let result = [{ entryVideos: [] }];
await Asset.find({ _id: { $in: content } }, 'uri').then((response) =>
response.map((val) => {
result[0].entryVideos.push(val.uri.split('/').slice(-1)[0]);
})
);
return result;
};
const getEquipment = async (content) => {
let result = [];
const ids = content.map((val) => {
return val.id;
});
await MarsEquipment.find(
{ _id: { $in: ids } },
'id name thumbnail background'
).then((response) =>
response.map((val) => {
result.push({
id: val.id,
name: val.name,
panelImage: val.thumbnail.split('/').slice(-1)[0],
productImage: val.background.split('/').slice(-1)[0],
});
})
);
return result;
};
const getExercises = async (content) => {
let result = [];
const ids = [].concat(
...content.map((val) => {
result.push({
equipment: val.equipment,
content: [],
});
return val.content.map((valC) => {
return valC.id;
});
})
);
await MarsExercise.find(
{ _id: { $in: ids } },
'id name level text thumbnail video product'
).then((response) =>
response.map((exer) => {
let instructions = [];
const index = result.indexOf(
result.find((equip) => equip.equipment === exer.product)
);
for (const [key, value] of Object.entries(JSON.parse(exer.text))) {
instructions.push(value);
}
result[index].content.push({
id: exer.id,
position: exer.position,
name: exer.name,
level: exer.level,
instructions: instructions,
panelImage: exer.thumbnail.split('/').slice(-1)[0],
programVideo: exer.video.split('/').slice(-1)[0],
});
})
);
return result;
};
const getOptions = async (content) => {
let result = content;
const ids = content[0].cards.map((val) => {
return val.panelImage;
});
await Asset.findById(content[0].bgImage, 'uri').then((response) => {
result[0].bgImage = response.uri.split('/').slice(-1)[0];
});
await Asset.find({ _id: { $in: ids } }, 'id uri').then((response) =>
response.map((val) => {
let index = result[0].cards.indexOf(
result[0].cards.find((card) => card.panelImage === val.id)
);
result[0].cards[index].panelImage = val.uri.split('/').slice(-1)[0];
})
);
return result;
};
const getWorkouts = async (content) => {
let result = content;
const ids = content.map((val) => {
return val.id;
});
await MarsWorkout.find(
{ _id: { $in: ids } },
'id name thumbnail video text required'
).then((response) => {
response.map((val) => {
let index = result.indexOf(
result.find((work) => work.id === val.id)
);
result[index].panelImage = val.thumbnail.split('/').slice(-1)[0];
result[index].programVideo = val.video.split('/').slice(-1)[0];
});
});
return result;
};
const getFiles = async (content) => {
let result = [];
await Asset.find({ _id: { $in: content } }, 'id uri updated_at').then(
(response) => {
response.map((val) => {
result.push({
id: val.id,
uri: val.uri,
filename: val.uri.split('/').slice(-1)[0],
timestamp: val.updated_at,
});
});
}
);
return result;
};
if (screenExist) {
const content = await MarsContent.findOne({
screens: { $in: screenExist.id },
});
if (content) {
// ID
contentOut.id = content.id;
// NAME
contentOut.name = content.name;
// ENTRY
const entry = getEntryVideos(content.entry);
// EQUIPMENT
const equipment = getEquipment(content.equipment);
// EXERCISES
const exercises = getExercises(content.exercises);
// OPTIONS
const options = getOptions(content.options);
// WORKOUTS
const workouts = getWorkouts(content.workouts);
// FILES
const files = getFiles(content.files);
// PROMISE
const results = await Promise.all([
entry,
equipment,
exercises,
options,
workouts,
files,
]);
//console.log(results);
return {
id: content.id,
name: content.name,
entry: results[0],
equipment: results[1],
exercises: results[2],
options: results[3],
workouts: results[4],
files: results[5],
};
} else {
return {};
}
}
},

Cannot read property 'price' of undefined node

I am very new to the node js and I get the following error:
Cannot read property 'price' of undefined
I used this package: https://github.com/paypal/PayPal-node-SDK
Code works without line
amount = req.body.price;
But I need to post data
Here is the code:
var paypal = require('paypal-rest-sdk');
var express = require('express');
var app = express();
var amount = 5;
paypal.configure({
'mode': 'sandbox', //sandbox or live
'client_id': 'AV-UFzoT7Ccua-ubSQwUGx96qVq46ySVLTHGPyMiK4CA6HP2gHNW61-cvN__sIoyxQD-xX9zZupCNi',
'client_secret': 'ELAElfR-2pjX5PWJpW3iCW0Yd-WQ_0u2LUk3BDO6v6dcSHgYv1mJG3wg6_gWaR3IwGnVvrZ8pFoRz-'
});
app.post('/pay',(req,res)=>{
amount = req.body.price;
var create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:8000/success",
"cancel_url": "http://localhost:8000/pay"
},
"transactions": [{
"item_list": {
"items": [{
"name": "item",
"sku": "item",
"price": amount,
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"currency": "USD",
"total": amount
},
"description": "This is the payment description."
}]
};
paypal.payment.create(create_payment_json, (error, payment)=> {
if (error) {
throw error;
} else {
console.log(payment);
for (let i = 0; i < payment.links.length; i++) {
if (payment.links[i].rel == 'approval_url') {
console.log(payment.links[i].href);
res.redirect(payment.links[i].href);
}
}
}
});
})
Thank you in advance
Also, this is how I post argument price in dart:
String _loadHTML() {
return '''
<html>
<body onload="document.f.submit();">
<form id="f" name="f" method="post" action="http://10.0.2.2:8000/pay">
<input type="hidden" name="ok" value="$price" />
</form>
</body>
</html>
''';
}
PayPal-node-SDK is deprecated and has no support, use Checkout-NodeJS-SDK for all new integrations

Validate the schema

How can we validate the written schema valid or not .
const schema = {
"properties": {
"foo": { "add": "string" , "minLenfeffgth": 3, "maxLefngth": 255 }
}
};
above mention schema are valid schema according to ajv.validateSchema() .
like we validating the data , there is any function who validate the schema .
complete code :
var Ajv = require('ajv');
var ajv = new Ajv({ allErrors: true});
const schema = {
"properties": {
"foo": { "add": "string" , "minLenfeffgth": 3, "maxLefngth": 255 }
}
};
// console.log(ajv.validateSchema(schema));
var validate = ajv.compile(schema);
test({"foo": ""});
function test(data) {
var valid = validate(data);
if (valid) console.log('Valid!');
else console.log(validate.errors);
}
result is : valid
You can configure Ajv to throw errors and use compile:
var ajv = new Ajv({
allErrors: true
});
var schema = {
type: 'object',
properties: {
date: {
type: 'unexisting-type'
}
}
};
try {
var validate = ajv.compile(schema);
} catch (e) {
console.log(e.message);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.6.2/ajv.min.js"></script>
above mention schema are valid schema according to ajv.validateSchema().
It's valid but it validated nothing, if you want to test a simple object with a foo mandatory property, you can do something like that :
var ajv = new Ajv({
allErrors: true
});
var schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"foo": {
"type": "string",
"minLength": 3,
"maxLength": 255
}
},
"required": [
"foo"
]
};
try {
var validate = ajv.compile(schema);
test({"foo": "" });
} catch (e) {
console.log("Validate error :" + e.message);
}
function test(data) {
var valid = validate(data);
if (valid) {
console.log('Valid!');
} else {
console.log(validate.errors);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.6.2/ajv.min.js"></script>
Run with data = {"foo": "" } return the error message below :
[
{
"keyword": "minLength",
"dataPath": ".foo",
"schemaPath": "#/properties/foo/minLength",
"params": {
"limit": 3
},
"message": "should NOT be shorter than 3 characters"
}
]
Run with data = {"foo": "abcdef" } return the message below :
Valid!

js grid and autocomplete

I am able to create a custom field with jsGrid and jquery autocomplete. All ajax CRUD calls are working for all other fields. The below code activates autocomplete and shows the available options in the input field as expected.
var tags = ["tag1", "tag2", "tag3"];
MyDescriptionField.prototype = new jsGrid.Field({
insertTemplate: function(value) {
return this._editPicker = $("<input>").autocomplete({source : tags});
},
editTemplate: function(value) {
return this._editPicker = $("<input>").autocomplete({source : tags});
},
........... (more code)
So far so good. However to actually capture the value so it can be inserted into the db, I also need to define insertValue and editValue.
The code below is NOT working
insertValue: function(){
return this._insertPicker = $("<input>").val();
},
...........(more code)
this one is not working eiter:
insertValue: function(){
return this._insertPicker.autocomplete({
select: function(event, ui) {
$("<input>").val(ui.item.value);
}
});
},
reference: jsGrid. http://js-grid.com/demos/
autocomplete: https://jqueryui.com/autocomplete/
Try this snippet:
$(function() {
var myTagField = function(config) {
jsGrid.Field.call(this, config);
};
myTagField.prototype = new jsGrid.Field({
sorter: function(tag1, tag2) {
return tag1.localeCompare(tag2);
},
itemTemplate: function(value) {
return value;
},
insertTemplate: function(value) {
return this._insertAuto = $("<input>").autocomplete({source : tags});
},
editTemplate: function(value) {
return this._editAuto = $("<input>").autocomplete({source : tags}).val(value);
},
insertValue: function() {
return this._insertAuto.val();
},
editValue: function() {
return this._editAuto.val();
}
});
jsGrid.fields.myTagField = myTagField;
$("#jsGrid").jsGrid({
width: "100%",
inserting: true,
editing: true,
sorting: true,
paging: true,
fields: [
{ name: "Name", type: "text" },
{ name: "Tag", type: "myTagField", width: 100, align: "center" },
{ type: "control", editButton: false, modeSwitchButton: false }
],
data: db.users
});
});
var tags = ["tag1", "tag2", "tag3"];
var db = {};
db.users = [
{
"Name": "Carson Kelley",
"Tag": ""
},
{
"Name": "Prescott Griffin",
"Tag": "tag1"
},
{
"Name": "Amir Saunders",
"Tag": "tag3"
},
{
"Name": "Derek Thornton",
"Tag": "tag2"
},
{
"Name": "Fletcher Romero",
"Tag": ""
}];
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script src="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.js"></script>
<link href="//code.jquery.com/ui/1.11.2/themes/cupertino/jquery-ui.css" rel="stylesheet"/>
<link href="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.css" rel="stylesheet"/>
<link href="//rawgit.com/tabalinas/jsgrid/master/dist/jsgrid-theme.css" rel="stylesheet"/>
<div id="jsGrid"></div>
or this codepen: https://codepen.io/beaver71/pen/rpaLEo
Thanks #beaver. Your pen helped my understand custom fields better. I extended it a bit to add filtering with autocomplete. https://codepen.io/obrienje/pen/aQKNry
$(function() {
var myTagField = function(config) {
jsGrid.Field.call(this, config);
};
myTagField.prototype = new jsGrid.Field({
autosearch: true,
sorter: function(tag1, tag2) {
return tag1.localeCompare(tag2);
},
itemTemplate: function(value) {
return '<span class="label label-primary">' + value + '</span>';
},
insertTemplate: function(value) {
return this._insertAuto = $("<input>").autocomplete({
source: tags
});
},
filterTemplate: function(value) {
if (!this.filtering)
return "";
var grid = this._grid,
$result = this._filterAuto = $("<input>").autocomplete({
source: tags
});
if (this.autosearch) {
$result.on("change", function(e) {
grid.search();
});
}
return $result;
},
editTemplate: function(value) {
return this._editAuto = $("<input>").autocomplete({
source: tags
}).val(value);
},
insertValue: function() {
return this._insertAuto.val();
},
filterValue: function() {
return this._filterAuto.val();
},
editValue: function() {
return this._editAuto.val();
}
});
jsGrid.fields.myTagField = myTagField;
$("#jsGrid").jsGrid({
width: "100%",
filtering: true,
inserting: true,
editing: true,
sorting: true,
paging: true,
fields: [{
name: "Name",
type: "text"
},
{
name: "Tag",
type: "myTagField",
width: 100,
align: "center"
},
{
type: "control",
editButton: false,
modeSwitchButton: false
}
],
data: db.users,
controller: {
loadData: function(filter) {
return $.grep(db.users, function(item) {
return (!filter.Tag || item.Tag.toLowerCase().indexOf(filter.Tag.toLowerCase()) > -1);
});
}
}
});
});
var tags = ["tag1", "tag2", "tag3"];
var db = {};
db.users = [{
"Name": "Carson Kelley",
"Tag": ""
},
{
"Name": "Prescott Griffin",
"Tag": "tag1"
},
{
"Name": "Amir Saunders",
"Tag": "tag3"
},
{
"Name": "Derek Thornton",
"Tag": "tag2"
},
{
"Name": "Fletcher Romero",
"Tag": ""
}
];
<html>
<head>
<link href="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.css" rel="stylesheet"/>
<link href="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid-theme.css" rel="stylesheet"/>
<link href="//code.jquery.com/ui/1.11.2/themes/cupertino/jquery-ui.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css"/>
</head>
<body>
<h1>Custom Grid DateField filtering with autocomplete</h1>
<div id="jsGrid"></div>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<script src="https://rawgit.com/tabalinas/jsgrid/master/dist/jsgrid.min.js"></script>
</body>
</html>
Thanks #beaver. Your pen helped my understand custom fields better. I extended it a bit to add filtering with autocomplete. https://codepen.io/obrienje/pen/aQKNry

with binding not working with default values

I have the following scanrio.
The child object defination
function SearchFilterOption(data){
var self = this
self.FilterCheck = ko.observable(false)
self.List = ko.observableArray([])
self.SelectedOptions = ko.observableArray([])
self.FilterText = ko.computed(function(){
var selectedDescriptions = [];
ko.utils.arrayForEach(self.List(), function (item) {
if (in_array(item.Value, self.SelectedOptions()))
selectedDescriptions.push(item.Description)
})
return selectedDescriptions.join(',')
})
self.FilterLabel = ko.computed(function(){
return (self.SelectedOptions() && self.SelectedOptions().length > 0) ? 'selected_filter' : ''
})
self.FilterClass = ko.computed(function(){
self.List(data)
return ( self.FilterCheck() == true ) ? 'set_check':'un_check'
})
self.Toggle = function(){
self.FilterCheck(!self.FilterCheck())
}
}
The parent model
var page = function() {
var self = this
self.LookupData = ko.observableArray()
self.Countries = ko.observableArray([])
self.LoadData = function(){
self.GetProfileData()
self.GetPreviousSearch()
}
self.GetProfileData = function(){
var url = 'ProfileApi/Index'
var type = 'GET'
var data = null
ajax(url , data , self.OnGetProfileDataComplete , type)
}
self.OnGetProfileDataComplete = function(data){
self.LookupData(getLookupData())
self.Countries(new SearchFilterOption(self.LookupData().Countries))
}
self.GetPreviousSearch = function(){
var url = 'SearchApi/PreviousSearch'
var type = 'GET'
var data = null
ajax(url , data , self.OnGetPreviousSearchComplete , type)
}
self.OnGetPreviousSearchComplete = function(data){
var search = data.Payload
if(search.locationCountryList){self.Countries().SelectedOptions(self.MakeInt(search.locationCountryList))}
}
self.MakeInt = function(array){
return array.map(function(item) {
return parseInt(item, 10);
})
}
self.LoadData()
}
And binding
$('document').ready(function () {
ko.applyBindings(new page())
})
<div data-bind="with:Countries">
<ul class="srh_fltr">
<li>
<label class="" data-bind="css:FilterLabel">Countries</label>
<p data-bind="text:FilterText"></p>
<div class="check_box">
<a class="un_check active" data-bind="css:FilterClass,click:Toggle">
<img src="../images/spacer.gif">
</a>
</div>
</li>
</ul>
<section class="form_view" data-bind="visible:FilterCheck">
<select multiple="multiple" data-bind="
options:List,
optionsText:"Description",
optionsValue:"Value",
optionsCaption:"--- ",
value:0,
selectedOptions:SelectedOptions"></select>
</section>
</div>
Here is some lookup data sample
[{
"Value": 1,
"Description": "United States"
}, {
"Value": 2,
"Description": "Canada"
}, {
"Value": 3,
"Description": "United Kingdom"
}, {
"Value": 4,
"Description": "Pakistan"
}, {
"Value": 5,
"Description": "India"
}, {
"Value": 6,
"Description": "Saudi Arabia"
}, {
"Value": 7,
"Description": "U.A.E."
}, {
"Value": 8,
"Description": "Afghanistan"
}, {
"Value": 9,
"Description": "Albania"
}]
OK. Code is complete. The problem is that when GetPreviousSearch is called and self.Countries().SelectedOptions() is filled nothing is selected. I mean FilterText is not displayed. i have seen in console the object contains the data filled by GetPreviousSearch but ui does not get changed? How can i resolve this issue. I suspect with biding may have to do something with it.
Here is the fiddle.
I have resolved the issue the problem is here
<select multiple="multiple" data-bind="
options:List,
optionsText:"Description",
optionsValue:"Value",
optionsCaption:"--- ",
value:0,
selectedOptions:SelectedOptions"></select>
</section>
In this i have to remove value attribute for multiselect. I found that value produces problem if used with selectedOptions in multiselect. Although if selectedOptions is used with dropdown it does not make any problem.

Resources