Data stored on variable only one/first time - javascript

I have a controller where I get some data to be updated on my page, but I'm having some kind of issue with the data that is injected in my controller.
Basically the data that is injected in my controller keeps updated always in real time, but I need the first data loaded in the page be saved in a variable and not be changed, but for some reason it keeps being updated.
My code:
vm.storedData = null;
function activate() {
if(vm.storedData == null){
vm.storedData = data.condition_lines;
}
}
activate();
Am I doing something wrong?

I believe this is caused by the way Javascript stores objects.
data is an object. data doesn't store condition_lines, it stores a reference to condition_lines, so setting vm.storedData = data.condition_lines;, causes assignment by reference. As a result, vm.storedData will ALWAYS equal data.condition_lines.
You could use angular.copy() to effectively assign by value instead (it does more complex stuff under the hood, but the results are the same).
vm.storedData = angular.copy(data.condition_lines);

It looks to me like you have pointed a reference variable (vm.storedData) at another object, so since the value of data.condition_lines is still changing, so is the the storedData.
If you copy the contents of the condition_lines across to the storedData, you should get what you want.
You might want this:
https://docs.angularjs.org/api/ng/function/angular.copy

Related

Angular.copy keeps giving me the same object

I have an AngularJS application that manages badges. In the application is a form to set the badge # and the name of the person it is assigned to, etc. This gets stored in $scope.badge.
When the user submits the form, I want to add the new badge to a list of badges, which is displayed below the form.
Partial code looks like this:
var badge = angular.copy($scope.badge); // make a copy so we don't keep adding the same object
$scope.badgeList.push(badge);
The first time I run this code, it adds the badge as expected.
Any subsequent time I run this code, the next badge REPLACES the previous badge in the badgeList. In other words, if I add 5 badges, the badgeList still only has 1 object in it because it just keeps getting replaced.
I'm thinking that this may be happening because the same object keeps getting added? Maybe I'm wrong? I am using angular.copy to try and avoid that happening, but it doesn't seem to be working.
Any thoughts on this?
$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
no need to use angular.copy since you are ultimately storing all the badges in an array
angular.copy is used when you want to make a clone of object and not update the existing object and the clone's change are not reflected in main object.
If you just want to maintain a list of badges you can execute this block of code
like this
function addBadges(){
$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
}
If you are refreshing the controller then obviously the variable will be reset and for such a case you need to make use of angular services.
Create a service and inside the service you need to define getter and setter method that will help in data persistence
and your bages array if saved in service will persist till the application is in foreground.
You could do something like this.
function addBadges(){
//initialize if undefined or null
if(!$scope.badgeList){
$scope.badgeList = [];
}
//Check if badge does not exists in the list
if ($scope.badgeList.indexOf($scope.badge) === -1) {
//Add to badge list
$scope.badgeList.push($scope.badge);
}
}

How to store and retrieve window state?

I'm trying to write functions for storing and retrieving window state but cannot figure out how to do that. The idea is that user could make at any time a "snapshot" of the screen and with next login to the app he could retrieve it back, also he can store as many snapshots as he want.
For example: on the page I have 4 different closed panels with some kind of filters and 6 different tabs with grid inside (by default the first tab is opened). Now let's say, I have opened 2 of 4 panels, set some filters and worked with 5th tab. I want to be able to store whole window state (For example "My state 1"), and when I logged in at next time, just choose "My state 1" and get back my window state.
I already store and retrieve all grid properties in DB with next functions:
Store:
$scope.state = {}
$scope.saveStateString = function(store) {
$scope.state = JSON.stringify($scope.gridApi.saveState.save(store));
// console.log("function save state string")
};
Retrieve
if(objNode.folderId){
eventService.singleFilter(nodeId)
.then(function (res) {
if (res.body){
$scope.restoreStateString(JSON.parse(res.body));
}
});
}
else if (typeof objNode.folderId === "undefined"){
return false
}
$scope.restoreStateString = function(restore) {
$scope.gridApi.saveState.restore( $scope, restore );
};
For now I'm trying to store window state in localstorage and do next:
var storeValue = null;
var keyName = null;
var _window = {};
$scope.storeWorkspace = function (){
for (prop in window)
_window[prop] = window[prop];
storeValue = JSON.stringify(_window)
keyName = prompt("put your key name");
localStorage.setItem(keyName, storeValue);
};
but I get this error
angular.js:13708 TypeError: Converting circular structure to JSON
at Object.stringify (native)
I clearly understand, why I'm getting this error, it cause JSON doesn't accept circular objects - objects which reference themselves also I see from
console.log(_window) how the "window" has many objects inside, so I decided to ask:
How to store and retrieve window state?
Don't mix application data and resources to store, its huge, hard to reuse and will lead to running into other issues.
Keep it simple!
Construct appState object with what you required to reload the views
var appState ={config:{}, data:{}};
Store it in internalStorage / sessionStorage based on how long you to retain forever vs per session
localStorage.setItem("appState", appState);
On initial app start logic, load data from internalStorage / sessionStorage or server and you may modify existing controller code for binding it to the view.
getApplicationData(){
var appState = localStorage.getItem("appState");//get it from browser storage
if(!appState)
//get it from server
return appState;
}
This is more robust and performant approach.
The vast majority of values stored on the window object are simply not serializable. You will not be able to use JSON for this. You should instead track all changes you make to window and store those in a separate JSON object as POJOs. Alternatively, you can copy the initial window object when your application starts and then only store the differences between the current window object and the original copy.
In any case, this is probably going to be a hunt of trial and error, depending on what libraries you are using, and how they are using global variables. You will probably find you need to manually filter out some stuff when you serialize. Best practices would suggest nothing should write to the window object. If you have things writing to the window object, you're probably dealing with badly behaving code.
Do not try to store the whole window. Store your application's state in a separate object, e.g. state, which you can then attach to the global object if you absolutely have to:
window.state = {}; // your application's state goes here
var serializedState = JSON.stringify(window.state); // To be put into localStorage
Make sure that all the information you need to rebuild your app during the next launch is contained in this object, and nothing more than that. Avoid global state where possible.
Also make sure that your state object only contains serializable data. E.g. functions or symbols cannot be serialzied to a JSON string and will get lost in the process.

