Backbone: put collection title and collection items into one json file - json

I have a FAQ on my website.
The questions are in a json file, but I want some extra info (like the title of the faq) also in the json file.
My json file looks like this:
{
"titel": "FAQ title",
"items": [
{
"question": "Question 1",
"answer": "Answer 1"
},
{
"question": "Question 2",
"answer": "Answer 2"
}
]
}
Collection extend code:
Faq.Collection = Backbone.Collection.extend({
model: Faq.Model,
url: '/scripts/json/faq.json',
parse: function(response){
return response.items;
}
});
The items are parsed, because this is for the render loop.
But how can I show the title on the page?
This is the render function:
render: function() {
this.el.innerHTML = this.template({
titel: 'Helpdesk'
});
console.log(this);
this.collection.each(function( faqitem ) {
var faqItemView = new Faq.Views.ModelView({ model: faqitem });
this.$el.find('.faq').append( faqItemView.render().el );
}, this);
return this;
}
I want to put the title from the json file at the place where 'Helpdesk' is.
I hope I'm clear enough

First change your parse function like this :
parse: function(response){
this.title = response.title;
return response.items;
}
and then in your render function :
this.el.innerHTML = this.template({
titel: this.collection.title // pay attention to the titel not being title :)
});

Related

GULP Create JSON file from another JSON file

