Trouble getting m.request to auto-cast to a class in Mithril - mithril.js

I have defined a class and am asking m.request to cast a web service's JSON response to it, but each of the class properties come out equal to n/b(), and my view renders each property as function (){return arguments.length&&(a=arguments[0]),a}.
If I do not attempt to auto-cast the JSON response to my class in m.request, then my view renders just fine, which I think tells me that the JSON object returned by the web service is valid JSON.
I want to use my class. What is wrong?
Here is an edited sample of the JSON returned by the web service:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"blah blah",
"indent":"true",
"wt":"json"}
},
"response":{
"numFound":97,
"start":0,
"docs":[
{
"identifier":"abc123",
"heading":"A Great Heading",
"id":"abc-123-1",
"title":"A Title",
"url":"path/to/some.html",
"content":["Blah blah blah blah blee blah."]
},
{
"identifier":"xyz789",
"heading":"Another Heading",
"id":"xyz-789-1",
"title":"Another Title",
"url":"another/path/to.html",
"content":["My bonny lies over the ocean."]
}
]
}
}
Here is my Mithril app:
var findus = {};
findus.Document = function (data) {
this.id = m.prop(data.id);
this.title = m.prop(data.title);
this.heading = m.prop(data.heading);
this.identifier = m.prop(data.identifer);
this.url = m.prop("//" + data.url + "#" + data.identifier);
};
findus.vm = (function() {
var vm = {};
vm.init = function () {
// user input
vm.queryText = m.prop("");
vm.search = function () {
if (vm.queryText()) {
vm.results = m.request({
method: "GET",
url: "/prd/query?q=" + vm.queryText(),
type: findus.Document,
unwrapSuccess: function (response) {
return response.response.docs;
},
unwrapError: function (response) {
console.log(response);
}
}).bind(vm);
}
};
};
return vm;
}());
findus.controller = function () {
findus.vm.init();
};
findus.view = function () {
return [
m("input", {onchange: m.withAttr("value", findus.vm.queryText), value: findus.vm.queryText()}),
m("button", {onclick: findus.vm.search}, "Search"),
findus.vm.results ? m("div", [
findus.vm.results().map(function (result) {
return m("div", [
m("h2", result.heading),
m("p", result.content),
m("a", {href: result.url}, result.url)
]);
})
]) : ""
];
};
m.module(document.body, {controller: findus.controller, view: findus.view});

Oh, bugger. I forgot that my class properties are getter/setters via m.prop, so I should have been calling them as functions in the view -- see below.
False alarm, problem solved, I'm embarrassed.
findus.view = function () {
return [
m("input", {onchange: m.withAttr("value", findus.vm.queryText), value: findus.vm.queryText()}),
m("button", {onclick: findus.vm.search}, "Search"),
findus.vm.results ? m("div", [
findus.vm.results().map(function (result) {
return m("div", [
m("h2", result.heading()),
m("p", m.trust(result.content())),
m("a", {href: result.url()}, result.url())
]);
})
]) : ""
];
};

Related

How to create location select box in cshtml page without model

I want to create a select box for the location field, in which if one types any letter should call API and fetch location details in the dropdown
I tried the below code but didn't work
<select class="js-data-example-ajax form-control" id="FilterLocation"></select>
#Html.Hidden("FilterLocation", new { id = "locationId" })
In the script written below code
function setLocation() {
$('.js-data-example-ajax').select2({
ajax: {
type: 'PUT',
url: function (params) {
return '/api/GoogleCustomSearch/getLocation?matchingName=' + params.term
},
delay: 250,
data: function (params) {
var query = {
}
// Query paramters will be ?search=[term]&page=[page]
return query;
},
processResults: function (data) {
data = JSON.parse(data);
let results = []
if (data.location !== null) {
data.location.forEach((e) => {
results.push({
id: e,
text: e
})
})
}
return {
results: results
};
}
},
placeholder: "Search"
})
$('.js-data-example-ajax').on('change',function(e){
var selVal = $('#FilterLocation').val()
$('#locationId').val(selVal)
//getZipCodeForDynamic(selVal)
})
var $newOption = $("<option selected='selected'></option>")
$("#FilterLocation").append($newOption).trigger('change');
}
Dropdown options are not getting with above code.
Here is a working demo for select2:
View:
<select id="mySelect2" class="js-data-example-ajax" style="width:200px"></select>
js:
$('#mySelect2').select2({
ajax: {
type: 'PUT',
url: "GetSelect2Data",
delay: 250,
data: function(params) {
var query = {
search: params.term,
}
// Query parameters will be ?search=[term]
return query;
},
processResults: function(data) {
//data = JSON.parse(data);
let results = []
if (data.location !== null) {
data.location.forEach((e) => {
results.push({
id: e,
text: e
})
})
}
return {
results: results
};
}
},
placeholder: "Search"
});
model:
public class Select2Model {
public List<string> Location { get; set; }
}
action:
[HttpPut]
public ActionResult GetSelect2Data(string Search)
{
return Json(new Select2Model() { Location = new List<string> { "a"+Search,"b" + Search, "c" + Search } });
}
result:

