I am trying to pass a variable into a service that can be access from multiple states within my application. I have implemented a service and tried to pass a variable from a controller into the service so I can access it from another state as well as use it in other functions as well.
Here's my code:
Controller that get's input from html form and uses it in a function, I want to also store the value received from the input inside a service to used in other functions as well.
$scope.submit = function()
{
var input = document.getElementById('userCode').value;
var current;
for(var i = 0; i < json.details.length; i++){
current = json.details[i];
if(current.pin == input){
$state.go('LogInHome');
$scope.Current = currentUser;
$scope.Current.currentUser = input;
}
}
console.log(current.currentUser);
}
Here is the service:
App.service('currentUser', function ()
{
return{};
});
When I look at the console, i get "Undefined" where am I going wrong?
$scope.submit = function()
{
var input = document.getElementById('userCode').value;
var current;
for(var i = 0; i < json.details.length; i++){
current = json.details[i];
if(current.pin == input){
$state.go('LogInHome');
$scope.Current = currentUser;
$scope.Current.currentUser = input;
current = $scope.Current;
}
}
console.log(current.currentUser);
}
This should give you the answer. Does it help?
Never Mind, I am an idiot. Rather than take this down and hide myself from the judging eyes of you all. I have decided to tell you all about my laughable mistake so people will have something to check. I forgot to inject the service. Many apologise for wasting people's time.
Related
newbie Javscripter here. I'm trying to get some practice with modules and I'm hitting a wall. I'm trying to get the controller module to access the wagerAmount(input by user) from the UIControl module with minimal success. My relevant code is as follows:
var UIControl = (function() {
return {
getWager: function() {
return {
wagerAmount: document.getElementById('wager').value
};
}
};
})();
var controller = (function(UICont) {
var topics = ['sports', 'history', 'technology', 'music', 'movies'];
var wager = UICont.getWager();
var changeClass = function() {
document.getElementById('bet').addEventListener('click', function() {
console.log(wager);
for (var i = 0; i < topics.length; i++) {
if (document.getElementById(topics[i]).classList[1] == 'select') {
activateAnswers();
}
}
});
}
})(UIControl);
When I run this, console.log(wager) prints a empty string, {wagerAmount: ""}. If I declare wager inside the changeClass function, the same thing happens. However, if I declare wager inside the click event, it prints the number input by the user. BUT, the topics variable, declared in the controller is being accessed just fine by the click event. The main problem here is when I need to add the functionality to multiple click events, I would have to declare a new variable in each click event to access the same function, which sounds like bad practice. So what's going on here?
Ok, so after spending a couple hours last night trying to figure this out, I quickly figured out the solution this morning after some coffee. In case anyone else has this problem I'll post my solution, and if someone else comes up with something else, they can post as well.
Instead of declaring wager in the controller anywhere, I created a new function inside controller:
var retrieveWager = function() {
return {
wager: UICont.getWager().wagerAmount
};
}
and then could access it in any of the click events like this:
retrieveWager().wager;
I want to know the correct way to do the following, I have a service, a factory, and a controller.
The service has a selectedTable property that starts as null by default and it's used to store the selected table in order to be used in both the factory and the controller; the service also has a touches property that it's updated regularly in the factory and the controller, that service looks like this:
Module.service('tablesService', function(){
this.data = {
selectedTable: null,
touches: 0
}
});
The factory uses the service by setting up a variable var data = tablesService.data and has a method select that modifies the value of data.selectedTable and data.touches:
if (data.selectedTable === this){
data.touches++;
if (data.touches === 2) {
data.selectedTable = null;
}
} else {
if (data.selectedTable && data.selectedTable != this) {
data.touches++;
data.selectedTable.select();
}
data.selectedTable = this;
}
The controller looks in a list of tables on every onClick event and when it founds the clicked table calls it's select() method, the one in the factory, if the table clicked is the selectedTable, it changes the touches variable so when it's select() method it's called, the selectedTable get's null as the new value.
$scope.tablesData = tablesService.data;
$scope.selectedTable = $scope.tablesData.selectedTable;
$scope.touches = $scope.tablesData.touches;
$scope.findTable = function(event){
$scope.touches = 0;
for(t in $scope.tables) {
var table = $scope.tables[t];
var result = table.findMe(event.offsetX,event.offsetY);
if(result.type === 'table') {
if ($scope.selectedTable === table){
$scope.touches++;
}
table.select();
break;
}
}
The problem is that changing $scope.touches won't update the variable in the service and vice-versa, this also happens with the selectedTable, I tried using $watch on both $scope.touches and $scope.tablesData.touches but the $digest() method doesn't fire up every time I change $scope.touches so I have to call $apply() which looks awful and doesn't solve the problem all the time.
My watchers look like this:
$scope.$watch('touches', function(){
$scope.tablesData.touches = $scope.touches;
});
$scope.$watch('tablesData.touches', function(){
$scope.touches = $scope.tablesData.touches;
});
Reading this post http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/ I found out that I can broadcast an event to the application via $rootScope.$broadcast(), but I'm not really sure how to implement that and perhaps that's not the best way to solve the problem.
I'm trying to build a task processing type of SPA. The idea is There are 10 steps, The user clicks submit, then a service will be called to begin a process, and the UI will wait. Once it gets a response a style will be applied for success for failure. Anyhow, right now I'm trying to mock this behavior up and I can't seem to get my Angular correct, specifically with $timeout. Below is my code, and please excuse the simplified example I'm trying to understand Angular by working through it.
;function(angular) {
'use strict'
angular.module("workApp", [] )
.controller ("TaskController", function ($routeParams, workService, $timeout, $scope) {
var tc = this;
// These will be used in the view to flip the CSS to the appropriate color for success or failure
tc.step_0_status = "ready";
tc.step_1_status = "ready";
tc.step_2_status = "ready";
// trying this out, by storing my functions in a array, b/c I will have other uses in the end for this array.
var func_array = [
function(){step_0},
function(){step_1},
function(){step_2}
]
// This is where I am misunderstanding $timeout I guess. I simply want the loop to sleep for 3 seconds, before the next function is called.
$scope.startTasks = function results() {
for(var x = 0; x < func_array.length; x++) {
**$timeout(func_array[x](),3000);**
}
}
var step_0 = function() {
tc.step_0_status = "running"
}
var step_1 = function() {
tc.step_0_status = "completed";
tc.step_1_status = "running";
}
var step_2 = function() {
tc.step_1_status = "completed";
tc.step_2_status = "failed";
}
}
})(window.angular);
You can use a $interval to create the sleep method you want:
$interval(function(){
// do wahetever you need here
console.log('running');
},3000, func_array.length);
just make sure to destroy it properly after you have used it
I have a firebaseObject (MyFirebaseService.getCurrentUser()) bind to $scope.user.
After binding successful, I loop tho the object to see if the object contain "associatedCourseId" equal to some value ($stateParams.id). If does, the $scope.finishLessonCount count up. The problem is, when I add new Object inside the firebaseObject (that bindto user) via other page OR inside firebase, the finishLessonCount value won't change as what I expect for 3 way binding. I need to refresh the page to see the finishLessonCount reflect the true value. What is wrong? I want the finishLessonCount change using the compare function as I add more finishedLessons into the firebaseObject. Please see code below:
MyFirebaseService.getCurrentUser().$bindTo($scope, "user").then(function(){
for (var key in $scope.user.finishedLessons) {
if ($scope.user.finishedLessons.hasOwnProperty(key)) {
if ($scope.user.finishedLessons[key].associatedCourseId == $stateParams.id) {
$scope.finishLessonCount++;
}
}
};
console.log ($scope.finishLessonCount);
});
UPDATE 1 according to #Kato solution:
I decide to use Extending firebaseOject way to solute this problem. But still, it does not. I did not use factory here to simplify thing since I need to pass in courseId to do the operation. Here is my code:
function countLessons(lessons, courseId) {
var count = 0;
for(var key in lessons) {
if( lessons[key].associatedCourseId == courseId) {
count++;
}
}
return count;
}
var UserWithLessonsCounter = $firebaseObject.$extend({
$$updated: function(snap) {
var changed = $firebaseObject.prototype.$$updated.call(this, snap);
this.lessonCount = countLessons(this.finishedLessons, $stateParams.id);
}
});
var refTemp = new Firebase($rootScope.baseUrl + "users/" + $rootScope.userId);
var userTemp = new UserWithLessonsCounter(refTemp);
userTemp.$bindTo($scope, "userTemp").then(function(){
console.log($scope.userTemp);
});
userTemp.$watch(function() {
console.log("Does this run at all? " + $scope.userTemp.lessonCount);
});
I update the user object, the lessonCount value did not change unless I refresh the page. And the console.log inside $watch did not run at all. What is wrong?
The promise returned by $bindTo is called exactly once. It's not an event listener. You can't listen to this to get updated each time there is a change.
Please read the guide, start to finish, and read about Angular's $watch method before continuing down this route, as with some fundamental knowledge, this should not have been your first instinct.
A beginner approach would be to utilize $watch:
MyFirebaseService.getCurrentUser().$bindTo($scope, "user");
$scope.$watch('user', function() {
for (var key in $scope.user.finishedLessons) {
if ($scope.user.finishedLessons.hasOwnProperty(key)) {
if ($scope.user.finishedLessons[key].associatedCourseId == $stateParams.id) {
$scope.finishLessonCount++;
}
}
};
console.log ($scope.finishLessonCount);
});
Or, having familiarized with the AngularFire API, one might pick $scope.user.$watch() in place of the scope method, which would prove more efficient.
Having written a large portion of the AngularFire code, I would pick the $extend tool, which was added precisely for use cases like this:
// making some assumptions here since you haven't included
// the code for your firebase service, which does not seem SOLID
app.factory('UserWithLessonsCounter', function($firebaseObject) {
return $firebaseObject.$extend({
$$updated: function(snap) {
var changed = $firebaseObject.prototype.$$updated.call(this, snap);
this.lessonCount = countLessons(this.finishedLessons);
return changed;
}
});
});
function countLessons(lessons) {
var count = 0;
for(var key in lessons) {
if( lessons.hasOwnProperty(key) ) {
count++;
}
}
return count;
}
And now in your controller:
app.controller('...', function($scope, UserWithLessonsCounter) {
var ref = new Firebase(...);
var user = new UserWithLessonCounter(ref);
user.$bindTo($scope, 'user');
user.$watch(function() {
console.log($scope.user.lessonCount);
});
});
I have a function in my controller which request dataservice for data and I am trying to add to current scope variable but it is giving me undefined error
$scope.affiliates = d.data;
if (isNaN($scope.affiliate_id)){
var i = 0;
while (i < $scope.affiliates.length){
var affiliate_id = $scope.affiliates[i].affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
$scope.affiliates[i].apiData = apiData;//ERROR IN HERE
});
i++;
}
}
TypeError: $scope.affiliates[i] is undefined
I have also tried returning data from dataService and set it outside but it always returns empty.
How can i resolve this?
Don't forget that getAffiliateConversionApiService is returning a promise (which means it is an async operation) therefore your while block will execute for every possible i value before you even get the result from getAffiliateConversionApiService.
Let's imagine that $scope.affiliates.length is 6. When your callback code inside the then is executed, your i will be 7.
One solution is to use angular.forEach instead of the while. However, if you still want to use the while you will need to store the i value in another variable:
while (i < $scope.affiliates.length){
var index = i;
var affiliate_id = $scope.affiliates[i].affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
$scope.affiliates[index].apiData = apiData;
});
i++;
}
This is not really an answer, but here is some debugging that should help you out.
Also, I switched from using a while loop to using angular's forEach
$scope.affiliates = d.data;
if (isNaN($scope.affiliate_id)){
console.log($scope.affiliates); // what does this return?
angular.forEach($scope.affiliates, function(value, index){
console.log(value); // output the affiliate, if any
var affiliate_id = value.affiliate_id.replace(/["']/g, "");
DataService.getAffiliateConversionApiService(affiliate_id).then(function(apiData){
console.log(apiData); // see what is returned
$scope.affiliates[index].apiData = apiData; // still an error?
});
});
}