Knockout bindings from JSON file - javascript

What am I doing wrong with my bindings? I simply get [object HTMLElement] returned for each one.
Please note this is a pared-down version and I will be wanting to access the full range of the JSON values.
Fiddle:
https://jsfiddle.net/0416f0s7/2/
Code:
<div data-bind="text: intro"></div>
function ViewModel(stories) {
var self = this;
self.stories = ko.observableArray(ko.utils.arrayMap(stories, function(story) {
return story.stories;
}));
};
$.getJSON('data.json', function(data) {
window.storyViewModel = new ViewModel(data.stories);
ko.applyBindings(window.storyViewModel);
});
JSON (filename data.json):
{
"stories": [
{
"intro": "Hi",
"outro": "Bye",
"elements": [
{
"title": "Title 1",
"image": "img/image1.jpg",
"paragraph": "Wordswordswords"
},
{
"title": "Title 2",
"image": "img/image2.jpg",
"paragraph": "More wordswordswords"
}
]
}
]
}

Your html change to
`<div data-bind="foreach:stories">
<div data-bind="text: intro"></div>
</div>
your code change to
function test(stories) {
var self = this;
self.stories = ko.observableArray(ko.utils.arrayMap(stories, function (story) {
return story;
}));
}

Related

Convert JQuery function w/each to vanilla JS

I need some help. I am using a multilingual static site w/JQuery and JSON but I would like to use it w/simple JS. Most part of the code is finished but I cannot resolve the commented part of the JS successfully (w/JQuery it is working well).
var language, translate, jsData;
// Here is the questioned part in JQuery
translate = function(jsdata) {
$('[block]').each(function(index) {
var strTr;
strTr = jsdata[$(this).attr('block')][$(this).attr('txt')];
$(this).html(strTr);
});
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p block="1.md" txt="title"></p>
<p block="1.md" txt="body"></p>
Please read the comments.
[...document.querySelectorAll("[block]")]
.forEach(block => block.innerHTML = jsData[block.getAttribute("block")][block.getAttribute("txt")]);
let language, jsData;
// Here is the questioned part in JQuery
const translate = () => {
[...document.querySelectorAll("[data-block]")].forEach(
block =>
block.innerHTML = jsData[block.getAttribute("data-block")][block.getAttribute("data-txt")]
);
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p data-block="1.md" data-txt="title"></p>
<p data-block="1.md" data-txt="body"></p>
<p data-block="2.md" data-txt="title"></p>
<p data-block="2.md" data-txt="body"></p>

Merge multiple json with the same id

the first json is coming from the API in get this data by making a http call
[
{
"clientid": 1,
"clientname": "facebook",
"forecasted": 18,
},
{
"clientid": 2,
"clientname": "youtube",
"forecasted": 83,
}
]
i create the second json to update the image on the first json
[
{
"clientid": 1,
"clientname": "facebook ",
"img":"images/21747.jpg"
},
{
"clientid": 2,
"clientname": "youtube",
"img": "images/youtube.svg"
},
]
now i need to macth both json base on the same id, and fill the first json that i coming from the api with images of the second json
app.factory('myFactory', function($http){
return{
client: function(callback){
$http({
method: "GET",
url: "http://www.erek.co.za/api/..",
cache:true
}).then(callback);
},
list: function(callback){
$http({
method: "GET",
url: "data/Client.json",
cache:true
}).then(callback);
}
};
});
app.controller('myController', function ($scope, myFactory) {
myFactory.client(function(response){
var data = response.data;
$scope.myClients = data;
})
myFactory.list(function (response) {
var data = response.data;
$scope.dark = data;
})
});
html
<div ng-repeat="client in myClients>
<a href="#{{client.clientid}}"
<div class="client-project-panel">
<div class="client-logo-container">
<div class="client-logo" >
<img ng-src="{{dark[$index].img}}" alt="{{client.clientname}}">
</div>
<div class="project-forecasted">
<h2 class="forecasted-value">{{client.forecasted}}%</h2>
<p>Complete</p>
</div>
</div>
</div>
</a>
You can use Object.assign method.
let array1=[ { "clientid": 1, "clientname": "facebook", "forecasted": 18, }, { "clientid": 2, "clientname": "youtube", "forecasted": 83, } ]
let array2 = [ { "clientid": 1, "clientname": "facebook ", "img":"images/21747.jpg" }, { "clientid": 2, "clientname": "youtube", "img": "images/youtube.svg" } ]
var expected = array1.map(a => Object.assign(a, array2.find(b => b.clientid == a.clientid)));
console.log(expected);
myFactory.client(function(response){
var data = response.data;
var clientsData = data;
// after you fetch the clients get list of images
myFactory.list(function (response) {
var data = response.data;
var clientImages = {}
for(var i=0;i<data.length;i++){
clientImages[data[i].clientid] = data[i].img
}
$scope.dark = clientImages
$scope.myClients = clientsData
})
})
In your template just refer to {{dark[client.clientid]}}
You might want to wrap your html in ng-if="myClients" so that the content is shown only after the data is fetched

Meteor + MongoDB: How to get nested data?

I'm new to Meteor and trying to figure out this issue I have.
I'm trying to load data from the Lessons collection based on the route being passed. e.g if /courses/level1/lesson1/1a is passed then show data
Unfortunately this doesn't work.
Am I on the right path or is there a better way of doing this?
Collection
{
"_id": "YSgr3fvjpEBn7ncRa",
"courseId": "level1",
"lesson": [
{
"lessonId": "lesson1",
"freeLesson": true,
"title": "Lesson 1",
"eachLesson": [
{
"eachLessonId": "1a",
"title": "This is (masculine)",
"video": "839843"
},
{
"eachLessonId": "1b",
"title": "That is (masculine)",
"video": "839843"
},
{
"eachLessonId": "1c",
"title": "This is (feminine)",
"video": "839843"
},
{
"eachLessonId": "1d",
"title": "That is (feminine)",
"video": "839843"
},
{
"eachLessonId": "1e",
"title": "Getting to know you",
"video": "839843"
}
]
}
]
}
Routes
Router.route("courses/:courseId/:lessonId/:eachLessonId", {
path:"/courses/:courseId/:lessonId/:eachLessonId",
layoutTemplate: "layoutLessons",
template:"lessons",
onBeforeAction:function(){
var currentUser = Meteor.userId();
if (currentUser) {
Session.set('courseId', this.params.courseId);
Session.set('lessonId', this.params.lessonId);
Session.set('eachLessonId', this.params.eachLessonId);
this.next();
} else {
Router.go('/')
}
},
});
Template helper
Template.lessons.onCreated(function(){
Meteor.subscribe('listLessons');
});
Template.lessons.helpers({
currentLesson: function() {
var currentLesson = Session.get('eachLessonId');
return Lessons.find({"lesson.eachLesson.eachLessonId" : currentLesson});
},
});
HTML
{{#each currentLesson}}
{{title}}
{{video}}
{{/each}}
Instead of storing courseId, lessonId and eachLessonId as Session values, you could use the Iron Router's waitOn and data option.
For example, you could rewrite your route as follows:
Router.route('/courses/:courseId/:lessonId/:eachLessonId', {
name: 'lessons',
layoutTemplate: 'layoutLessons',
template: 'lessons',
onBeforeAction: function() {
let currentUser = Meteor.user();
if (currentUser) this.next();
else Router.go('/');
},
data: function() {
var doc = Lessons.findOne({
"courseId": this.params.courseId,
"lesson.lessonId": this.params.lessonId,
"lesson.eachLesson.eachLessonId": this.params.eachLessonId
});
if (doc) {
var lesson = {};
var lessonId = this.params.eachLessonId;
_.each(doc.lesson, function(i) {
lesson = _.find(i.eachLesson, function(j) {
return j.eachLessonId == lessonId;
});
});
return lesson;
}
return {};
},
waitOn: function() {
return [
Meteor.subscribe('lesson', this.params.courseId, this.params.lessonId, this.params.eachLessonId)
];
}
});
This should set the data context to the requested eachLesson object. However, you may consider setting the data context to a document in the Lessons collection and then just picking certain eachLesson objects. In addition, you should create a publish function which returns just the requested Lessons document and not all of them, like you probably do now in your listLessons publication. You can pass all IDs as arguments to the corresponding publish function.

How can I filter results in typeahead.js using a second variable?

I am trying to filter results using Typeahead.js. I can currently filter the results using a field called activity_title. This works fine.
How can I filter my results by a second value? In this case, I would like to select only the results that have a certain value for activity_level. I need to set this when the typeahead is initialised rather than hard coding it into the Bloodhound initialisation (e.g. I don't want to use url: 'api/activity/&range=1,3')
I have the following valid JSON that I access remotely:
{
"meta": [
{
"name": "activity_id",
"table": "table",
"max_length": 4
},
{
"name": "activity_title",
"table": "table",
"max_length": 91
},
{
"name": "activity_level",
"table": "table",
"max_length": 2
}
],
"detail": [
{
"activity_id": "57",
"activity_title": "Help old ladies to cross the road.",
"activity_level": "2"
},
{
"activity_id": "58",
"activity_title": "Help mum with the washing up.",
"activity_level": "3"
},
{
"activity_id": "59",
"activity_title": "Shine my shoes",
"activity_level": "1"
},
{
"activity_id": "60",
"activity_title": "Put the bins out",
"activity_level": "1"
}
]
}
I set up a Bloodhound instance like this:
var activities = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.activity_title);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: '/api/activity/',
filter: function(data) {
return $.map(data['detail'], function(detail) {
return {
activity_id: detail.activity_id,
activity_title: detail.activity_title,
objective_level: detail.objective_level
};
});
}
}
});
I use Typeahead.js to do a lookup on the data as I type.
$( document ).on( "focus", ".typeahead-init", function() {
// + '&range=' + minimum + ',' + maximum
var minimum = $('#group-level-min-1').val();
var maximum = $('#group-level-max-1').val();
$(this).typeahead({
highlight: true
},
{
name: 'activity_title',
displayKey: 'activity',
source: activities.ttAdapter(),
templates: {
header: '<div class="header-name">Activities</div>',
empty: [
'<div class="empty-message">',
'No activities match your search',
'</div>'
].join('\n'),
suggestion: Handlebars.compile('<div class="typeahead-activity" id="typeahead-activity-{{activity_id}}"><strong>{{objective_level}}</strong> - {{activity_title}}</div>')
}
})
//info on binding selection at https://github.com/twitter/typeahead.js/issues/300
.bind('typeahead:selected', function(obj, datum, name) {
var target = $(this).closest('.activity-container');
var activityId = datum['activity_id'];
var url = '/api/activity/id/'+activityId;
$(target).children('.activity-id').val(activityId);
//http://runnable.com/UllA9u8MD5wiAACj/how-to-combine-json-with-handlebars-js-for-javascript-ajax-and-jquery
var raw_template = $('#activity-output').html();
// Compile that into an handlebars template
var template = Handlebars.compile(raw_template);
// Fetch all data from server in JSON
$.get(url,function(data,status,xhr){
$.each(data,function(index,element){
// Generate the HTML for each post
var html = template(element);
// Render the posts into the page
target.append(html);
});
});
});
$(this).removeClass("typeahead-init");
$(this).focus();
});
This has been cobbled together from several answers on Stackoverflow and others. Any help greatly appreciated.

How to access second level JSON object via $.getJSON

Hi guys please take a look at this having trouble reaching second level inside a object that looks like this:
{
"results": [{
"title": "TEAM",
"subtitle": "athletes",
"offset": 0,
"icon": "info",
"relevance": 1,
"link": {
"title": "HOME",
"url": "http://www.test.com",
"id": "23458"
}]
}
and this is the code I have:
var theJson = {
init: function() {
this.url = 'http://test.com/js?jsonfeed=123123';
this.fetch();
},
fetch: function() {
$.getJSON(this.url, function( data ){
var obj = $.map(data.results, function( newObj ){
return {
title: newObj .title,
icon: newObj.icon,
topic: newObj.link.id, //??
topic: newObj["link"].id //??
topic: newObj[1].id //??
};
});
});
}
};
theJson.init();
My question is how do I reach the id var in link array inside results object? Thank guys you all rock!
You are doing it correct the first and second time:
newObj.link.id
newObj["link"].id
There are both correct ways to get the id under link.
You can easily access it by index, like that:
newObj["link"][0]

Categories