i have an object that stores a table for converting a json response in a user readeable text. The thing is, the same text applies for multiple json conditions.
I want to know if there's a way to set the same value for 2 different Id's in a object.
Like, instead of:
var msg = {
"error_code1": "user_msg",
"error_code2": "user_msg",
}
Something like this
var msg = {
"error_code1" && "error_code2": "user_msg",
}
Is it possible?
First approach is using "getter" functions to reference the initial object:
var errorBindings = {
'errorCode101': 'Unauthorized request',
get 'errorCode102'() { return this.errorCode101; },
get 'errorCode103'() { return this.errorCode101; },
'errorCode104': 'All fields are required',
get 'errorCode105'() { return this.errorCode104; },
};
console.log(errorBindings.errorCode103); // "Unauthorized request"
Second approach is using an anonymous function with referencing properties from which we should borrow the needed value:
var messages = function(o) {
o.errorCode101 = 'Authorization error!';
o.errorCode102 = o.errorCode101;
o.errorCode103 = o.errorCode101;
o.errorCode104 = 'All fields are required';
return o;
}({});
console.log(messages.errorCode101); // "Authorization error!"
console.log(messages.errorCode103); // "Authorization error!"
What about:
var msg = {};
msg['a'] = msg['b'] = 'value';
But for such simple cases I prefer to repeat myself instead of doing premature optimizations.
Unfortunately in JSON all keys must be strings so what you are attempting will not work.
As a work around I can suggest storing a reference to the value of the user_msg and assigning it to each error code key.
For example:
var storedMessage = "user_msg";
var msg = {
"error_code1": storedMessage,
"error_code2": storedMessage
}
Let me know if you have any questions.
Related
I am looking for a way to make my JS errors handling function dynamic, now i am checking each possible error seperately, the function code looks like this.
function handleErrors(errors){
if (errors.nameError){
nameEl.classList.add('is-invalid');
nameEl.nextElementSibling.innerHTML = errors.nameError;
}
if (errors.surnameError){
surNameEl.classList.add('is-invalid');
surNameEl.nextElementSibling.innerHTML = errors.surnameError;
}
if (errors.emailError){
emailEl.classList.add('is-invalid');
emailEl.nextElementSibling.innerHTML = errors.emailError;
}
if (errors.passwordError){
passwordEl.classList.add('is-invalid');
passwordEl.nextElementSibling.innerHTML = errors.passwordError;
}
if (errors.passwordConfirmError){
passwordConfirmEl.classList.add('is-invalid');
passwordConfirmEl.nextElementSibling.innerHTML = errors.passwordConfirmError;
}
if (errors.phoneError){
phoneEl.classList.add('is-invalid');
phoneEl.nextElementSibling.innerHTML = errors.phoneError;
}
if (errors.addressError){
addressEl.classList.add('is-invalid');
addressEl.nextElementSibling.innerHTML = errors.addressError;
}
}
Basically each time i have two variables, i am checking if there exists specific error object and if it exists i am printing that error value to the corresponding HTML element. What would be the way to write a function that would check all errors dynamically. Thank you for your thoughts.
You could do it like that:
var errorsAndElements = {
nameError: nameEl,
surnameError: surNameEl,
emailError: emailEl,
passwordError: passwordEl,
passwordConfirmError: passwordConfirmEl,
phoneError: phoneEl,
addressError: addressEl
};
function handleErrors(errors){
var possibleErrors = Object.keys(errorsAndElements);
for (var i = 0; i < possibleErrors.length; i++) {
var errorName = possibleErrors[i];
if (errors[errorName]) {
var errorElement = errorsAndElements[errorName];
errorElement.classList.add('is-invalid');
errorElement.nextElementSibling.innerHTML = errors[errorName];
}
}
}
It stores the possible errors and according elements in an object, then it iterates over that object and checks if the error exists in the passed error object. If the error exists, it takes the element and displays it.
I am using a complex object graph serialized to JSON with MVC4/jQuery/Sammy/Rivets for SPA functionality.
I have a object graph that looks a bit like this when serialized to JSON (obviously mocked-up):
model =
{
Name: "Me",
Age: 22,
Hobbies:
[
{ Name: "Biking", IsActive: true },
{ Name: "Programming", IsActive: true }
]
}
Everything works quite well until I need Unobtrusive validation, since my Hobbies are in a SlickGrid and I am managing all the data myself. To handle this I am returning my ModelState with my JSON next to my model.
return JSON(new { model = model, modelState = this.ModelState });
From there I intend to iterate through the modelState and assign errors to the right place with some custom function, but there is one problem.
ModelState looks like this:
"Name",
"Age",
"Hobbies[0].Name",
"Hobbies[0].IsActive",
"Hobbies[1].Name",
"Hobbies[1].IsActive"
I need to separate the [0]'s into an object and [1]'s into their own objects so I can smoothly get the values. This gets confusing for me when I begin to account for a third level of complex object array.
Solution:
var ModelStateConverter = function ($, module) {
module = module || {};
// Convert The ModelState form style object to a standard JS object structure.
module.toObject = function (modelState) {
var ModelState = {};
$.each(modelState, function (key, value) {
AssignValuesToObjectStore(key, ModelState, value);
});
return ModelState;
}
// item is the full identifier ex. "Hobbies[0].Name"
// store is the object we are going to throw arrays, objects, and values into.
// value is the error message we want to get in the right place.
// index is an internal processing parameter for arrays only, setting it's value has no effect.
function AssignValuesToObjectStore(item, store, value, index) {
var periodMatch = item.match(/[\.]/);
if (periodMatch === null) {
if (Array.isArray(store)) {
if (store[index] === undefined) {
store[index] = {};
}
store[index][item] = value;
}
else {
store[item] = value;
}
}
else {
// This wasn't a simple property or end of chain.
var currentProperty = item.slice(0, periodMatch.index); // Get our property name up to the first period.
var container = {}; // We assume we are dealing with an object unless proven to be an array.
var arrayIndex; // This is irrelevant unless we have an array.
if (currentProperty.slice(-1, currentProperty.length) === "]") {
// We are dealing with an array! Hoo Ray?!
arrayIndex = parseInt(currentProperty.slice(currentProperty.indexOf("[") + 1, currentProperty.indexOf("]")));
currentProperty = currentProperty.slice(0, currentProperty.indexOf("[")); // remove the indexer ex. [0] so we are left with the real name
container = []; // We know we need an array instead;
}
if (store[currentProperty] === undefined) {
store[currentProperty] = container; // If this property isn't already created, then do so now.
}
//Recurseive nature here.
AssignValuesToObjectStore(item.slice(periodMatch.index + 1, item.length), store[currentProperty], value, arrayIndex);
}
}
return module;
}($, ModelStateConverter);
You can call this from:
ModelStateConverter.toObject(data.modelState);
Where data.modelState is assumed to be the ModelState from the server.
You could try a library like JSON.NET, or the class JavaScriptSerializer, to serialize the ModelState.
I am stuck with the following situation. I have a select statement which uses a function in the current scope me. How do I go about putting me into the select function?
var me = this;
var results = Enumerable
.from(jsonData)
.select('x,i=>{abbr:me.transform(x), name:x}')
.toArray(); //me.transform(x) will hit error
'me' is an instance of a dynamically generated object, and me.transform(x) uses other dependencies in 'me' to work as well. That means I cannot make 'me.transform()' global function.
EDIT
var me = this;
var results = Enumerable
.from(jsonData)
.select(function(x,i){
return {abbr:me.transform(x), name:x};
}).toArray();
Actually, this modification will work, however, I would like to find out the how to make the shortcut syntax work.
What you could do is project your objects to a composite object containing both the item in the collection and the object you want to introduce into the query.
You can use this Capture function to capture the variables:
function Capture(bindings, name) {
var benumerable = Enumerable.From(bindings),
itemname = name || 'Item';
return function (e) {
return e.Select(function (item) {
return benumerable.Concat(Enumerable.Return({ Key: itemname, Value: item }))
.ToObject("$.Key", "$.Value");
});
};
}
Use it in a Let binding.
var query = Enumerable.From(data)
.Let(Capture({ Me: me }))
.Select("{ abbr: $.Me.transform($.Item), name: $.Item }")
.ToArray();
My bad. Is this what you mean?
var me = this;
var results = Enumerable
.from(jsonData)
.select('x,i=>{abbr:' + me.transform(x) + ', name:x}')
.toArray(); //me.transform(x) will hit error
I get undefined whenever I get the value of a property of an object.
function run(id){
var report = services.getReportInfo(id);
var childReport = {
id: newGuid(),
parentId: report.id, // i get undefined
reportPath: report.path // i get undefined
};
...
}
services.js
angular.module('project.services').factory('services', function(){
var reports = [
{
....
},
{
....
}
];
function getReportInfo(id){
var report = reports.filter(function(element){
return element.id === id;
});
};
return{
getReportInfo: getReportInfo
};
}
Whenever I put breakpoint on my var report = services.getReportInfo(id) it could contains the correct values for each property of the my report object. However, when I get the report.id or report.path, I get undefined value.
--Edited--
Oh, I know now where I got wrong.
The getReportInfo function returns an array and I'm accessing the properties without telling from what index should it get the values for the said properties.
function run(id){
var report = services.getReportInfo(id);
var childReport = {
id: newGuid(),
parentId: report[0].id,
reportPath: report[0].path
};
...
}
I placed static index 0, since I know that the array will always have a length of 1.
You are not returning anything from the .factory method and the getReportInfo is also not returning anything. For what you are trying to do, try to use .service method:
angular.module('project.services').service('services', function(){
var reports = [
{
....
},
{
....
}
];
this.getReportInfo = function (id){
var report = reports.filter(function(element){
return element.id === id;
});
return report;
}
}
Here is a good explanation on how to use .factory and .service:
Confused about Service vs Factory
Two immediate issues with the code I can see:
1) Your factory function needs to return a value or constructor function. Right now your code is not initializing the factory to any value.
2) Your getReportInfo function also doesn't return a value, yet you are assigning the function result to a variable.
Read more here: http://docs.angularjs.org/guide/dev_guide.services.creating_services
I'm tring to get some json to contain a function so that the option in the plugin gets the returned value of the function that was in the json string.
The set up is as such:
default.txt
[
{
"element":"a[href$='.jpg']",
"options":{
"label":"test"
}
},{
"element":"a#hover",
"options":{
"label":(function(ele){ return 'test'; })()
}
}
]
and the plug-in is
(function($) {
function defined(obj){return typeof(obj)!=='undefined';}
function evaluate(ele, obj) {
if(typeof obj === 'function') {
//alert('is function');
obj = obj(ele);
}else{
try{
//alert('thinking it may be a function still');
obj = eval("("+obj+"("+ele+"));");
//obj = (obj)(ele);
}catch(err){
//Handle errors here
//alert('not function');
}
}
return obj;
}
function debug(message) {
console.debug(message);
}
$.set = function(options){
var settings = $.extend({}, {}, options);
if(defined(settings.actions)){
$.each(settings.actions, function(index, value) {
$(value.element).do_action(defined(value.options)?value.options:null);
});
}
}
$.fn.do_action = function(options) {
// Add event handler to all matching elements
return this.each(function() {
var ele = $(this);
var settings = $.extend({}, $.fn.do_action.defaults, options);
var label= evaluate(ele, settings.label);
var message = "label:'" + label + "'";
debug('Tracking ' + action + ' ' + message);
});
};
$.fn.do_action.defaults = {
label : function(ele) { return ele.attr('href'); }
};
}(jQuery));
and the control is
$.getJSON('default.txt' , function(data){
$.set({
actions:data // this si where we pull in the options
});
});
I seems to work if I put in an alert inside the anonymous function, so from
"label":(function(ele){ return 'test'; })()
to
"label":(function(ele){ alert('test') })()
but after that I can't seem to get it to print right in the console as I only get the message
Tracking label:'(function(ele){ return 'test'; })
[EDIT]
Please stop saying that "you should not put code in json". If you work for Google and are their top level programmers getting paid the big super bucks then I'll listen to the why's, but I'm sorry if it's good for them, it'll work for me. Don't know what else to say, I already understand the point of why you should avoid it which I aim to do as well. There are times when you just can't and why i supect Google does the same.
Your label member is not a function; it's a value returned by a function. The final () at the end means "execute this function immediate whenever the value of label is requested and provide its return value as the value of label".
That said, if you must include functions in your JSON object (and from what you've said, you do) your best bet is to stringify-and-eval your functions. You should also be able to use raw functions in your JSON, like {foo : (function(){ ... }) }, though I've personally had a tricky time getting this right (but that's just me -- if you can get it right, go for it).
(As an aside, it's not possible to stringify functions from existing objects, but if you're getting your functions as text from user/programmer input, that's not an issue.)