Angular Updating scope variables after returning from a promise

I am using the q service in one of my controllers to make sure my requests finish before binding the responses in the then clause. Now here is the tricky part. There is a directive on the page who's template updates a scope variable. This scope variable is used to switch between different parts of the response json, A selector if you will. I need to updated a variable set in the then clause after the page is loaded. It is set by the id added in a directive.
I can't seem to figure out an efficient way to go about updating them.
$scope.selector = {}; //property added from a child scope
$q.all({
//some factory calls and assignment to properties
}).then(function(responses){
//scope variable assignments off of the responses object.
//some assignment that uses the selector. a[selector.id] ex.
}, function(err){
$log.error(err);
}).finally(function(){
//some setting of load params
});
//Then I need to update those variables set in the then based on whether or not the selector id was changed in the directive template.
Any help would be greatly appreciated.
Taking a guess here as the question isn't clear but from the looks of it, you should just save the entire set of responses on the scope and then pull out the data you need. I don't see why you are trying to update the entire response everytime you want to pull one aspect out.
$scope.selector = {}; //property added from a child scope
$scope.responses = {};
$q.all({
//some factory calls and assignment to properties
}).then(function(responses){
//scope variable assignments off of the responses object.
//some assignment that uses the selector. a[selector.id] ex.
$scope.responses = responses;
}, function(err){
$log.error(err);
}).finally(function(){
//some setting of load params
});
// Use something like $watch here and have it call a function
Watchers in AngularJS might be helpful as well.

Storing original values for IIFEs in watch function (reset filters) in Angular

I have a bunch of filters and I seek to write a function so that on ng-click all filters are reset to the original values.
However, all my filter
For example,I have a filter for number of stops which all works fine.
$filter('stopsFilter')($scope.data)(stopsObject);
Data is obtained by http request. I have a $watch function on data to update the view based on data of course. Within the watch function there is stopsObject so that I updates itself as well based on $scope.data. Since stopsObject strictly depends on the structure of $scope.data, there not proper way to define stopsObject beforehand (expect setting it empty).
$scope.stopsObject = { //happens in the watch function of $scope.data
"allCars": $scope.selectall,
"allElements": $scope.checkedStops,
"singleElements": $scope.stopsPerLeg
};
$scope.stopsPerLEg and checkedStops are again IIFEs within the watch function. Whenever I update the allElements, the entire stopsObject updates.
But to write a reset function for all my filters
$scope.allFilters = {
stopsObject:$scope.stopsObject,
};
$scope.resetFilter = function() {
$scope.stopsObject=$scope.allFilters.stopsObject;
};
This does not work as stopsObject is updates constantly. How can I set the original calculated $scope.stopsObject into my allFilters object. I basically need the value that is calculated right after the HTTP request is finished.
Any suggestions?
If you just do a angular.copy such as
$scope.allFilters=angular.copy({...})
it works perfectly.

knockout.js preventing first value change

I've a view with knockout.js which has some textboxes and dropdowns.
known when the user changes a value i save the data with a $post
for this i created some computed propties like
self.subjectChanged ko.computed(function () {
var subject self.subject();
//save...
But this also triggers when the subject was loaded from database and set for first time.
What is the best practice for this ?
A similar problem is that i have a function getdata() which depends on two properties.
Now on load this method is raised twice (for each property)
What are best practices to handle this szenarios ?
One way of doing it is to load the page and bind the data as normal, and then use subscriptions to monitor changes to the observable you are interested in.
http://knockoutjs.com/documentation/observables.html#explicitly-subscribing-to-observables
viewModel.subject.subscribe(function(newValue) {
// code you want to run when the value changes...
});
for example http://jsfiddle.net/m8mb5/
This may not be best practice, but in the past I tied a loaded variable to the vm and when the data finished loading from the server I set it to true;
In my computeds I would surround the code that actually did the work in an if that checked the loaded. Computeds can be a little tricky though, you may need to reference the observables outside of the if to ensure they fire correctly.
com = ko.computed(function(){
if(loaded){
var subject = self.subject();
}
// reference observable outside of if to ensure the computed fires when the observable changes
self.subject();
});

Categories

Resources