i was able to install ko.mapping in VisualStudio but when i try to map some Json Data in my view it does not work. can anyone tell me what i am doing wrong ?
here is my viewmodel
define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'mapping'], function (router, ko, logger, app, mapping) {
//#region Internal Methods
function activate() {
logger.log('Google Books View Activated', null, 'books', true);
return true;
}
//#endregion
//==jquery=================================================
function attached() {
}//-->end of viewAttached()
//========VIEWMODEL========================================
var ViewModel = function (data) {
activate = activate;
attached = attached;
title = 'google Books';
};
return new ViewModel();
});
and here ist an working example in Jsfiddle
I don't think that you don't need to return a new View Model. you just need to return the view model.
define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'mapping'],
function (router, ko, logger, app, mapping) {
var books = ko.observableArray();
function activate() {
getBooks().then(function(){
logger.log('Google Books View Activated', null, 'books', true);
return true;
});
}
function attached() {
}
function getBooks(){
$.getJSON(url, function (data) {
vm.books(ko.mapping.fromJS(data));
return true;
});
}
var vm = {
activate : activate,
attached : attached,
title : 'google Books',
books: books
};
return vm;
});
EDIT
To find requirejs errors add to your main.js file. It should help in tracking down requirejs module loading errors.
requirejs.onError = function (err) {
console.log(err.requireType);
if (err.requireType === 'timeout') {
console.log('modules: ' + err.requireModules);
}
throw err;
};
Related
I'm trying to prevent the user to save a piece if it doesn't achieve some requirements.
Currently I'm doing it like this:
self.beforeSave = function(req, piece, options, callback) {
let success = true;
let error = "";
if (Array.isArray(piece._subevents) && piece._subevents.length) {
success = self.checkDateAndTimeCompabilitiyWithChildren(piece);
}
if (!success) {
self.apos.notify(req, "Check the compatibility between parent event and subevents", { type: "error" });
error = "Subevents are not compatible with parent event";
}
callback(error);
};
This works but the problem is it shows 2 errors notifications (the default and my custom), 1 because of callback(error) and 1 because of apos.notify.
Any idea how to stop the item of being saved and only show my notification?
Thanks in advance.
UPDATE 1:
As Tom pointed out, my code looks like this now:
// lib/modules/events/public/js/editor-modal.js
apos.define('events-editor-modal', {
extend: 'apostrophe-pieces-editor-modal',
construct: function(self, options) {
self.getErrorMessage = function(err) {
if (err === 'incompatible') {
apos.notify('A message suitable for this case.', { type: 'error' });
} else {
apos.notify('A generic error message.', { type: 'error' });
}
};
}
});
// lib/modules/events/index.js
var superPushAssets = self.pushAssets;
self.pushAssets = function() {
superPushAssets();
self.pushAsset("script", "editor-modal", { when: "user" });
};
self.beforeSave = async function(req, piece, options, callback) {
return callback("incompatible")
};
For testing purposes I'm just returning the error in beforeSave. The problem is that an exception is being thrown in the browser console and the modal is not properly rendered again. Here's a screenshot about what I'm talking:
I'm trying to debug it and understand what's happening but no clue yet.
In your server-side code:
self.beforeSave = function(req, piece, options, callback) {
let success = true;
if (Array.isArray(piece._subevents) && piece._subevents.length) {
success = self.checkDateAndTimeCompabilitiyWithChildren(piece);
}
if (!success) {
return callback('incompatible');
}
return callback(null);
};
And on the browser side:
// in lib/modules/my-pieces-module/public/js/editor-modal.js
apos.define('my-pieces-module-editor-modal', {
extend: 'apostrophe-pieces-editor-modal',
construct: function(self, options) {
self.getErrorMessage = function(err) {
if (err === 'incompatible') {
return 'A message suitable for this case.';
} else {
return 'A generic error message.';
}
};
}
});
If the error reported by the callback is a string, it is passed to the browser. The browser can then recognize that case and handle it specially. 'my-pieces-module-editor-modal' should be substituted with the name of your pieces module followed by -editor-modal.
I am new to Apostrophe and trying to create a contact us form with file attachment in Apostrophe by following the tutorial.
https://apostrophecms.org/docs/tutorials/intermediate/forms.html
I have also created the attachment field in my index.js and it works fine from the admin panel.
Now, I am trying to create my own html for the form with file submission.
// in lib/modules/contact-form-widgets/public/js/always.js
apos.define('contact-form-widgets', {
extend: 'apostrophe-widgets',
construct: function(self, options) {
self.play = function($widget, data, options) {
var $form = $widget.find('[data-contact-form]');
var schema = self.options.submitSchema;
var piece = _.cloneDeep(self.options.piece);
return apos.schemas.populate($form, self.schema, self.piece, function(err) {
if (err) {
alert('A problem occurred setting up the contact form.');
return;
}
enableSubmit();
});
function enableSubmit() {
$form.on('submit', function() {
submit();
//I can access file here
// console.log($form.find('file'))
return false;
});
}
function submit() {
return async.series([
convert,
submitToServer
], function(err) {
if (err) {
alert('Something was not right. Please review your submission.');
} else {
// Replace the form with its formerly hidden thank you message
$form.replaceWith($form.find('[data-thank-you]'));
}
});
function convert(callback) {
return apos.schemas.convert($form, schema, piece, callback);
}
function submitToServer(callback) {
return self.api('submit', piece, function(data) {
alert("I AM AT SUBMIT API ")
if (data.status === 'ok') {
// All is well
return callback(null);
}
// API-level error
return callback('error');
}, function(err) {
// Transport-level error
alert("I AM HERE AT API ERROR")
return callback(err);
});
}
}
};
}
});
//and my widget.html is
<div class="form-group">
<input name="custom-file" type="file">
</div>
When I run this I get following errors
user.js:310 Uncaught TypeError: Cannot read property 'serialize' of undefined
at Object.self.getArea (user.js:310)
at Object.self.getSingleton (user.js:303)
at Object.convert (user.js:686)
at user.js:164
at async.js:181
at iterate (async.js:262)
at async.js:274
at async.js:44
at setImmediate.js:27
at runIfPresent (setImmediate.js:46)
My question is, how do I handle file submission? Is there any better approach for this?
This is much easier to do using the apostrophe-pieces-submit-widgets module, which allows you to define a schema for what the user can submit. You can include a field of type attachment in that, and this is demonstrated in the README.
I want to use an module to get and process data from my MongoDB database. (It should generate an object that represents my Express.js site's navbar)
I thought of doing something like this:
var nav = { Home: "/" };
module.exports = function() {
MongoClient.connect(process.env.MONGO_URL, function(err, db) {
assert.equal(err, null);
fetchData(db, function(articles, categories) {
combine(articles, categories, function(sitemap) {
// I got the data. What now?
console.log("NAV: ", nav);
})
});
});
};
var fetchData = function(db, callback) {
db.collection('articles').find({}).toArray(function(err, result) {
assert.equal(err);
articles = result;
db.collection('categories').find({}).toArray(function(err, result) {
assert.equal(err);
categories = result;
db.close();
callback(articles, categories);
});
});
};
var combine = function(articles, categories, callback) {
categories.forEach(function(category) {
nav[category.title] = {};
articles.forEach(function(article) {
if(article.category == category.name) {
nav[category.title][article.title] = "link";
}
})
});
callback(nav);
};
As of line 6, I do have all data I need.
(An object, currenty like { Home: '/', Uncategorized: { 'Hello world!': 'link' } })
But since I'm in an anonymous function, I don't know how to return that value. I mean, return would just return it the function that called it... And in the end, MongoClient.connect would receive my data.
If I set a variable instead, it would be set as module.exports returned before Node can even query the data from the database, right?
What can I do in order to make this work?
It should result in some kind of function, like
var nav = require('nav');
console.log(nav());
Thanks in advance!
Add another callback:
var nav = { Home: "/" };
module.exports = function(cb) {
MongoClient.connect(process.env.MONGO_URL, function(err, db) {
assert.equal(err, null);
fetchData(db, function(articles, categories) {
combine(articles, categories, function(sitemap) {
cb(sitemap);
})
});
})
});
And then use this way:
var nav = require('nav');
nav(function(sitemap){ console.log(sitemap); });
You can use mongoose module or monk module. These modules have been tested properly .
Just use
npm install mongoose or monk
The suggestion about mongoose is great and you can look into it, however I think you've already done the job with the fetching of the data from the db. You just need to access it in your main node flow.
You can try this:
module.exports.generateNav = function() {
MongoClient.connect(process.env.MONGO_URL, function(err, db) {
assert.equal(err, null);
var output = fetchData(db, function(articles, categories) {
combine(articles, categories, function(sitemap) {
})
});
return (output);
});
};
And then in your main application you can call it in the following way:
var nav = require('nav');
navigation = nav.generateNav();
console.log(navigation);
I have this file containing the following code which is the database layer of my api. It is externally dependent on SQL Server to fetch the data.
var sql = require('mssql');
var async = require('async');
module.exports = {
getDetails: function(number, callback) {
async.parallel({
general: function(callback) {
getGeneral(number, callback);
},
preferences: function(callback) {
getPref(number, callback);
}
},
function(err, results) {
if (err) {
logger.error(err);
throw err;
}
callback(results);
});
}
};
function getGeneral(number, callback) {
var mainconnection = new sql.Connection(dbCredentials[1].generalDBConfig, function(err) {
var request = new sql.Request(mainconnection);
request.input('number', sql.BigInt, number);
request.execute('[number].[genral_get]', function(err, recordsets) {
if (err) {
logger.error(err);
}
var gen = {};
var spResult = recordsets[0];
if (spResult[0] != null) {
spResult.forEach(function(record) {
var test = {};
gen[record.genCode] = record.genValue;
});
callback(null, gen);
} else {
callback(null, null);
}
});
});
}
function getPref(number, callback) {
var mainconnection = new sql.Connection(dbCredentials[0].prefDBConfig, function(err) {
var request = new sql.Request(mainconnection);
request.input('number', sql.BigInt, number);
request.execute('[number].[pref_get]', function(err, recordsets) {
if (err) {
logger.error(err);
}
var spResult = recordsets[0];
if (spResult[0] != null) {
callback(null, spResult[0]);
} else {
callback(null, null);
}
});
});
}
The database layer return this JSON format data:
{
"general": {
"number": "72720604"
"title": "Mr ",
"buildingNameNumber": null,
"subBuildingName": null,
"streetName": null,
"postalTown": null,
"county": null
},
"pref": {
"UseAccessibilitySite": "00000",
"IntroductorySource": "75"
}
};
As I am new to unit testing, I don't know how to start about writing unit tests for this module even though choosing mocha with chai as my unit testing framework. Any kind of suggestions or help is appreciated...
The question is: what exactly do you want to test in this code?
I'm assuming that the answer roughly is "I want to call getDetails() in my test suite and verify that it behaves correctly".
Of course, you don't want to create a whole MSSQL server in your test suite. Instead, it's much easier to stub the database.
A module for mocking, stubbing and spying that I've had great success with is sinon.js. For your code, you'll need to stub a few things:
Stub sql.Connection() to return a stubbed connection object.
Stub sql.Request() to return a stub object that has methods input and execute.
Stub sql.BigInt() to verify that it was called correctly.
Then, for the stubbed request object, you need to:
Verify that .input() was called correctly.
Make sure that execute returns the record(s) of your choosing.
It will be a lot of setup work to test the getDetails() function entirely. You might also be interested in the rewire module, which allows you to test getGeneral() and getPref() directly without having to add them to module.exports.
Finally, refactoring this code into smaller pieces will also help a lot. For example, if you could do something like this:
// This is your own db module, which takes care of connecting
// to the database for you.
var db = require('./db');
function getGeneral(number, callback) {
var request = db.createRequest();
// The rest of the function
}
Then it would be much easier to use sinon to create a fake request object that does exactly what you want it to do.
I've written a node script that gets some data by requesting REST API data (using the library request). It consists of a couple of functions like so:
var data = { /* object to store all data */ },
function getKloutData() {
request(url, function() { /* store data */}
}
// and a function for twitter data
Because I want to do some stuff after fetching all the I used the library async to run all the fetch functions like so:
async.parallel([ getTwitterData, getKloutData ], function() {
console.log('done');
});
This all works fine, however I wanted to put everything inside a object pattern so I could fetch multiple accounts at the same time:
function Fetcher(name) {
this.userID = ''
this.user = { /* data */ }
this.init();
}
Fetcher.prototype.init = function() {
async.parallel([ this.getTwitterData, this.getKloutData ], function() {
console.log('done');
});
}
Fetcher.prototype.getKloutData = function(callback) {
request(url, function () { /* store data */ });
};
This doesn't work because async and request change the this context. The only way I could get around it is by binding everything I pass through async and request:
Fetcher.prototype.init = function() {
async.parallel([ this.getTwitterData.bind(this), this.getKloutData.bind(this) ], function() {
console.log('done');
});
}
Fetcher.prototype.getKloutData = function(callback) {
function saveData() {
/* store data */
}
request(url, saveData.bind(this);
};
Am I doing something basic wrong or something? I think reverting to the script and forking it to child_processes creates to much overhead.
You're doing it exactly right.
The alternative is to keep a reference to the object always in context instead of using bind, but that requires some gymnastics:
Fetcher.prototype.init = function() {
var self = this;
async.parallel([
function(){ return self.getTwitterData() },
function(){ return self.getKloutData() }
], function() {
console.log('done');
});
}
Fetcher.prototype.getKloutData = function(callback) {
var self = this;
function saveData() {
// store data
self.blah();
}
request(url, saveData);
};
You can also do the binding beforehand:
Fetcher.prototype.bindAll = function(){
this.getKloutData = this.prototype.getKloutData.bind(this);
this.getTwitterData = this.prototype.getTwitterData.bind(this);
};
Fetcher.prototype.init = function(){
this.bindAll();
async.parallel([ this.getTwitterData, this.getKloutData ], function() {
console.log('done');
});
};
You can save this into another variable:
var me = this;
Then me is your this.
Instantiate object with this function:
function newClass(klass) {
var obj = new klass;
$.map(obj, function(value, key) {
if (typeof value == "function") {
obj[key] = value.bind(obj);
}
});
return obj;
}
This will do automatic binding of all function, so you will get object in habitual OOP style,
when methods inside objects has context of its object.
So you instantiate you objects not through the:
var obj = new Fetcher();
But:
var obj = newClass(Fetcher);