Need debugging/outside advice. Angular not working - javascript

So, my instructor thought it would be a great idea to throw in local storage and all that great nonsense the second week into learning Angular JS and basically told us to copy her code but change the names (as if that's learning). But anyways I have no idea how to work with angular js except for a few concepts and I need help finding the issues in my code. Any input would help. So far it looks as if the information from the form isn't being inputed to the html where it will be displayed. Here is my js fiddle. Any input is greatly appreciated
http://jsfiddle.net/g3tg5L15/1/
var app = angular.module('myApp', []);
app.controller("EmployeeController", function($scope, DataService){
$scope.empInfo = DataService.getEmpInfo();
$scope.newempInfo = {};
$scope.addNewEmpInfo = function(){
DataService.saveEmpInfo($scope.newempInfo.employee, $scope.newempInfo.street,
$scope.newempInfo.city,$scope.newempInfo.state, $scope.newempInfo.zip);
$scope.newempInfo = {};
};
$scope.removeEmpInformation = function(index){
DataService.removeEmpInfo(index);
};
$scope.clearInfo = function(){
DataService.destroyLocalStorage();
};
});
angular.module('myApp').service("DataService", function(){
var empInfoArray = [];
this.getEmpInfo = function(){
var employeeInfoArray = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
var empInfoArray = employeeInfoArray;
return empInfoArray;
};
this.saveEmpInfo = function(aName, aStreet, aState, aCity, aZip){
var savedEmpInfo = {employee : aName, street : aStreet, state : aState, city : aCity, zip : aZip};
empInfoArray.push(savedEmpInfo);
localStorage.setItem("employeeInformationLS", JSON.stringify(empInfoArray));
};
this.removeEmpInfo = function(aIndex){
empInfoArray.splice(aIndex, 1);
localStorage.setItem("employeeInformationLS", JSON.stringify(empInfoArray));
};
this.destroyLocalStorage = function(){
empInfoArray.splice(0);
localStorage.clear();
};
});

The main reason for the lack of response and debugging ability is due to AngularJS not loading correctly. For it to load you must change the dropdown in the left menu bar of jsFiddle from onLoad to No wrap - in <body> to load Angular correctly (as shown in the following screenshot).
The following surmises the issues I observed when debugging the code.
The getEmpInfo function within the DataService returns a new array each time it is called which prevents Angular from effectively monitoring it for changes. Instead of this function checking localStorage each time it is called it should just return the local array. The array can be simply loaded from localStorage when the service is first initialized.
The following update to the fiddle demonstrates this http://jsfiddle.net/g3tg5L15/6/. The changes implemented are as follows:
Change the dropdown in the menu bar of jsFiddle from onLoad to No
wrap - in <body> to load Angular correctly.
Added ng-click to 'Add Entry' button in HTML
<!-- Added ng-click to call addNewEmpInfo function on scope -->
<button ng-click='addNewEmpInfo()'>Add Entry</button>
Amended text on employeeInfo header to employee name rather than being hard coded value and added ng-click to remove in HTML.
<!-- Amended to add.employee rather than hardcoded value -->
<h3>{{add.employee}}</h3>
<!-- Added ng-click to call removeEmpInformation function on scope -->
<button ng-click='removeEmpInformation($index)'>X</button>
Amended the DataService to load from localStorage when it is initialized rather than initializing as an empty array.
var empInfoArray = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
Amend the getEmpInfo object to just return the local Array
this.getEmpInfo = function(){
return empInfoArray;
};
If necessary you can also watch for events triggered when localStorage changes, as included in the above fiddle. This will pick up changes from different tabs / windows if multiple are open. To monitor for these you must:
Include the services $window and $timeout in the DataService
angular.module('myApp').service("DataService", function($window, $timeout){
Add a trigger when a storage change occurs.
//Watch for storage events indicating changes to storage
angular.element($window).on('storage', function(event) {
//Check if the storage change was for our key
if (event.key === 'employeeInformationLS') {
//In a timeout (i.e. on next digest) update the array
//This could be done in a smarter way rather than clearing
//and rebuilding the entire array
$timeout(function(){
empInfoArray.splice(0);
var newArr = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
for (var i=0; i<newArr.length; i++){
empInfoArray.push(newArr[i]);
}
});
}
});

Related

How to access an HTML element that is dynamically filled with values?

I've read lots of stackoverflow questions with no luck. My problem is, I have an HTML page in which I have
<select id="myid"></select>
and there, there's a Firebase command that retrieves names of values i need, and put it inside the <option> like HERE:
reference.on("child_added", function (childSnapshot){
var key = childSnapshot.key;
var opt = document.createElement('option');
opt.textContent = key;
document.getElementById('myid').appendChild(opt);
});
Now, i need to somehow access these values, that by the way are correctly appearing in both HTML and my site, however:
var val = document.getElementById('myid').value;
console.log(val);
It always returns blank in console. I don't know how else can I access it. Whenever I type those values in <option> by myself in HTML, everything works as it should and console returns the names that are in database.
#edit: as far as i tried to crack it, it seems to do with the fact that javascript cannot access elements, that for javascript itself aren't yet loaded, but i tried doing window.onload and other similar ones and they don't help.
You can use AngularJS, with directive $scope.watch, i will write a simple example of how to use and the link of documentation, if you have any question talk back to me!
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.buttonClicked = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
}
AngularJS Documentation
I hope help with this :)

AngularJS UI router's "resolve" fires twice

I am using angular ui router to handle some routing on my frontend. This is what my routing code looks like.
// angular config
$stateProvider.state('app', {
templateUrl: '/static/partials/home.html',
controller: 'NavCtrl'
});
$stateProvider.state('app.reader', {
url : '/reader/*path?start&end&column&page',
templateUrl: '/static/partials/reader.html',
resolve : {
panelContent : [
'$state', '$stateParams', '$http',
function ($state, $stateParams, $http) {
alert('resolving panel Content');
return []; // simplest thing possible to illustrate my point
}
]
},
controller: 'ReaderCtrl'
});
/// etc etc
$urlRouterProvider.otherwise('/reader/');
My html makes use of multiple nested views, I'll try and illustrate as best I can
index.html
<html>
<div ui-view></div> <!-- /static/partials/home.html gets injected here -->
</html>
/static/home.html
<html>
<!-- some side bar stuff -->
<!-- reader -->
<div ui-view></div> <!-- /static/partials/reader.html gets injected here -->
</html>
So I've got multiple levels of nesting going on
-- index.html
-- home.html
-- reader.html
Now, when I load the page for the first time, my alert message
alert('resolving panel Content');
fires just once.. that makes sense. However, let's say I click "next page" inside my pagination..
<!-- inside /static/partials/reader.html -->
<uib
pagination total-items= "totalItems"
ng-model= "pageNumber"
ng-change= "pageUpdate"
max-size= "maxPageNumbersDisplayed"
></uib>
this eventually fires a function inside my "ReaderCtrl"
$scope.pageUpdate(page) {
$state.go( '.', {page: page});
}
This updates the url, from going to something like this
/#/reader/<my path>
to something like this
/#/reader/<my_path>?page=2
Now for the part that has me tearing my hair out.
I get back to the "resolve" code block in the reader section of my routing.
The alert message happens twice.
By doing a bit of debugging in the web console, I discovered that the order goes
1) alert message in resolve
2) travel through the entirety of ReaderCtrl
3) lots and lots of angular calls
4) alert message (2nd time)
5) travel through entirety of ReaderCtrl a second time.
You might be inclined to know what is going on in NavCtrl, but I am not making any calls there. All that is in NavCtrl are functions that ReaderCtrl can inherit, in order to update the scope for /static/partials/home.html
So really, it appears as though I am stuck on step 3 here.
Does anyone have any ideas as to why my resolve block appears to be firing twice?
edit:
after a bit more debugging, I have seemed to figure out that the order goes something like this, starting right after the "updatePage" function executes.
1) first "resolving message"
-- the url has not yet changed
2) second "resolving message"
-- the url appears to have changed very shortly before this message
So, I guess my question now is...
why does
$state.go('.', args);
NOT change the url before the first alert fires, but DOES change the url at/near the second alert?
edit 2: could not end up fixing my issue, so I sort of hacked around it for the time being... I essentially made a function that did what I assume $state.go() was doing behind the scenes, and constructed the url.
function _mk_url(args) {
var url = "/reader";
var pageNumber = args.pageNumber || 1;
url += "?page=" + pageNumber;
var columns = args.columns || [];
columns.forEach(function(d) {
url += "&column=" + d;
});
//etc..
return url;
}
var args = {"columns" : ["a", "b", "c"], "pageNumber" : 2};
var url = _mk_url(args);
$location.url(url);
I was having this problem and found out it was because I was calling my resolve function manually somewhere else in the code. Search your code for panelContent() and you may find where it's getting triggered again.
I'd got this problem. The reason was in my html template. I used ui-sref directive in both child and parent elements
<li ui-sref="{{r.name}}" ng-class="vm.isCurrent(r)" ng-repeat="r in vm.settingsRoutes">
<span ui-sref="{{r.name}}" ng-bind-html="r.title"></span>
</li>
so when I clicked on span, I fired stateChange twice.
I've had the same bug.
And I found that I was changed $stateParams in one of the resolve functions.
The solution is make a copy from this object and then do what you want with a copy.
resolve: {
/** #ngInject */
searchParams: function ($stateParams) {
let params = angular.copy($stateParams); // good
// good:
if (params.pending === undefined) {
params.pending = true;
}
// bad:
if ($stateParams.redirect === 'true') {
$stateParams.pending = false; // this line changing the URL
}
return params;
},
}

