I have a scope variable called jobs which is an array of objects for each department and their data.
[
“Accounting”: {
“foo” : “foo”,
“bar” : bar"
},
“Delivery”: {
“foo”: “foo”,
“bar”: “bar
}
]
The HTML5 date input requires that all dates be converted to using new Date() in javascript even though they are in the correct yyyy-mm-dd format specified. I could go through by hand and type in each date that needs to be converted, but I wanted to use a nested foreach to do so because I have a lot of dates to convert.
angular.forEach($scope.job, function(value, key){
angular.forEach(value, function(innerValue, innerKey){
if(typeof(innerValue) === "string"){
var matches = innerValue.match(/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/);
if(matches){
// Need to set $scope.job.key.innerKey = new Date($scope.job.key.innerKey) here
}
}
});
});
My issues is that I don’t know how to access the $scope.job object at the key and innerKey values I came to. How can I edit the current item being looped over? The documentation on using ‘this’ in a situation like this was almost impossible to find.
You just need to use [] object notation for variable property names
if(matches){
value[innerKey] = new Date(value[innerKey]);
}
Another approach would be to make a directive wrapping the date input and put the conversion logic in the controller of that directive. This way all the looping would be implicitly done by the framework, for all instances of the custom date input directive.
Related
Basically I have a complex object that retrieves the GPT API (google publisher tag) with this function:
googletag.pubads().getSlots();
The object value is something like this:
I need to know if there is a way to compare the value of each property with an X value without getting a problem of recursivity (because the object is huge and i need to to that validation several times)
Also, I tried to convert that object into a JSON with JSON.stringify(), and then tried to get the value with a regex, faster, but with this option, I have the problem with Cyclic Object Value.
Any suggestions ?
it's more simple. try it to convert it into an array and later use a filter for comparative with your value.
var objGoogle = {};
var arrayObjectGoogle = [objGoogle];
var filter = arrayObjectGoogle.filter(function(obj){
obj.yourAttr == yourValue; });
this will give you a second array with the values found it. later, index the array for pick up the value do you need.
I'm calling an external service and I get the returned domain object like this:
var domainObject = responseObject.json();
This converts the response object into a js object. I can then easily access a property on this object like this
var users = domainObject.Users
Users is a collection of key/value pairs like this:
1: "Bob Smith"
2: "Jane Doe"
3: "Bill Jones"
But CDT shows users as Object type and users[0] returns undefined. So how can I get a handle to the first item in the collection? I'm assuming that some type of type cast is needed but not sure how I should go about doing this
UPDATE
Here is one way I could access the values:
//get first user key
Object.keys(responseObject.json().Users)[0]
//get first user value
Object.values(responseObject.json().Users)[0]
But I need to databind through ng2 so I was hoping for a simpler way like this:
<div>
<div *ngFor="let user of users">
User Name: {{user.value}}
<br>
</div>
</div>
Maybe I should just create a conversion function in my ng2 component which converts the object into what I need before setting the databinding variable?
UPDATED ANSWER
So after scouring through a few docs I found the "newish" Object.entries() javascript function. You can read about it here. Pretty cool.
Anyways, give this a try. I am ashamed to say that I don't have time to test it, but it should get you going in the right direction.
usersArray = []
// Turn Users object into array of [key, value] sub arrays.
userPairs = Object.entries(users);
// Add the users back into an array in the original order.
for (i=0; i < userPairs; i++) {
usersArray.push(_.find(userPairs, function(userPair) { return userPair[0] == i }))
}
ORIGINAL ANSWER
I would use either underscore.js or lodash to do this. Both are super helpful libraries in terms of dealing with data structures and keeping code to a minimum. I would personally use the _.values function in lodash. Read more about it here.. Then you could use users[0] to retrieve the first item.
The only caveat to this is that lodash doesn't guarantee the iteration sequence will be the same as it is when the object is passed in.
users = _.values(users);
console.log(users[0]);
How about this:
let user= this.users.find(() => true)
This should return the "first" one.
If your initial object is just a plain object, how do you know it is sorted. Property members are not sorted, ie: looping order is nor guaranteed. I´d extract the user names into an array and the sort that array by the second word. This should work (as long as surnames are the second word, and only single spaces are used as separators).
var l=[];
for(var x in users) {
push.l(users[x]);
}
var l1=l.sort ( (a,b) => return a.split(" ")[1]<b.split(" ")[1]);
I have $scope.myArray, and it's binding with an input field by ngModel and the expression {{myArray}}
My issue is when I modified myArray by call changeMyArray(), the input's value did not change. But the expression {{myArray}} is display new value.
So, Why the expression work but input field does not?
I have a way to do, but I want to find a better approach
var newArr = $scope.myArray;
newArr.push("b");
$scope.myArray = angular.copy(newArr);;
Example fiddle
Basically, I think what you want to do is bind the input to a "new entry" scope variable, and then push the value of that variable to your array when the user clicks "Push To". Here's what I mean:
In controller:
$scope.changeMyArray = function() {
$scope.myArray.push($scope.newEntry);
$scope.newEntry = "";
}
In HTML:
<input ng-model="newEntry">
But actually:
Really what you want is a way to edit the contents of an array via text, and have updates to that array from elsewhere also update the text. This is actually pretty simple since browsers come with a JSON library.
I implemented it by starting with a known pair of objects:
$scope.myArray = [];
$scope.myArrayString = "[]";
That way you can update the string via ngModel:
<input ng-model="myArrayString">
Watch for changes on this model to update the actual array:
$scope.$watch("myArrayString", function() {
$scope.myArray = JSON.parse($scope.myArrayString);
});
Then update the string in the changeMyArray function:
$scope.changeMyArray = function() {
$scope.myArray.push("b"); // Or whatever you would like to add here
$scope.myArrayString = JSON.stringify($scope.myArray);
}
Experiment in my fork of the Fiddle.
What's going on?
The variable $scope.myArray is an object, and any object in Javascript can be converted to a string (most complex objects end up as the unhelpful "[object Object]"). Arrays will actually display their contents when converted to a string, so binding an array to HTML via {{myArray}} is pretty straightforward.
However, the reverse conversion is not as simple. In general, a text input can't be bound to an array in a two-way fashion as we'd like. The solution, then, is to use an intermediary variable to hold the string value, and use $scope.$watch to keep the two values in sync.
So you seem to be wondering why when pushing to the array, your $watch function doesn't do the increment. That's because the #watch function only checks object reference equality.
When pushing to the array, the reference stays the same. When you copy the array and set it again in the same variable, the reference changes.
That's why #watchCollection works as expected and increments when each item is pushed.
I have an explanation for my question. Please correct me if I wrong, very thank.
My Question:
Why "myArray" input field does not update when $scope.myArray is changed (Model doesn't update View)?
<input ng-model="myArray" id="myArray">
The answer is AngularJs ng-model doesn't know $scope.myArray is changed. Because ng-model does not perform a deep watch of object (rather than a string or number), it only looks for a change of identity or compares the reference of the objects.
In my case, $scope.myArray is collection. So, although $scope.myArray has changed by push new item (structure is changed), it's reference does not change.
As the result, $setViewValue() and $render() never invoked to update the view.
$render
$setViewValue
Solution:
Sol1: Add new item to $scope.myArray, make a copy of myArray object and then asign a copy to $scope.myArray again. By this way, the object reference is changed. AngularJs see that change and update the view.
var newArr = $scope.myArray;
newArr.push("b");
$scope.myArray = angular.copy(newArr);
Sol2: Create custome $watch('email', function(){...}, true). The last parameter is TRUE to let Angular perform a deep watch. Then, in watch's listener function, I manually set $viewValue = newValue and invoke $render() of ngModelController to update the view. In case we have Formatters, we should invokes them in this step.
$scope.$watch('myArray', function(newValue, oldValue) {
if (newValue !== oldValue) {
var ctrl = angular.element(document.querySelector('#myArray')).controller('ngModel');
// Invoke formatter
var formatters = ctrl.$formatters,
idx = formatters.length;
while(idx--) {
newValue = formatters[idx](newValue);
}
ctrl.$render();
}
}, true);
Please see my script
I have a grid using jqgrid. I need to store the some data returned by the url:, in a local variable so that I can display it in the subgrid. For this I add that column as a hidden column in the main grid, which is of the format in json:
"Details":[{"Name":"ttt", "Quantity":"12"}] .
Then in the loadcomplete: function I save the value to a variable using
var griddata = $("#grid').getCol('Details', false);
Now when I access griddata[0], I get a object Object. I tried to parse it to get the values correctly, but to no avail. I tried:
griddata[0].Details.Name
or
griddata[0].Details.0.Name
or
griddata[0].Name,
but all failed. I guess I am missing the format if the data returned by the getcol() function.I looked up the documentation on the method and it says that it returns just the values when we specify false, but could not get any examples.
If there are any examples or if there are any pointer to the solution, it will be greatly appreciated.
If you check the type of griddata[0] (for example with typeof operator) you will see that it has type of string - this is because the value you are getting is the result of toString() on the object you have passed. The reason for that is the way in which jqGrid is treating values.
If you want to store JSON as the value, you need to stringify it entirely:
"Details": "[{\"Name\":\"ttt\", \"Quantity\":\"12\"}]"
Now you can deserialize those string values into objects like this:
var griddata = $.map($("#grid").jqGrid('getCol', 'Details', false), function(value) {
return JSON.parse(value);
});
After that you can access the objects in the way you want:
var name = griddata[0].Name;
var quantity = griddata[0].Quantity;
As an alternative you can split your object into two columns, so the values can be accessed directly with getCol.
I am working on an app that heavily uses JavaScript. I am attempting to include some object-oriented practices. In this attempt, I have created a basic class like such:
function Item() { this.init(); }
Item.prototype = {
init: function () {
this.data = {
id: 0,
name: "",
description: ""
}
},
save: function() {
alert("Saving...");
$.ajax({
url: getUrl(),
type: "POST",
data: JSON.stringify(this.data),
contentType: "application/json"
});
}
}
I am creating Item instances in my app and then saving them to local storage like such:
Item item = new Item();
window.localStorage.setItem("itemKey", JSON.stringify(item));
On another page, or at another time, I am retriving that item from local storage like such:
var item = window.localStorage.getItem("itemKey");
item = JSON.parse(item);
item.save();
Unfortunately, the "save" function does not seem to get reached. In the console window, there is an error that says:
*save_Click
(anonymous function)
onclick*
I have a hunch that the "(anonymous function)" is the console window's way of saying "calling item.save(), but item is an anonymous type, so I am trying to access an anonymous function". My problem is, I'm not sure how to convert "var item" into an Item class instance again. Can someone please show me?
Short answer:
Functions cannot be serialized into JSON.
Explanation:
JSON is a cross-platform serialization scheme based on a subset of JS literal syntax. This being the case, it can only store certain things. Per http://www.json.org/ :
Objects: An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).
Arrays: An array is an ordered collection of values. An array begins with [ (left bracket) and ends with ] (right bracket). Values are separated by , (comma).
values: A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested.
Functions cannot be serialized into JSON because another non-JS platform would not be able to unserialize and use it. Consider the example in reverse. Say I had a PHP object at my server which contained properties and methods. If I serialized that object with PHP's json_encode() and methods were included in the output, how would my JavaScript ever be able to parse and understand PHP code in the methods, let alone use those methods?
What you are seeing in your resulting JSON is the toString() value of the function on the platform you're using. The JSON serilizer calls toString() on anything being serialized which isn't proper for JSON.
I believe your solution is to stop storing instances in JSON/local storage. Rather, save pertinent data for an instance which you set back to a new instance when you need.
I know this question is answered already, however I stumbled upon this by accident and wanted to share a solution to this problem, if anyone is interested.
instead of doing this:
var item = window.localStorage.getItem("itemKey");
item = JSON.parse(item);
item.save();
do something like this:
// get serialized JSON
var itemData = window.localStorage.getItem("itemKey");
//instantiate new Item object
var item = new Item();
// extend item with data
$.extend(item, JSON.parse(itemData));
// this should now work
item.save();
this will work so long as the function you are wanting to call (ie, save()) is prototypal and not an instance method (often times the case, and is indeed the case in the OP's original question.
the $.extend method is a utility method of jquery, but it is trivial to roll your own.
You cant do that, how can javascript possibly knows that item have a save function ? json doesnt allow functions as datas. just read the json spec , you cant save functions.
what you need to do is to create a serialize and deserialize method in the hash you want to stock. that will specifiy what to export and how you can "wake up" an object after parsing the corresponding json string.
You can only store plain Objects in DOMstorages (cookies, urlparams..., everything that needs [de]serialisation through JSON.stringify/JSON.parse). So what you did when sending the ajax data
ajaxsend(this.data);
also applies to string serialisation. You can only store the data, not the instance attributes (like prototype, constructor etc.). So use
savestring(JSON.stringify(item.data));
which is possible because item.data is such a plain Object. And when restoring it, you will only get that plain data Object back. In your case it's easy to reconstruct a Item instance from plain data, because your Items hold their values (only) in a public available property:
var item = new Item;
item.data = JSON.parse(getjsonstring());
Disclaimer
Not a full time time J.S. Developer, answer may have some minor bugs:
Long Boring Explanation
As mentioned by #JAAulde, your object cannot be serialized into JSON, because has functions, the technique that you are using doesn't allow it.
Many people forget or ignore that the objects that are used in an application, may not be exactly the same as saved / restored from storage.
Short & quick Answer
Since you already encapsulate the data members of your object into a single field,
you may want to try something like this:
// create J.S. object from prototype
Item item = new Item();
// assign values as you app. logic requires
item.data.name = "John Doe";
item.data.description = "Cool developer, office ladies, love him";
// encoded item into a JSON style string, not stored yet
var encodedItem = JSON.stringify(item.data)
// store string as a JSON string
window.localStorage.setItem("itemKey", encodedItem);
// do several stuff
// recover item from storage as JSON encoded string
var encodedItem = window.localStorage.getItem("itemKey");
// transform into J.S. object
item.data = JSON.parse(encodedItem);
// do other stuff
Cheers.