Pushing into JSON suddenly not working - javascript

I'm a beginner in AngularJS, and I have a problem with pushing into an array inside a JSON. (I hope I'm using the correct terminology)
//I have a JSON that looks like this
var items = [
{name:[
{names: 'John', msg:[""]}, {names: 'Dolly', msg:[""]}
]}
];
Below code is inside a Controller:
$rootScope.items = People.list();
$rootScope.name = "John";
for(var i=0;i<$rootScope.items.name.length;i++)
{
if (name== $rootScope.items.name[i].names)
{
People.addcomment(user.comment,i);
}
}
Below code is in my service called 'People':
this.addcomment = function(comment,x) {
items[0].name[x].msg.push(comment);
}
this.list = function () {
return items[0];
}
The JSON is declared in the service. The HTML code is as below:
<textarea placeholder="Enter comment" ng-model="user.comment"></textarea>
<button ng-repeat = "x in items.name" ng-if="name==x.names"
ng-click="addcomment(user,name)">Submit</button>
Now, my problem is that on clicking the 'Submit' button, the 'message' I have typed in the teaxtarea is not getting stored in the 'msg' array corresponding to "John" (inside of the JSON). What is going wrong?
I had the entire code of the Service inside of the Controller earlier, and the code used to work properly then. I suppose I must have done some mistake with the arguments I'm passing to the function in the service.
Could someone please help me out?

So the workflow of you app is as follows:
Service is instantiated, JSON is stored in the scope of your service
Controller is instantiated, copies the array from People.List() to rootScope.items
The ng-repeats repeats for each Element in the rooScope.items - NOT for each Element in the items array in your People-service
Someone enters a new message
This message is added to items in your service with People.addComment.
Step number 5 only changes the items in your service, NOT the items in your rootScope. That's why the ng-repeat doesn't add a new Element.
To keep those two scopes in sync, add a function to your controller:
function updateList() {
$rootScope.items = People.list();
}
Call this method after you call People.addComment().
Lastly, maybe this is just an opinion, you should change your JSON-structure. In gets ways clearer if you set up your data like this:
var items = [
{name: 'John', msg:[]},
{name: 'Dolly', msg:[]}
];
Now you have an array of objects. Your People.list() function now returns items and you can add a new Person by calling items.push({name: 'Judith', msg:[]}); and you can add a message with items[i].msg.push("This is a new message");

Related

Angular controller not able to read update from service. Any advice?

