I have an AngularJS Factory (holds collection of Objects), and I am trying to find what item a user is requesting.
Factory
angular.module('app').factory('APIMethodService', [function() {
return {
apis: [
{
accounts: [
{
parameters : [
{
name : "Account",
version : "1.0"
}
],
uri: Head + "/v1/accounts/account_number",
methods : [
{
name: "Account Number",
desc: "Returns the account number."
}, {
name: "Account Money",
desc: "Returns the monetary amount within the account."
}
]
},
{
parameters : [
{
name : "Account",
version : "2.0"
}
],
uri: Head + "/v2/accounts/account_number",
methods: [
{
name: "Account Number",
desc: "Returns the account number."
}, {
name: "Account Money",
desc: "Returns the monetary amount within the account."
}, {
name: "Account Token",
desc: "Returns the account's token."
}
]
}
],
customers:[
{
parameters : [
{
name : "Customers",
version : "1.0"
}
],
uri: Head + "/v1/customers/customer_number",
methods : [
{
name: "Customer Name",
desc: "Returns the customer's name."
}, {
name: "Customer ID",
desc: "Returns the customer's ID."
}
]
},
{
parameters : [
{
name : "Customers",
version : "2.0"
}
],
uri : Head + "/v2/customers/customer_number",
methods: [
{
name: "Customer Name",
desc: "Returns the customer's name."
}, {
name: "Customer ID",
desc: "Returns the customer's ID."
}, {
name: "Customer Email",
desc: "Returns the customer's email."
}
]
}
]
}
]
};
}]);
Controller
angular.module('app').controller('apiTabController', ['$scope', '$route', '$location', 'APIMethodService', function($scope, $route, $location, APIMethodService) {
$scope.params = $route.current.params;
if ($scope.data == null && Object.keys($scope.params).length > 0) {
console.log("came from URL");
// Variables I get from URL
var name = $scope.params.name;
var version = $scope.params.version;
// My list of API's
$scope.currentAPI = APIMethodService.apis;
for (var i = 0, len = $scope.currentAPI.length; i < len; i++) {
// FIND THE API OBJECT I NEED
}
}
}]);
So I pull in name and version from the URL parameters (like apiTab?name=Customers&version=1.0), and then I am trying to get the right object from my Factory that matches the parameters.
So If I wanted Customers, version 1.0, that would be $scope.currentAPI[0].customers[0], and the URL that would have given me this would be apiTab?name=Customers&version=1.0
But I am unsure how to search and match up the right factory object..
You can write logic inside service to get proper object, or other possible solution could be to replace for loop in your controller with following one, it handles best case only:
var matchAPIObject = {}; // to capture right object
for(var i in currentAPI){
var targetAPI = currentAPI[i][name.toLowerCase()];
for(var j in targetAPI){
//console.log(targetAPI[j].parameters[0].name.toLowerCase());
if(targetAPI[j].parameters[0].name.toLowerCase() === name.toLowerCase()
&& targetAPI[j].parameters[0].version === version ){
matchAPIObject = targetAPI[j]; //found it now get me out
break;
}
}
}
Happy Helping!
Related
I'm looking for a way to replace a given string in a JSON by another string, but only for a certain key. For example, suppose I have the following JSON, data:
[
{
"name": "please, I said please",
"title": "Test One",
"more": [
{
"name": "another name",
"title": "please write something"
}
]
},
{
"name": "testTwo",
"title": "Test Two"
},
{
"name": "testThree",
"title": "Test Three"
},
{
"name": "testFour",
"title": "Test Four"
}
]
Let's say for example, I am looking to replace all occurrences of the word "please" by "could you". In this example, I only want to replace whole words. Right now, I've got a working example which does this:
const wordToReplace = "please"
const jsonString = JSON.stringify(data)
const reg = new RegExp(`\\b${wordToReplace}\\b`, 'gi) // whole words only and case insensitive
const dataReplaced = jsonString.replace(reg, function () {
return 'could you'
}
console.log(JSON.parse(dataReplaced))
Given the above code, the word 'please' will be replaced by 'could you' for all occurrences. However, I want to replace the words only if the key is "title". Right now, it will replace it for any given key (for example, in the first instance where the key is 'name' and also 'title').
One more thing to note is that the JSON can have a varying amount of nested properties as you can see from the example. It's not always the same nested objects within.
Also, the reason I've added a function in the replace method is because I wast trying out ways to specify a key.
No need to reinvent the wheel. Both JSON methods, JSON.parse and JSON.stringify are capable each of processing an additionally provided callback referred to ...
as either reviver ...
const sampleJSON = '[{"name":"please, I said please","title":"Test One","more":[{"name":"another name","title":"please write something"}]},{"name":"testTwo","title":"Test Two"},{"name":"testThree","title":"Test Three"},{"name":"testFour","title":"Test Four"}]'
const sampleData = JSON.parse(sampleJSON, (key, value) => {
const regX = (/\bplease\b/gmi);
return (key === 'title' && regX.test(value))
? value.replace(regX, 'could you')
: value;
});
console.log({ sampleData });
console.log('JSON.parse(sampleJSON) ...', JSON.parse(sampleJSON));
.as-console-wrapper { min-height: 100%!important; top: 0; }
or as replacer ...
const sampleData = [{
name: 'please, I said please',
title: 'Test One',
more: [{
name: 'another name',
title: 'please write something',
}],
}, {
name: 'testTwo',
title: 'Test Two',
}, {
name: 'testThree',
title: 'Test Three',
}, {
name: 'testFour',
title: 'Test Four',
}];
const sampleJSON = JSON.stringify(sampleData, (key, value) => {
const regX = (/\bplease\b/gmi);
return (key === 'title' && regX.test(value))
? value.replace(regX, 'could you')
: value;
});
console.log({ sampleJSON });
console.log("JSON.stringify(sampleData) ...", { stringifed: JSON.stringify(sampleData)});
.as-console-wrapper { min-height: 100%!important; top: 0; }
I wont recommend using replace on a json converted to string.
Instead, I'd recursively loop through the array/objects and alter the value if needed
Simple example:
const data = [{"name": "please, I said please", "title": "Test One", "more": [{"name": "another name", "title": "please write something"} ] }, {"name": "testTwo", "title": "Test Two"}, {"name": "testThree", "title": "Test Three"}, {"name": "testFour", "title": "Test Four"} ];
const regx = new RegExp(`\\bplease\\b`, 'gi');
function replace(obj) {
for (var k in obj) {
if (typeof obj[k] == "object" && obj[k] !== null) {
replace(obj[k]);
} else if (k === 'title') {
obj[k] = obj[k].replaceAll(regx, 'could you');
}
}
return obj;
}
const result = data.map(o => replace(o));
console.log(result);
I have two dropdowns - where each dropdown should filter an objects key. The dropdowns should not exclude each other, or both values from dropdown should work indenpentedly from each other (ie both dropdown values does not need to be true for filtering).
When I select an item from the dropdown, I get one array with two objects, for each dropdown:
[
{
"name": "Type",
"value": [
"calibration"
],
"selected": [
{
"text": "calibration"
}
]
},
{
"name": "Function group",
"value": [
"1 - Test",
"2 - Programming"
],
"selected": [
{
"text": "1 - Test"
}
]
}
]
Above shows two objects, for the two different dropdowns - one with name "type" and one with "Function group".
The "value" in the object above is all of the dropdown items.
"selected" holds the selected item from the dropdown and the filtering should be based on that.In this case we have selected "calibration" and "Test".
The "type" dropdown should filter on the data "category" field while the "function group" should filter on "groupDescription" field. The data that needs to be filtered based on the mentioned keyes and selected values looks like this:
const mockData = [
{
operationDetails: {
id: '5119-03-03-05',
number: '41126-3',
description: 'Clutch wear, check. VCADS Pro operation',
category: 'calibration', //type dropdown
languageCode: 'en',
countryCode: 'GB'
},
functionDetails: {
groupId: 411,
groupDescription: 'Test', //function group dropdown
languageCode: '',
countryCode: ''
},
lastPerformed: '2021-02-22',
time: 20,
isFavorite: false
}
,
{
operationDetails: {
id: '5229-03-03-05',
number: '41126-3',
description: 'Defective brake pad',
category: 'calibration', ///type dropdown
languageCode: 'en',
countryCode: 'GB'
},
functionDetails: {
groupId: 411,
groupDescription: 'Programming', //function group dropdown
languageCode: '',
countryCode: ''
},
lastPerformed: '2020-01-22',
time: 20,
isFavorite: false
}
]
Playground with mock data and response example from dropdown here.
How to filter the data based on the values from the dropdown objects, for each key its responsible for?
It's not the prettiest code, but it does work. The one thing that you'd want to watch out for is the regex. It would be better to not have to parse and do a straight match like category, but if your cases are static then you should be able to figure out if this will work every time. It would also be nice to have a field key in filterDetails so you know which field to try to match in the actual data and you could program that in.
const filterDetails = [
{
name: "Type",
value: ["calibration"],
selected: [
{
text: "calibration",
},
],
},
{
name: "Function group",
value: ["1 - Test", "2 - Programming"],
selected: [
{
text: "Test",
},
],
},
];
const mockData = [
{
operationDetails: {
id: "5119-03-03-05",
number: "41126-3",
description: "Clutch wear, check. VCADS Pro operation",
category: "calibration", //type
languageCode: "en",
countryCode: "GB",
},
functionDetails: {
groupId: 411,
groupDescription: "Test", //function group
languageCode: "",
countryCode: "",
},
lastPerformed: "2021-02-22",
time: 20,
isFavorite: false,
},
{
operationDetails: {
id: "5229-03-03-05",
number: "41126-3",
description: "Defective brake pad",
category: "calibration", ///type
languageCode: "en",
countryCode: "GB",
},
functionDetails: {
groupId: 411,
groupDescription: "Programming", //function group
languageCode: "",
countryCode: "",
},
lastPerformed: "2020-01-22",
time: 20,
isFavorite: false,
},
];
console.log(
"filtered mockData: ",
mockData.filter(({ operationDetails, functionDetails }) => {
let groupDescriptionMatch = false;
let categoryMatch = false;
for (const details of filterDetails) {
if (
details.name === "Type" &&
details.selected[0].text === operationDetails.category
)
categoryMatch = true;
if (details.name === "Function group") {
let parsedGroup = details.selected[0].text.match(/[a-zA-Z]+/g);
if (parsedGroup[0] === functionDetails.groupDescription) {
groupDescriptionMatch = true;
}
}
}
return groupDescriptionMatch && categoryMatch;
})
);
Hi get the JOSN data dynamically, based on the JOSN will render data in the table. In the JSON nested objects come and those names and properties keys also dynamically. I need to sort based on the table columns up and down button action ASC and DESC. On button action, I get the property names to sort, that properties key names will be placed inside the nested objects or it might be in the upper level. How to identify and sort dynamically number, strings or dates. Greate appreciate.
Below logic, I have added only work for the Single level JSON objects, not nested level objects.
dynamicJSON.sort((a, b) => {
if (a[sortKeyname] < b[sortKeyname]) {
return sortConfig.direction === 'ascending' ? -1 : 1;
}
if (a[sortKeyname] > b[sortKeyname]) {
return sortConfig.direction === 'ascending' ? 1 : -1;
}
return 0;
});
Below is the Sample Dynamic JSON data
From the below JSON, I have 7 columns, If I select the status column ASC/ DESC button, I get statusname property for the sort. it should traverse and sort nested JSON objects based on the key properties statusname. In case, if I select the Details column ASC/ DESC button,I get maindescription property for the sort. it should traverse and sort nested JSON objects based on the key properties maindescription.
[
{
"Date":"2020-10-16T04:15:58+00:00",
"applicationName":"abc Portal",
"status":{
"statusname":"Success",
"style":"success"
},
"details":{
"maindescription":"welcome to the application",
"maindescriptionSecn":"description 2",
"maindescriptionSecnthrid":"description 3"
},
"location":"Sondekoppa, Karnataka, IN",
"ipAddress":"157.49.147.190",
"count":123
},
{
"Date":"2020-10-16T04:15:56+00:00",
"applicationName":"poratl 1",
"status":{
"statusname":"Success",
"style":"success"
},
"details":{
"maindescription":"welcome to the application",
"maindescriptionSecn":"description 2",
"maindescriptionSecnthrid":"description 3"
},
"location":"Sondekoppa, Karnataka, IN",
"ipAddress":"157.49.147.190",
"count":789
},
{
"Date":"2020-10-16T04:21:41+00:00",
"applicationName":"app Hub",
"status":{
"statusname":"Failure",
"style":"error"
},
"details":{
"maindescription":"welcome to the application",
"maindescriptionSecn":"description 2",
"maindescriptionSecnthrid":"description 3"
},
"location":"Sondekoppa, Karnataka, IN",
"ipAddress":"157.49.147.190",
"count":666
}
]
You could use a closure over the wanted key/keys and direction and use the returned function for sorting.
The sorting funtion uses function which splits the handed over key string by dot and gets the value from the (nested) object.
const
sortBy = (key, direction = 'ascending') => (a, b) => {
const
factor = +(direction === 'ascending') || -1,
getValue = (object, keys) => keys.split('.').reduce((o, k) => o?.[k], object),
valueA = getValue(a, key),
valueB = getValue(b, key);
return factor * (valueA > valueB || -(valueA < valueB));
},
data = [{ Date: "2020-10-16T04:15:58+00:00", applicationName: "IAM Portal/IAM Portal", status: { foo: 'b', statusname: "Success", style: "success" }, details: { maindescription: "welcome to the application", maindescriptionSecn: "description 2", maindescriptionSecnthrid: "description 3" }, location: "Sondekoppa, Karnataka, IN", ipAddress: "157.49.147.190", count: 123 }, { Date: "2020-10-16T04:15:56+00:00", applicationName: "IAM Portal/IAM Portal", status: { foo: 'a', statusname: "Success", style: "success" }, details: { maindescription: "welcome to the application", maindescriptionSecn: "description 2", maindescriptionSecnthrid: "description 3" }, location: "Sondekoppa, Karnataka, IN", ipAddress: "157.49.147.190", count: 789 }, { Date: "2020-10-16T04:21:41+00:00", applicationName: "IAM Portal/Reports Hub", status: { foo: 'c', statusname: "Failure", style: "error" }, details: { maindescription: "welcome to the application", maindescriptionSecn: "description 2", maindescriptionSecnthrid: "description 3" }, location: "Sondekoppa, Karnataka, IN", ipAddress: "157.49.147.190", count: 666 }];
data.sort(sortBy('status.foo'));
console.log(data);
data.sort(sortBy('status.foo', 'descending'));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm able to create to create a dummy user object at bootstrap before adding a "Date" attribute. In bootstrap.js I have:
module.exports.bootstrap = function(cb) {
var dummyData = [
{
"firstName":"Jane",
"lastName":"Doe",
"dateofbirth": 1279703658 //timestamp
}
]
User.count().exec(function(err, count){
if(err){
return cb(err);
}
if(count == 0){
User.create(dummyData).exec(function(){
cb();
});
}
});
};
User.js is simple and looks like this:
module.exports = {
attributes: {
firstName : {
type : 'string',
required : true
},
lastName : {
type : 'string',
required : true
},
dateofbirth : {
type : 'date'
}
}
};
when I try creating the same object in browser (sails nice CRUD functionality) I get an error about the date:
{
"error": "E_VALIDATION",
"status": 400,
"summary": "1 attribute is invalid",
"model": "User",
"invalidAttributes": {
"dateofbirth": [
{
"rule": "date",
"message": "`undefined` should be a date (instead of \"123454345\", which is a string)"
}
]
}
}
So question is how can I create such object with date attribute?
date (or the equivalent dateTime) accepts e.g. an ISO date string. So, your example could look like:
var dummyData = [
{
"firstName":"Jane",
"lastName":"Doe",
// "1970-01-15T19:28:23.658Z"
"dateofbirth": new Date(1279703658).toISOString()
}
]
Basically I want to validate my observable before apply the bindings, so that I will never get that something something is not defined error.
Say I do have a javascript class that defines all that I need, and I want to validate an observable created from ajax against it.
Is there a generic way to do it?
Edit:
http://jsfiddle.net/rgneko/GuR8v/
Currently the demo will throw error because one of the items has no id property. I want to verify whether all items are valid.
$(document).ready(function () {
function ModelToTestAgainst(id, name, type, items) {
this.id = id;
this.name = name;
this.type = type;
this.items = items;
}
var data = {
items: [{
id: 1,
name: 'name1',
type: 'folder',
items: []
}, {
id: 2,
name: 'name2',
type: 'file',
items: []
}, {
name: 'name2',
type: 'file',
items: []
}]
};
var someRandomObservaleIGotFromWherever = ko.mapping.fromJS(data);
// I want to Validate(someRandomObservaleIGotFromWherever) before apply bindings
ko.applyBindings(someRandomObservaleIGotFromWherever);
});
There is a standard JSON Schema, defined in http://json-schema.org/.
An schema can be as simple as:
var schema = {"type" : "object"};
which requires the value to be an object. Or much more complex, like this example from json-schema.org:
{
"title": "Example Schema",
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}
which requires several properties to exists, defines their types, and even a minimun value for one of them.
Then you need a library that allows to validate the JS objects with this kind of schema. In the same site, you can find a list of libraries for validating (and parsing, creating documentation...). Note that not all libraries are updated, and not all of them support the latest JSON schema version.
For example, using JSV, you can make a validation simply like this:
var report = env.validate(json, schema);
Why don't you map/wrap it into a view model that you know has all the properties that you need?
function ViewModel(model) {
this.x = ko.observable(model.x || 0);
this.y = ko.observable(model.y || 0);
}
var original = { x: 27 }; // Use your ajax object here.
var viewModel = new ViewModel(original);
What about using JSON Schema validation.
I have used library from Tiny Validator to enable validation. Validation is easy to add as an observable extension
$(document).ready(function () {
// Attach a validationSchema method to all observable types
ko.subscribable.fn["validateSchema"] = function(schema){
// Convert the observable back to an object literal to test.
var source = ko.mapping.toJS(this());
return tv4.validate(source, schema);
};
// Define schema for ModelToTestAgainst
var modelSchema = {
"$schema": "http://tempuri.org/ModelToTestAgainst",
"title": "ModelToTestAgainst Set",
"type": "array",
"items": {
"title": "ModelToTestAgainst",
"type": "object",
"properties" : {
"id" : {
"type": "number",
"minimum" : 1
},
"name" : { "type": "string" },
"type" : { "type": "string" },
"items" : { "type" : "array" }
},
"required": ["id", "name", "type", "items"]
}
};
function ModelToTestAgainst(id, name, type, items) {
this.id = id;
this.name = name;
this.type = type;
this.items = items;
}
var data = {
items: [{
id: 1,
name: 'name1',
type: 'folder',
items: []
}, {
id: 2,
name: 'name2',
type: 'file',
items: []
}, {
name: 'name2',
type: 'file',
items: []
}]
};
var obs = ko.mapping.fromJS(data));
var isValid = obs.items.validateSchema(modelSchema);
if( isValid ) {
ko.applyBindings(obs);
}
});