Angular.js (possible bug) - objects attribute return undefined on a single item view

i have a poll application with 2 views, list view and single item view. these are the init functions in the angular client controller
// Poll List
$scope.pollList = function(){
$scope.votes = Votes.query({
userId:Authentication.user._id
});
$scope.polls = Polls.query();
};
// View Poll
$scope.findOne = function(){
$scope.votes = Votes.query({
userId:Authentication.user._id
});
$scope.poll = Polls.get({
pollId: $stateParams.pollId
});
$scope.categories = Categories.query();
$scope.languages = Languages.query();
$scope.pollItem($scope.poll);
};
// Poll Item
$scope.pollItem = function(poll){
$scope.pollVoted(poll);
$scope.countPollVotes(poll);
for (var i = 0; i < poll.poll_options.length; i++){
$scope.optionItem(poll,poll.poll_options[i]);
};
};
both the list view and the single view initiate a pollItem function in each poll object. in the list view everything works good - each poll object passes the pollItem function without an error. but in the single poll view im recieveing errors, namely, that the diffrent attributes of the poll (i.e user, comments, poll_options) are returning undefined! allthough when i console.log the poll object i can see them all there. please help! im aware to the fact that it sounds a bit vague but im not sure which other details to provide.. im stuck a couple of days on this problem and no answer to be found.