I have a situation where within my angular service, I have a number of properties. These properties are linked to the controller.
Service:
angular.module('...')
.factory('PollServ', function PollServ($http, ...) {
var service = {
question: '',
votes: [[]]
}
...
// make http request to API
var request = $http({ ...
// once the value is retrieved, update properties
request.then(function (res) {
service.question = res.data.question;
...
}
Controller:
angular.module('...')
.controller('PollCtrl', function PollCtrl(..., PollServ) {
$scope.question = PollServ.question;
$scope.votes = PollServ.votes;
...
Now, although the votes are being updated properly, the question is not. I am not doing anything different, except the fact that votes is an array and question is just a regular string. I think the array may have something to do with being able to dynamically update, but not the simple string.
How can I get it to work, without unnecessary turning the string into an array as well?
You said it yourself - the question is a string and thus will not be updated in your controller/view.
What you could do is turning the question into an object. For example:
In Factory
var service = {
question: {
name: ''
},
votes: [[]]
}
...
service.question.name = res.data.question;
You then need to change the reference in your view to question.name.

how can I call the (ko.observableArray) from (init) JSON function?

Well the title is confusing so I'll give you my code to understand my problem
in knockout tutorials they use functions instead of JSON I mean like this:
data = [
{
id: 1,
name: 'somehing'
},{
id: 2,
name: 'somehing else'
},
]
here is my problem
var ViewModel = function () {
var self = this;
self.dataList = ko.observableArray(data);
console.log(ViewModel.dataList);
};
while on other websites and most of tutorials and projects in github uses JSON
var ViewModel = {
dataList : ko.observableArray(data),
init: function() {
console.log(ViewModel.dataList);
}
};
this line
dataList : ko.observableArray(data),
when I try to call dataList it return this
function d(){if(0<arguments.length)return d.Wa(c,arguments[0])&&(d.X(),c=arguments[0],d.W()),this;a.k.Ob(d);return c}
and If I try to get its value console will tell me that dataList is not defined
but if I pass data directly to dataList like this (Which is not observableArray anymore) it will give me the full objects values in console
dataList : dataList,
the return value in console
[Object, Object]
how can I call the ko.observableArray from init function?
I want to follow the tutorials on the web like this one but my issue is the same.
http://opensoul.org/2011/06/23/live-search-with-knockoutjs/
Actually it's not only ko.observableArray arrays also I cannot call ko.observable objects
when I try to call dataList it return this
Your code doesn't call ViewModel.dataList, it just accesses it, which gives you the function (remember observables are functions). To call it, add ():
console.log(ViewModel.dataList());
// Note ----------------------^^

Meteor Storing and Retrieving Custom Objects in Session

I have a custom shopping cart object that I created and put it in the lib folder.
ShoppingCart = function ShoppingCart() {
this.Items = new Array();
this.grandTotal = 0.00;
}
ShoppingCart.prototype.addItem = function(Item){
this.Items.push(Item);
this.Items.sort();
this.calculateTotal();
}
I initialized the shopping cart and store it as Session.set('shoppingCart') during the page created phase.
Template.loginStatus.created = function() {
Session.set('loginShown',false);
if(!Session.get('shoppingCart')){ //set default if session shopping cart not exist
var cart = new ShoppingCart();
Session.setDefault('shoppingCart',cart);
}
Then when user click add item to cart, it will trigger this logic:
var cart = Session.get('shoppingCart');
cart.addItem(item);
Session.set('shoppingCart',cart);
Somehow, it does not work. When I take a look ad the chrome console it says undefined is not a function, pointing at cart.addItem(item) line. If I change it to this, it will work , but of course since everytime new shopping cart is created, I cannot accumulate items in the cart.
var cart = new ShoppingCart();
cart.addItem(item);
Session.set('shoppingCart',cart);
How should I store and retrieve the object from session properly? It looks like the returned object from the Session.get() somehow not considered as ShoppingCart. Did I miss any type cast?
As #Peppe L-G mentioned, you can only store EJSONs in Session. To store your custom object, you need to be able to manually transform it to and from EJSONs. Example:
_.extend(ShoppingCart, {
fromJSON: function(json) {
var obj = new ShoppingCart();
obj.grandTotal = json.grandTotal;
obj.Items = json.Items;
return obj;
},
});
_.extend(ShoppingCart.prototype, {
toJSON: function() {
return {
grandTotal: this.grandTotal,
Items: this.Items,
};
},
});
Then you can save it to Session:
Session.set('shoppingCart', cart.toJSON());
and restore:
ShoppingCart.fromJSON(Session.get('shoppingCart'));
I ran into the same problem. Essentially what is happening Meteor Sessions (and Collections) can only store EJSON types, so your ShoppingCart custom type is retrieved from the Session as a normal Object.
While you can manually transform to and from EJSONs, you may end up needing to do this repeatedly in a lot of different places. If your ShoppingCart is a member of another object, you'll have to also manually transform the member. It's better to use EJSON.addType to tell Meteor how to handle it automatically anywhere you store or retrieve an object of that type.
There's a great demo of this here: https://www.eventedmind.com/feed/meteor-create-a-custom-ejson-type. Full docs are also here: http://docs.meteor.com/#/full/ejson. But a short version is this:
Add a method to your custom type called typeName:
ShoppingCart.prototoype.typeName = function(){
return "ShoppingCart";
};
Add another method called toJSONValue:
ShoppingCart.prototype.toJSONValue = function(){
/* return a JSON compatible version of your object */
};
And finally, add the custom type to EJSON with:
EJSON.addType("ShoppingCart", function fromJSONValue(value){
/* return an object of your custom type from the JSON object 'value' */
};
NOTE: the "Type Name" in steps 1 and 3 must match exactly.

AngularJS ng-repeat doesn't update when LocalStorage is changed (using store.js)

In my view I want to display a list of items (subject names) which are saved in a LocalStorage element. My code in the view looks like this:
<div class="list">
<a class="item" href="#" ng-repeat="subject in subjects">
{{subject.name}}
</a>
</div>
my controller looks like this:
.controller('SubjectCtrl', function ( $scope ) {
$scope.subjects = store.get('subjects');
$scope.submit = function() {
if (store.get('subjects') != null) {
var existing = store.get('subjects')
var subject = [ { name: $scope.form.name, weighting: $scope.form.weighting, grades: [] } ]
subject.add(existing)
store.set('subjects', subject)
}
else {
var subject = [ { name: $scope.form.name, weighting: $scope.form.weighting, grades: [] } ]
store.set('subjects', subject)
}
};
})
The $scope.subjects variable gets the items from LocalStorage using Store.js (https://github.com/marcuswestin/store.js, a Plugin that simplifies LocalStorage access) and provides them to the view.
The code underneath is triggered when the user submits a form to add a new subject. The form contains two inputs: name and weighting. When the form is submitted, the code checks if there are already any subjects inside the LocalStorage object 'subjects'. If yes, the new subject is added to the array of subjects and the LocalStorage is updated. If not, a LocalStorage object named 'subjects' is being created and the new subject is added.
The code above works as intended, but my big problem is, that if a new subject is added to the array inside LocalStorage, Angular doesn't update the view and I have to reload the page manually to see the new subject appearing in the list.
After some research, I have learned that the problem may be caused because the LocalStorage object is updated outside of AngularJS' scope. But I'm an Angular beginner and don't know what's the best way to notify Angular as soon as the object changes.
I appreciate any help!
-- UPDATE --
I have switched from store.js (which doesn't work well together with angular) to ngStorage (https://github.com/gsklee/ngStorage). Maybe it's helping for somebody if I post my updated controller code using ngStorage:
.controller('SubjectCtrl', function ( $scope, $localStorage ) {
$scope.$localStorage = $localStorage.$default({
subjects: []
});
$scope.subjects = $localStorage.subjects;
$scope.submit = function() {
$localStorage.subjects.push({ name: $scope.form.name, weighting: $scope.form.weighting, grades: [] });
};
})
There is a couple of things in your code that can be improved -- the most important: you never inform $scope about the change.
Try this one and let me know if it helped:
.controller('SubjectCtrl', function ( $scope ) {
$scope.subjects = store.get('subjects');
if($scope.subjects == null) {
$scope.subjects = [];
}
$scope.submit = function() {
$scope.subjects.push({ name: $scope.form.name, weighting: $scope.form.weighting, grades: [] });
store.set('subjects', $scope.subjects)
};
})
-- UPDATE --
The solution OP found is in the updated-question, above

Why is my controller property 'undefined' when I assign it the result of $resource query()?

I have a JSON data structure:
[
{
"title" :"a1",
"id" :"b1",
"name" :"c1"
},
{
"title" :"a2",
"id" :"b2",
"name" :"c2"
}
]
I am accessing is as an external JSON and parsed through a factory method. I want it to assign it to a Javascript variable in my controller.
function Control($scope,data)
{
var e=data.query(); /* getting the external JSON data */
alert(e[0].title);
}
It says that e[0] is undefined. Is there any other way I can assign it to a Javascript variable and then traverse through it? Please help.
Most likely, #Marty is correct. If you are using the query() method from the $resource service, it is asynchronous. This will likely do what you want:
data.query( function( data ) {
var e = data;
alert(e[0].title);
});
Okay, so $resource can be confusing like this... It immediately gives you a reference to the return object, but doesn't update the object until the asynchronous AJAX call returns... so...
If you put your return value from data.query() in a property on $scope, since it's $watched when you bind it in your view, you'll see it update. HOWEVER, if you're just trying to alert it, it will alert the value before it's been updated.. again because of the async aspect of $resource.
Otherwise, you can get the value the way that #MarkRajcok has shown in his answer.
Here is a psuedo-code illustration of ways you can use $resource query();
app.controller('FooCtrl', function($scope, $resource) {
var Bar = $resource('/Bar/:id', {id: '#id'});
// here, we get the result reference and stick it in the scope,
// relying on a digest to update the value on our screen.
$scope.data = Bar.query();
//OR
//here we already have a reference.
var test = Bar.query(function() {
//in here, test has been populated.
$scope.data2 = test;
});
alert(test); //not populated here, yet.
//OR
Bar.query(function(x) {
$scope.data3 = x;
});
});
This is all done so the object(s) returned can have functions pre-instantiated on them like $save(), etc.

Categories