AngularJS controller's variable is not updated - javascript

I have problem with below code. I have prices factory which returns object containing prices received from server by websocket. Prices are sent after button Create is clicked. Problem is that main.prices variable is not updated at all. I can check everything by Check button, which confirms this. Prices.data is updated, but this.prices is not, but it refers the same object, so I thought it should be updated as well. Do you have any ideas why below does not work as expected?
angular.module('myApp', ['ngWebSocket'])
.factory('ws', ['$websocket', function($websocket){
var url = 'ws://localhost/websocket';
var ws = $websocket(url);
return ws;
}])
.factory('prices', ['ws', function(ws){
var prices = {
data: [],
clear: function(){
this.data = [];
},
create: function(){
ws.send('send')
}
}
ws.onMessage(function(message){
message = JSON.parse(message.data);
var type = message.type;
if (type == 'new prices'){
prices.data = message.data;
}
});
return prices;
}])
.controller('main', ['prices', function(prices){
this.prices = prices.data;
this.check = function(){
console.log('works ', prices.data);
console.log('not works ', this.prices);
};
this.create = function(){
prices.create();
};
this.stop = function(){
prices.clear();
};
}]);
<div ng-controller="main as main">
{{ main.prices }}
<button ng-click="main.create()">Create</button>
<button ng-click="main.stop()">Stop</button>
<button ng-click="main.check()">Check</button>
</div>

There are a lot of issues with the code you posted (working on a fiddle so i can help rework it) ...
First change :
if (type == 'new prices'){
prices.data = message.data;
}
To:
if (type == 'new prices'){
prices.data.length = 0;
prices.data.push.apply(prices.data,message.data) ;//copy all items to the array.
}
From a readability / maintainability point of view you should just use this.prices vs this.prices.data. It's confusing to map them to other variables, when you can just use prices. Also note that I updated it to use "that" constantly to avoid any type of context this issues.
.controller('main', ['prices', function(prices){
var that = this;
that.prices = prices;
that.check = check;
that.create = create;
that.stop = stop;
function check(){
console.log('works ', that.prices.data);
console.log('not works ', that.prices);
}
function create(){
that.prices.create();
}
function stop(){
that.prices.clear();
}
}]);

To add to the previous response, you also have an issue on the clear():
var prices = {
...
clear: function(){
this.data = [];
},
...
}
when you do the clear with this.data = [] you are actually creating a new empty array an storing that in the this.data prop, and since this is a NEW array, the reference on main controller -> this.prices = prices.data; is still pointing to the old one. If you need to delete elements on the array just use this.data.length = 0 as Nix pointed out for the other method. that will keep all references in sync since you are re using the original array

Related

Javascript & knockoutjs: how to refactor the following code to be able to access the properties outside the function