One time bind two elements to be visible on page load

I'm using an external filtering service and ngGrid to display my data. There are some controls (text box, search buttons) that I want be hidden until the promise to fill my table data is resolved, but only on the page load. After the page is loaded, I want the static HTML external to ngGrid to remain visible. I currently have a flag like
//first line in controller
$scope.isUpdating = true;
var getData = function(){
$scope.isUpdating =true;
ngGridDataPromise.then(function(){//operations..})
.finally(function(){ $scope.isUpdating = false });
}
So I have a function that, as its first action is sets isUpdating = true, and a promise inside of that function whose finally will set isUpdating = false. The problem I have is that as that request is happening, there's a small time window where the promise isn't resolved and the ng-show I have on my template doesn't show the controls I need.
HTML
<div ng-show="!isUpdating"><buttons></buttons></div>
How can I make the isUpdating binding only care about the initial page load, or are there some more framework type facilities that I can take advantage of for this? Angular version - 1.2.26
EDIT: using another external dependency is not an option, I'm looking for an angular way, or a clever JS solution.
I'm not 100% sure I follow what you're asking but I believe you want the controls hidden the first time getData is called until it is fully resolved but not for subsequent requests on that same session? If so, I believe this would work:
//first line in controller
$scope.isUpdating = true;
var firstLoad = true;
var getData = function(){
if(firstLoad) {
$scope.isUpdating = true;
firstLoad = false;
}
ngGridDataPromise.then(function(){//operations..})
.finally(function(){ $scope.isUpdating = false });
}

Knockout observable/viewmodel that can be accessed by other viewmodels?

I am building an SPA and everything is going well. It has multiple Viewmodels which are built dynamically and there can be multiple of the same kind, i.e you can open two calculators each having its own model which is bound to a specific div on the page.
Recently I realized that several of the viewmodels were requesting the same data from a web service and on a constant loop every 30 secs - 1 minute. So the same service call was being made multiple times every 30 seconds yet returning the same information.
So what I am trying to figure out is how I can create a "global" observableArray which multiple viewModels can be notified of a change and update rather than doing it themselves, this also helps to make sure the data on the page is consistent.
I was hoping I could do something like:
var GlobalData = (function() {
var commonData = ko.observableArray();
setInterval(function() {...go get data...commonData(data);}, 30000);
return {CommonData:commonData}
})();
ko.applyBindings(GlobalData, $('#RandomLonelyDiv')[0]);
Then later
function Calculator(element){
function init() { ko.applyBindings(calculator, $(element)[0]); }
var calculator = {
CommonData = GlobalData.CommonData
}
return calculator;
}
If it helps the only reason why I dont have a MainViewModel which contains all my other viewmodels is because i frankly dont know how to set that up for my environment.
I have a AppViewModel which contains a ko.observableArray called Windows, which is contains objects which define the options/information to build certain window types.
<!-- ko template:{name:'WindowTemplate', foreach:SelectedTab().Windows} --><!-- /ko -->
and then I have a custom Window binding that creates a modified kendoWindow, which creates a new viewmodel of a specific type such as Calculator, and like I said you could have multiple calculators at one time. But when I started this I wasnt really sure how to put that viewmodel into my AppViewModel. Perhaps its just another array?
It sounds like what you really need is a "Pub/Sub" model. That would allow you to publish and subscribe to messages that are ignorant of their generation or destination. Check out https://github.com/postaljs/postal.js/wiki.
I believe this may be what you are looking for: http://jsfiddle.net/xSKyR/474/
You can subscribe to another viewmodel's observable like so..
var ViewModel1 = function () {
var self = this;
self.something1 = ko.observable("1");
self.clickMe = function (data, event) {
self.something1("2");
};
};
var ViewModel2 = function () {
var self = this;
self.something2 = ko.observable();
vm1.something1.subscribe(function (newValue) {
self.something2(newValue);
});
};
var vm1 = new ViewModel1();
var vm2 = new ViewModel2();
ko.applyBindings(vm1, document.getElementById("vm1"));
ko.applyBindings(vm2, document.getElementById("vm2"));

Categories