How to avoid duplicate entries in IBM JSONStore

WL.JSONStore.get(collectionName).change(data, options) method does not seem to work for duplicate values. I get duplicate values entered whenever data is loaded through the adapter. Below is the code that I have used to avoid duplicate entries.
init(){
console.log('JSONStore init function callled');
let collections = {
activities: {
searchField: {serialKey: 'string'},
adapter: {
name: 'ServiceAdapter',
add: 'pushActivities',
remove: 'removeActivity',
replace: 'replaceActivity',
load: {
procedure: 'getActivities',
params: [],
key: 'rows'
}
}
}
}
WL.JSONStore.init(collections).then((success) => {
console.log('-->JSONStore init success')
}, (failure) => {
console.log('-->JSONStore init failed', failure)
})
}
load() {
let dataRequest = new
WLResourceRequest("/adapters/ServiceAdapter/getActivities",
WLResourceRequest.GET);
dataRequest.send().then(
(response) => {
this.data = response.responseJSON.rows;
this.activityService.put(this.data);
})
}
put(data){
console.log('--> JSONStore put function called');
let collectionName = 'activities';
let options = {
replaceCriteria: ['serialKey'],
addNew: true,
markDirty: false
};
WL.JSONStore.get(collectionName).change(data, options).then((success) => {
console.log('--> JSONStore put success')
}, (failure) => {
console.log('--> JSONStore put failed', failure)
})
}
Adapter Function:
function getActivities() {
var path = 'employees' + '/_all_docs?include_docs=true';
var input = {
method : 'get',
returnedContentType : 'json',
path : path,
};
var response = MFP.Server.invokeHttp(input);
if (!response.rows) {
response.isSuccessful = false;
return response;
} else {
var results = [];
for (var i=0; i < response.rows.length; i++) {
results.push(response.rows[i].doc);
}
return {'rows': results};
}
}
I have even tried by:
searchFields: {serialKey: 'string',serialId: 'string'}
replaceCriteria: ['serialKey','serialId']
But no luck.
NOTE: There is no error in the former one, whereas the later results in an error.
ERROR : PROVISION_TABLE_SEARCH_FIELDS_MISMATCH (I have already tried to destroy the collection and perform the change, as the link suggests.
I have followed the below link:
https://www.youtube.com/watch?v=Ep6w1zXoI-k
I am using the below versions:
mfpdev : 8.0.0-2017102406
Let me know if you need any more details.

Bind validation results from ajax request to form model in mithril

Hi I would like to bind html inputs with validation response model returned from API like that:
{"userName":[{"memberNames":["UserName"],"errorMessage":"Field User Name is required."}],"acceptTerms":[{"memberNames":["AcceptTerms"],"errorMessage":"Accepting terms is requried"}]}
And my component in mithril
var RegisterPage = {
vm: {
userName: m.prop(),
password: m.prop(),
confirmPassword: m.prop(),
acceptTerms: m.prop(false)
},
controller: function (args) {
this.title = 'Register new user account';
this.vm = RegisterPage.vm;
this.register = function (e) {
e.preventDefault();
apiRequest({ method: "POST", url: "http://localhost:12116/auth/register", data: RegisterPage.vm }).then(RegisterPage.vm.registerResult)
}
},
view: function (ctrl, args) {
return m('form.input-group',
[
m('.input-row', [m('label', 'Email'), m('input[type=email][placeholder=Your email address like myemail#email.com]', { onchange: m.withAttr("value", ctrl.vm.email) })]),
m('.input-row', [m('label', 'Password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.password) })]),
m('.input-row', [m('label', 'Confirm password'), m('input[type=password][placeholder=your password]', { onchange: m.withAttr("value", ctrl.vm.confirmPassword) })]),
m('.input-row', [m('label', 'Accept terms and conditions'), m('input[type=checkbox]', { onchange: m.withAttr("checked", ctrl.vm.acceptTerms) })]),
m('button[type=submit].btn btn-positive btn-block', { onclick: ctrl.register }, 'Register account')
]);
}
}
I am looking for some generic solution. I would like to mark invalid fields with css class and add field validation message.
UPDATE
In my project I use some wrapper around m.request to get more details when 400 is thrown
function apiRequest(args) {
NProgress.start();
if (!args.unwrapError) {
args.unwrapError = function (data, xhr) {
if (xhr.status === 401)
{
layout.badRequestMsg(xhr.statusText);
}
NProgress.done();
return data;
}
}
if (!args.unwrapSuccess) {
args.unwrapSuccess = function (data, xhr) {
NProgress.done();
return data;
}
}
return m.request(args);
}

How to use jsonschema for Loopback remoteMethod?

In my app I want define JSON schemas for custom API.
For example from: http://docs.strongloop.com/display/public/LB/Remote+methods#Remotemethods-Example
module.exports = function(Person){
Person.greet = function(msg, cb) {
cb(null, 'Greetings... ' + msg);
}
Person.remoteMethod(
'greet',
{
accepts: <generate definitions from jsonschema>,
returns: <generate definitions from jsonschema>
}
);
};
How to do that?
This is right way?
MY SOLUTION - validation decorator + remote method params with object type
var validate = require('jsonschema').validate;
bySchema = function (schema) {
return function (func) {
return function () {
var data = arguments[0],
callback = arguments[1];
var result = validate(data, schema);
if (result.errors.length > 0) {
// some errors in request body
callback(null, {
success: false,
error: 'schema validation error',
});
return;
}
return func.apply(this, arguments);
};
};
};
defaultRemoteArguments = {
accepts: {
arg: 'data',
type: 'object',
http: function(ctx) {
return ctx.req.body;
}
},
returns: {
arg: 'data',
type: 'object',
root: true
}
};
Example:
Auth.login = bySchema(require('<path to shcemajson json for this request>'))
(function(data, cb) {
// process request
});
Auth.remoteMethod('login', defaultRemoteArguments);
In this solution contrib loopback explorer will not be useful, because request/response are objects, not fields...
The correct way to do it is to set the type in the returns attribute to the model name.
In your case you would write:
Person.remoteMethod(
'greet',
{
...
returns: {type:'Person', ...}
}
);
You need to modify your output to match the format accepted by the returns property.
...
returns: [{arg: "key1", type: "string"}, {arg: "key2", type: "object"}, ...];
...

Why could not load data from Adapter into JSONStore?

function getListPhoneNumbers() {
var data = {listContacts:[{name:'Ho Cong Vi',number:'12345666'},{name:'hcv',number:'6543218'}]};
WL.Logger.info('Data:'+JSON.stringify(data));
return data;
}
function addListPhoneNumber(data) {
WL.Logger.debug('Add Data to JSONStore: ' + data);
return;
}
function updateListPhoneNumber(data) {
WL.Logger.debug('Updata Data from JSONStore: ' + data);
return;
}
function deleteListPhoneNumber(data) {
WL.Logger.debug('Delete Data from JSONStore: ' + data);
return;
}
This is my code in main.js:
$('#show-all-btn').on('click', showAllData);
var collectionName = 'Contacts',
collections = {};
collections[collectionName] = {
searchFields: {
name: 'string',
number: 'string'
},
adapter: {
name: 'listPhoneNumbers',
add: 'addListPhoneNumber',
replace: 'updateListPhoneNumber',
remove: 'deleteListPhoneNumber',
load: {
procedure: 'getListPhoneNumbers',
param: [],
key: 'listContacts'
}
}
};
WL.JSONStore.init(collections)
function showAllData() {
$('#show-all-btn').on("click", function() {
$('#info').show();
});
WL.JSONStore.get(collectionName).load().then(function(res) {
alert('ok' + JSON.stringify(res));
}).fail(function(errorObject) {
alert(errorObject);
});
}
This is the error:
[wl.jsonstore] {"src":"load","err":18,"msg":"FAILED_TO_LOAD_INITIAL_DATA_FROM_ADAPTER_INVALID_L‌​OAD_OBJ","col":"Contact","usr":"jsonstore","doc":{},"res":{}
The error message is saying the load object you passed is invalid. This is probably because you passed param instead of params. Notice the s at the end.
Also, this code:
WL.JSONStore.init(collections)
function showAllData() {
$('#show-all-btn').on("click", function() {
$('#info').show();
});
WL.JSONStore.get(collectionName).load().then(function(res) {
alert('ok' + JSON.stringify(res));
}).fail(function(errorObject) {
alert(errorObject);
});
}
Looks wrong, maybe what you meant to write is something like this:
WL.JSONStore.init(collections).then(function () {
WL.JSONStore.get(collectionName).count().then(function (numberOfDocsInCollection) {
if(numberOfDocsInCollection < 1) {
WL.JSONStore.get(collectionName).load().then(function(res) {
//handle success
})
}
})
});
I omitted handling failures for brevity. Note that the load will will duplicate items in the collection if those items already exist, hence the count to check if the collection is empty or not.