I am trying to create a local lang file that will be formatted as json. I have the following navigation in json format below. And I need to create a new JSON file using GULP to create a lang file (see below)
"lists": [
{
"title": "Application Intel",
"items": [
{
"title": "Analytics Dashboard",
"href": "intel_analytics_dashboard.html"
},
{
"title": "Marketing Dashboard",
"href": "intel_marketing_dashboard.html"
},
{
"title": "CEO Dashboard",
"href": "intel_ceo_dashboard.html"
},
{
"title": "Introduction",
"href": "intel_introduction.html"
},
{
"title": "Build Notes",
"href": "intel_build_notes.html",
"text": "Build Notes",
"span": {
"class": "",
"text": "v{{version}}"
}
}
]
}
I need to create a file that looks like the following json:
"nav": {
"application_intel": "Application Intel",
"intel_analytics_dashboard": "Analytics Dashboard",
"intel_marketing_dashboard": "Marketing Dashboard",
"intel_ceo_dashboard": "CEO Dashboard",
"intel_introduction": "Introduction",
"intel_build_notes": "Build Notes",
}
Whats the best way to go about this?
Here is solution.
Let's say you have nav.json file inside src and you want to change its shape and place it into dest directory. You can achieve this from within gulpfile.js
const { src, dest } = require("gulp");
const through = require("through2");
// gulp task
function json() {
return src("src/nav.json")
.pipe(
through.obj((file, enc, cb) => {
// get content of json file
const rawJSON = file.contents.toString();
// parse raw json into javscript object
const parsed = JSON.parse(rawJSON);
// transform json into desired shape
const transformed = transformJson(parsed);
// make string from javascript obj
const stringified = JSON.stringify(transformed, null, 2);
// make bufer from string and attach it as current file content
file.contents = Buffer.from(stringified);
// pass transformed file into next gulp pipe
cb(null, file);
})
)
.pipe(dest("dest"));
}
// transformation
function transformJson(input) {
const result = { nav: {} };
// read json field by field
Object.keys(input).forEach(topLevelKey => {
// current object
const topLevelItem = input[topLevelKey];
// in your design topLevelItems are arrays
topLevelItem.forEach(menuItem => {
if (menuItem.title) {
// make url either from item href or title
const itemUrl = makeUrl(menuItem.href || menuItem.title);
result.nav[itemUrl] = menuItem.title;
}
// prcoess children
if (menuItem.items) {
menuItem.items
.filter(child => !!child.title) // process only child items with title
.forEach(child => {
const childUrl = makeUrl(child.href || child.title);
result.nav[childUrl] = child.title;
});
}
});
});
return result;
}
// helper func
function makeUrl(href) {
return href
.toLowerCase()
.replace(/\.html$/, "")
.replace(/\s/g, "_");
}
// export for use in command line
exports.json = json;
json transformation function is bit forEachy and if you have deep nested navigation structure, maybe you should change it into something recursive

Knockout nested model properties

I need a feature like a repeating table in my web form and need to store my data in a JSON format like this:
[
{
"id": 1, "name": "T01", "title": "T01 form title", "totalPoints": "total of all points for sections below",
"sections":
[
{ "section": "First section", "point": 4 },
{ "section": "Second section", "point": 5 }
]
},
{
"id": 2, "name": "T02", "title": "T02 form title", "totalPoints": "total of all points for sections below",
"sections":
[
{ "section": "First section", "point": 4 },
{ "section": "Second section", "point": 5 }
]
}
]
I'm using knockout and I implemented top level of the structure below, but struggling with a nested sections.
Here is my attempts to structure my Model, please advise what option to use, or if this incorrect, please advise the right option:
function Form(data)
{
this.Id = ko.observable(data.Id);
this.Name = ko.observable(data.Name);
this.Title = ko.observable(data.Title);
this.Total = ko.observable(data.Total);
// Option 1, like an array
this.Sections = ko.observableArray([
{
Section: data.Section,
Point: data.Total
}
]);
// Option 2, like a function
function Sections(data) {
this.Section = ko.observable(data.Section),
this.Point = ko.observable(data.Point)
}
}
Later I push this data as a model to observable array like this, again I can push the top level, but couldn't nested properties:
self.addForm = function () {
self.forms.push(
new Form({
Id: this.id(),
Name: this.name(),
Title: this.title(),
Total: function() // TODO
// Sections nested properties implementation
})
);
self.name("");
};
I'd say it's best to define two viewmodels:
Form, and
Section
Your Form will have three kinds of properties:
Regular ko.observable values, for Id, Name and Title
Note: If some of those are static, it's best to not make them observable. I can imagine the Id will never change: you can signal this to other readers of your code by making it a regular string.
An observableArray for your list of Sections
A pureComputed for Total that sums up all your Section points
function Section(points, title) {
// TODO: check if you need these to be observable
this.points = points;
this.title = title;
};
// Create a new Section from a plain object
Section.fromData = function(data) {
return new Section(data.point, data.section);
};
function Form(id, name, title, sections) {
this.id = ko.observable(id);
this.name = ko.observable(name);
this.title = ko.observable(title);
// Map using our static constructor helper
this.sections = ko.observableArray(
sections.map(Section.fromData)
);
this.total = ko.pureComputed(function() {
return this.sections().reduce(function(sum, section) {
return sum + section.points;
}, 0);
}, this);
}
// A helper to get from your data object to a new VM
Form.fromData = function(data) {
return new Form(data.id, data.name, data.title, data.sections);
}
// Apply bindings
ko.applyBindings({
forms: getTestData().map(Form.fromData)
});
// Your test data
function getTestData() {
return [{
"id": 1,
"name": "T01",
"title": "T01 form title",
"totalPoints": "total of all points for sections below",
"sections": [{
"section": "First section",
"point": 4
},
{
"section": "Second section",
"point": 5
}
]
},
{
"id": 2,
"name": "T02",
"title": "T02 form title",
"totalPoints": "total of all points for sections below",
"sections": [{
"section": "First section",
"point": 4
},
{
"section": "Second section",
"point": 5
}
]
}
]
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: forms">
<li>
<p data-bind="text: title"></p>
<ul data-bind="foreach: sections">
<li>
<span data-bind="text: title"></span> (
<span data-bind="text: points"></span>)
</li>
</ul>
<span data-bind="text: 'total: (' + total()"></span>)
</li>
</ul>
The Section class is a bit plain; if you don't have additional requirements you could choose to use plain objects instead.

Concat Json files with prefix

I would like to merge my json files with a gulp task with a prefix per jsonfile.
I have several json files like this
{
"en": {
"title": "Component title english",
"subtitle": "Component subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
I would like to merge these with the component name as a prefix so the outcome of the merge will be come something like this:
"componentBlogBox": {
"en": {
"title": "Component title english",
"subtitle": "Component subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
},
"componentCaseSlider": {
"en": {
"title": "Caseslider title english",
"subtitle": "caseslider subtitle english",
},
"nl": {
"title": "Component title dutch",
"subtitle": "Component subtitle dutch",
}
}
I have this gulp task using node module gulp-merge-json but this just replaces the keys to form one.
gulp.task('json-merge', function(){
gulp.src(['dest/json/blog-box/home.json', 'dest/json/case-slider/home.json'])
.pipe(jsonMerge('combined.json'))
.pipe(gulp.dest('dest/json'));
});
Is there a way to merge using a gulptask and without editing all my json files?
gulp-merge-json offers an edit option that allows you to modify the parsed JSON for each file before all of them are merged.
So in your case all you have to do is stick the parsed JSON for each file into a new object like {'componentBlogBox': parsedJson} and return that. Whether the property should be componentBlogBox or componentCaseSlider you can determine by looking at the path of the file:
var jsonMerge = require('gulp-merge-json');
var path = require('path');
function camelCase(str) {
return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
}
gulp.task('json-merge', function(){
return gulp.src([
'dest/json/blog-box/home.json',
'dest/json/case-slider/home.json'
])
.pipe(jsonMerge({
fileName: 'combined.json',
edit: function(parsedJson, file) {
var component = path.basename(path.dirname(file.path));
var editedJson = {};
editedJson[camelCase('component-' + component)] = parsedJson;
return editedJson;
}
}))
.pipe(gulp.dest('dest/json'));
});
(Credit for the camelCase function goes to this answer.)
Awesome Sven, just what I was looking for. The camelcase thing wasn't really an must-have but it's a nice touch for further development.
I simplified it into this
gulp.task('json-merge', function(){
return gulp.src([
'dest/json/blog-box/home.json',
'dest/json/case-slider/home.json'
])
.pipe(jsonMerge({
fileName: 'combined.json',
edit: function(parsedJson, file) {
var component = path.basename(path.dirname(file.path));
var editedJson = {};
editedJson[component] = parsedJson;
return editedJson;
}
}))
.pipe(gulp.dest('dest/json'));
});

Backbone JS parse json attribute to a collection's model

I'm having trouble parsing a json to a model.
Here is the JSON:
[
{
"name": "Douglas Crockford",
"email": "example#gmail.com",
"_id": "50f5f5d4014e045f000002",
"__v": 0,
"items": [
{
"cena1": "Cena1",
"cena2": "Cena2",
"cena3": Cena3,
"cena4": "Cena4",
"cena5": "Cena5",
"cena6": Cena6,
"_id": "50ee3e782a3d30fe020001"
}
]
}
]
And i need a model to have the 'items' attributes like this:
cena = new Model({
cena1: "Cena1",
cena2: "Cena2",
...
});
What I've tried:
var cenaCollection = new Backbone.Collection.extend({
model: Cenas,
url: '/orders',
parse: function (response) {
return this.model = response.items;
}
});
then I create new instance of the collection and fetch, but i get "response.items" always "undefined" :|
Thanks in advance!
The parse function should return the attributes hash to be set on the model (see the documentation here). So you'll need simply:
parse: function (response) {
return response[0].items;
}

backbone collection fetch error

I'm trying to fetch a collection from a .json file. Here is my collection code
define(['jquery', 'underscore', 'backbone', 'vent'], function($, _, Backbone, vent) {
'use strict';
var Wine = Backbone.Model.extend({
urlRoot: "js/models/wines.json",
defaults: {
"id": null,
"name": "",
"grapes": "",
"country": "USA",
"region": "California",
"year": "",
"description": "",
"picture": ""
}
});
return Backbone.Collection.extend({
model: Wine,
url: "js/models/wines.json",
});
});
I'm fetching the collection like this:
var _wine = new wineCollection();
_wine.fetch({
success : function(data) {
console.log("ON SUCCESS");
console.log(data);
},
error: function(response) {
console.log("ON ERROR");
console.log(response);
}
});
In the console it's always showing the "ON ERROR" message:
ON ERROR
child
_byCid: Object
_byId: Object
_callbacks: Object
length: 0
models: Array[0]
__proto__: ctor
And here is one item of my wines.json file
{"id":"9","name":"BLOCK NINE","year":"2009","grapes":"Pinot Noir","country":"USA","region":"California","description":"With hints of ginger and spice, this wine makes an excellent complement to light appetizer and dessert fare for a holiday gathering.","picture":"block_nine.jpg"}
What am I doing wrong?
Have you tried inspecting the collection class in the fetch method (what is actually send over).
You might need to override the parse method in order to access an inner part of the data send over.
For instance:
Wine.Collection = Backbone.Collection.extend({
//we need to parse only the inner list
parse : function (response) {
this.cursor = response.cursor;
return response.list;
}
Where the array is an inner list: {list: [{item: one}, {item: two}]}