Im struggling to find a way to get the properties Override & Justification available outside of the function. The code is:
self.CasOverridesViewModel = ko.observable(self.CasOverridesViewModel);
var hasOverrides = typeof self.CasOverridesViewModel === typeof(Function);
if (hasOverrides) {
self.setupOverrides = function() {
var extendViewModel = function(obj, extend) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
extend(obj[property]);
}
}
};
extendViewModel(self.CasOverridesViewModel(), function(item) {
item.isOverrideFilledIn = ko.computed( function() {
var result = false;
if (!!item.Override()) {
result = true;
}
return result;
});
if (item) {
item.isJustificationMissing = ko.computed(function() {
var override = item.Override();
var result = false;
if (!!override) {
result = !item.hasAtleastNineWords();
}
return result;
});
item.hasAtleastNineWords = ko.computed(function() {
var justification = item.Justification(),
moreThanNineWords = false;
if (justification != null) {
moreThanNineWords = justification.trim().split(/\s+/).length > 9;
}
return moreThanNineWords;
});
item.isValid = ko.computed(function() {
return (!item.isJustificationMissing());
});
}
});
}();
}
I've tried it by setting up a global variable like:
var item;
or
var obj;
if(hasOverrides) {...
So the thing that gets me the most that im not able to grasp how the connection is made
between the underlying model CasOverridesviewModel. As i assumed that self.CasOverridesViewModel.Override() would be able to fetch the data that is written on the screen.
Another try i did was var override = ko.observable(self.CasOverridesViewModel.Override()), which led to js typeError as you cannot read from an undefined object.
So if anyone is able to give me some guidance on how to get the fields from an input field available outside of this function. It would be deeply appreciated.
If I need to clarify some aspects do not hesitate to ask.
The upmost gratitude!
not sure how far outside you wanted to go with your variable but if you just define your global var at root level but only add to it at the moment your inner variable gets a value, you won't get the error of setting undefined.
var root = {
override: ko.observable()
};
root.override.subscribe((val) => console.log(val));
var ViewModel = function () {
var self = this;
self.override = ko.observable();
self.override.subscribe((val) => root.override(val));
self.load = function () {
self.override(true);
};
self.load();
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Save key=>value style with ngStorage/localstorage

In my Ionic app I've added the plugin 'ngStorage' and it comes with a little demo code:
var add = function (thing) {
$localStorage.things.push(thing);
}
This works exactly as told. I add("foo") it, and do getAll() and the value is there. I remove the add(), but keep the getAll(), I still have the value "foo" (as expected).
This isn't very usefull for me, I want to access it with keys, so I've made the following:
var addByKey = function (key, value) {
$localStorage.things[key] = value;
// Or, I've also tried:
$localStorage.things.key = value;
}
When I do the addByKey("foo","bar") and then the getAll() I get the values exactly as I want. When I remove the addByKey() and reload, I expect it to still remember the set information, but it doesn't exist. However, the first attempt via the add() function still exists, "foo" is still there (meaning the array doesnt reset).
How do I make a key->value type of structure?
In case it's usefull:
.factory ('StorageService', function ($localStorage) {
$localStorage = $localStorage.$default({
things: []
});
var _getAll = function () {
return $localStorage.things;
};
var _add = function (thing) {
$localStorage.things.push(thing);
}
var _addByKey = function (thing, value) {
$localStorage.things[key] = value;
// Or, I've also tried:
$localStorage.things.key = value;
}
return {
getAll: _getAll,
add: _add,
addByKey: _addByKey
};
})
Assuming that you want a key value storage system you can simply use an object instead of an array so that every key can be set as a property of this object.
.factory('StorageService', function($localStorage) {
$localStorage = $localStorage.$default({
things: {}
});
var _getAll = function() {
return $localStorage.things;
};
var _addByKey = function(thing, value) {
$localStorage.things[thing] = value;
}
return {
getAll: _getAll,
addByKey: _addByKey
};
})
However, assuming that you want to keep a reference of all values on the main collection and access them through keys, you can consider using an object to store the things intead of an array. So that you can use a property to store all items (you can store in a different place as well) and use this object to store your keys by referencing the to a desired value on your collection.
You may need to implement the deletion logic to maintain the consistence between the collection and the dictionary.
Your factory would look like this:
.factory('StorageService', function($localStorage) {
$localStorage = $localStorage.$default({
things: {
items: []
}
});
var _getAll = function() {
return $localStorage.things.items;
};
var _add = function(thing) {
$localStorage.things.items.push(thing);
}
var _addByKey = function(thing, value) {
var i = $localStorage.things.items.push(value) - 1;
$localStorage.things[thing] = $localStorage.things.items[i];
}
return {
getAll: _getAll,
add: _add,
addByKey: _addByKey
};
})

Adding properties to an object using function and bracket notation

I have an assignment on a basic javascript class that I'm taking and I can't seem to get this to work. I have this unit test that was given to me:
describe('AddSixthProperty', function() {
it('should add a food property with the value of bbq using bracket notation', function() {
expect(objects.addSixthProperty()['food']).to.equal('BBQ');
});
});
I was given an empty function:
// don't touch this line
var mysticalAnimal = objects.mysticalAnimal();
function addSixthElement(){
return
}
So I tried this:
var mysticalAnimal = objects.mysticalAnimal();
objects.addSixthProperty = function(){
mysticalAnimal['food'] = "bbq";
return mysticalAnimal["food"];
};
It doesn't work. Our test page doesn't pass that. Any help is greatly appreciated!
Thanks in advance!
You're returning mysticalAnimal['food'], and then the test tries to access ['food'] again, so it ends up accessing 'bbq'['food'], which is undefined. You need to just return mysticalAnimal, as well as get all your letter cases right. Here's a little proof of concept:
var objects = (function() {
var animal = { mystical: true };
return {
mysticalAnimal: function() { return animal; }
};
})();
var mysticalAnimal = objects.mysticalAnimal();
objects.addSixthProperty = function(){
mysticalAnimal['food'] = "bbq";
return mysticalAnimal;
};
var capturedAnimal = objects.addSixthProperty();
document.getElementById('result').innerText = capturedAnimal['food'];
<p id="result" />
Here is the function:
var mysticalAnimal = objects.mysticalAnimal();
objects.addSixthProperty = function(){
mysticalAnimal['food'] = "BBQ";
return mysticalAnimal;
};
// Test the function
console.log(objects.addSixthProperty()['food'])

Can't get array.splice to work in Angular

I have the object words and a checkbox which should hide a specific element from this object, but I cannot get it work.
<body ng-controller="ArrController">
<input type="checkbox" ng-model="hide"> {{kc}}
{{words}}
</body>
The ArrController:
app.controller('ArrController', function ($scope, $http) {
$scope.hide = false;
$http.get('array.json').success(function(data) {
var keyword = 'lol';
$scope.words = data.unsorted_arr;
$scope.$watch('hide', function () {
if ($scope.hide == true) {
var remove = function() {
$scope.words.splice(keyword, 1);
}
$scope.kc = 'hidden';
} else {
$scope.kc = 'not hidden';
$scope.words = data.unsorted_arr;
}
});
});
});
The file array.json contains data for words:
{"unsorted_arr":{"gonna":3,"lol":114,"wouldn":2,"know":6,"lowkey":2,"man":5}}
The kc modifies according to the checkbox status, but the words stays the same.
Where am I wrong?
Splice is for removing something in an array, and it takes in two integers as parameters.
Since you have an object, just use delete:
delete $scope.words[keyword];
By doing, $scope.words = data.unsorted_arr, the two variables refer to the same object so deleting something from $scope.words will delete it from data.unsorted_arr.
Keep a reference to it so you can repopulate it later:
var word = $scope.words[keyword];
...
delete $scope.words[keyword];
...
$scope.words[keyword] = word;
You are confusing Indexed arrays with associative arrays,
Array.splice is a method of Indexed Arrays,
you have a simple Javascript Object (associative array)...
on POJO you can use the delete operator or a simply reassignment to undefined:
var a = { foo: 'baz' };
delete a['foo'];
var b = ['foo', 'baz'];
b.splice(0, 1)
In your example you are defining a function for removing the element, but the function is never being called.
var remove = function() {
$scope.words.splice(keyword, 1);
}
you may need to your logic to remove the function (as it doesn't seem to be needed) and replace the of use splice with the delete statement:
$http.get('array.json').success(function(data) {
var keyword = 'lol';
$scope.words = data.unsorted_arr;
$scope.$watch('hide', function () {
if ($scope.hide == true) {
delete $scope.words[keyword];
$scope.kc = 'hidden';
} else {
$scope.kc = 'not hidden';
$scope.words = data.unsorted_arr;
}
});
});

Passing variables between functions in Javascript

I have this code:
collection = (function() {
function collection(removeLinkTitle){
this.removeLinkTitle = removeLinkTitle || 'delete';
}
collection.prototype = {
removeLinkTitle: this.removeLinkTitle,
init:function(){
...some code...
this.deleteCollectionForm();
},
deleteCollectionForm:function(){
var removeFormA = $(''+this.removeLinkTitle+'');
linkLi.append( removeFormA );
removeFormA.on('click', function(e) {
e.preventDefault();
linkLi.remove();
var index = collectionHolder.data( 'index' );
collectionHolder.data( 'index', index - 1 );
});
}
};
return collection;
})();
The thing is that the var removeForm returns its value only the frst time it loads, the following times it returns undefined.
I don't want to pass the variable as an argument so, is it there any other way to do this?
Thanks !!
i think this is not really the problem, the one thing that is undefined is the, removeLinkTitle, try this:
return new collection;
so you hit your constructor, or set some other value with:
return new collection("delete item");

Categories