Why is my variable "unresolved" in AngularJS - javascript

I have a really stupid question and I'm hoping someone can help me understand AngularJS a little better here whilst I trawl through more documentation... please be aware that I have been working with AngularJS for a week now as I have inherited a project off a colleague, anyway...
The unit tests associated with my project are failing with the following error message "scope.signupForm is undefined in /Users/.../.../.../app/login/login.js"
In WebStorm my code is being highlighted (underlined grey) with the following messages "Unresolved Variable signinForm" & "Unresolved Variable signupForm", the code where this is being raised is below...
this is part of the controller...
function LoginController($scope, userService) {
$scope.loggedInUser = null;
$scope.signIn = function (user) {
console.log("SignIn");
$scope.loggedInUser = { userName: user.userName };
$scope.user = undefined;
$scope.signinForm.$setPristine(); // Error here is "Unresolved Variable signinForm"
};
$scope.register = function (user) {
console.log("Register");
$scope.loggedInUser = user;
$scope.user = undefined;
console.log(user);
userService.addUser(user);
$scope.signupForm.$setPristine();// Error here is "Unresolved Variable signupForm"
};
$scope.signOut = function () {
console.log("SignOut");
$scope.loggedInUser = undefined;
$scope.signInVisible = false;
};
... // more code here
Now this is my HTML code contained in a View (for want of a better word)
<div id="login-signin" class="loginLeftBox">
<form name="signinForm" novalidate ng-submit="signIn(loginUser)" autocomplete="off">
<div> ... Form Stuff...</div>
</form>
<div ng-show="signinForm.userName.$dirty && signupForm.userName.$invalid">
... Validation Stuff...
</div>
<div ng-show="signinForm.password.$dirty && signupForm.password.$invalid">
... Validation Stuff...
</div>
</div>
<div id="login-register" class="loginRightBox">
<form name="signupForm" novalidate ng-submit="register(user)" autocomplete="off">
... Form Stuff...
</form>
</div>
Any explanations would be appreciated...

You have to place your controller in the same level as the form:
<form name="signinForm" ng-controller="SinginFormCtrl" ...>
Then the SinginFormCtrl will have the signinForm in scope, e.g.:
function SinginFormCtrl($scope, userService) {
$scope.signIn = function (user) {
...
$scope.signinForm.$setPristine(); // WILL WORK NOW
};
...
}
This probably means that you will have to restructure your code a bit.

I found that, after writing $scope to the console that both $scope.signinForm & $scope.signupForm where present and defined! Thus I added the following condition to the controller and now all the Unit Tests seem to work?
reset = function(){
if($scope.signinForm){
$scope.signinForm.$setPristine();
}
if($scope.signinForm){
$scope.signupForm.$setPristine();
}
};
Not sure if this is a solution or a hack?

Related

Using 'Ng-If' "breaks" program on a $('#x').change or an angularfire $add

I've noticed two instances in my code where using ng-if causes my program to start working. On one, I do an ng-if="isOwnProfile", for an image-upload toolbar.
Using the ng-if causes the event listener to stop working. Code example:
$scope.myOwnProfile = false;
if (userLoggedIn === userUID) {
console.log('my images');
$scope.myOwnProfile = true;
} else {
console.log('not my images');
}
$("#image-upload").change(function(e) {
$scope.myOwnProfile = true;
var file = e.target.files[0];
var imageRef = firebase.storage().ref...
and in HTML:
<section ng-if="myOwnProfile">
<input id="image-upload" type="file" accept="image/*"> <br /><br />
</section>
In this case the event listener will stop working and not respond.
Another case is where you add a message to the page (and firebase).
Code:
$scope.addMessage = function(){
var date = new Date();
$scope.messages.$add({
timestamp: (date.getMonth() + 1) + "/" + date.getDate() + "/" + date.getFullYear(),
from: $scope.visitorRealName.name,
content: $scope.message
});
$scope.message = '';
};
HTML:
<section ng-if="AreWeFriends === true || myOwnProfile === true">
<form ng-submit="addMessage()">
<input ng-model="message">
<button type="submit">Add Message</button>
</form>
</section>
In the second case I get an error from Firebase "Key content was undefined. Cannot pass undefined in JSON. Use null instead".
I can't determine why using ng-if causes this to happen? What I do is set the user's profile to true whether they are a) a friend or b) it's the person's own profile (which is why I change $scope).
That's because the ngIf directive creates it's own child scope.
The ngIf directive creates it's own scope, so the ngModel directive containing message is set on the scope created by the ngIf directive, not your controller.
When you access the value in your controller it's not there, so it's undefined. You are essentially passing undefined to your content key inside your object you're adding to your messages so Firebase complains.
To fix it i'd recommend using the controller as syntax so that you can reference the controller, or use the ngShow directive, which doesn't create it's own child scope.
Here are a few examples of what happens:
(function() {
'use strict';
angular.module('app', []);
})();
(function() {
'use strict';
angular.module('app').controller('MainController', MainController);
MainController.$inject = ['$scope'];
function MainController($scope) {
var vm = this;
$scope.test1 = test1;
$scope.test2 = test2;
$scope.test3 = test3;
$scope.test4 = test4;
function test1() {
// this is undefined because there is no property `message`
// on the $scope of this controller
alert($scope.message);
}
function test2() {
// this contains the value binded to the message property
// of this controller because we used the controller as syntax
alert(vm.message);
}
function test3(message) {
// because we are passing in the message value we can
// access it without caring where it came from
alert(message);
}
function test4() {
// the property `message` exists on this $scope because we
// used the ngShow directive instead of the ngIf
alert($scope.message4);
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainController as MainCtrl">
<h4>Without controller as syntax</h4>
<p>Does not work because message is not set on the controller's $scope, it's set on the scope created by the ngIf directive</p>
<form ng-if="true">
<input ng-model="message">
<button ng-click="test1()">Submit</button>
</form>
<hr>
<h4>With controller as syntax</h4>
<p>Works because message is set on the controller so we can access it using `this` in our MainController</p>
<form ng-if="true">
<input ng-model="MainCtrl.message">
<button ng-click="test2()">Submit</button>
</form>
<hr>
<h4>Without controller as syntax but passing the message value into our function</h4>
<p>Works because although message is set on the scope created by the ngIf directive, we are passing it to test3 in our MainController.</p>
<form ng-if="true">
<input ng-model="message">
<button ng-click="test3(message)">Submit</button>
</form>
<hr>
<h4>With ngShow directive instead of ngIf</h4>
<p>Works because message is set on $scope from our contoller</p>
<form ng-show="true">
<input ng-model="message4">
<button ng-click="test4()">Submit</button>
</form>
</div>

Angulrjs: A controller doesn't send a value via a factory with the "as" statement

I've been teaching myself how to use the as statement of Angularjs's controller, but struggling to make controllers communicate with others, using the as syntax.
<script type="text/javascript">
angular.module('angularApp', [])
.factory('MessageService', function(){
var message = {
addedItem: "initialMessge"
};
return {
returnMessage: message//This is supposed to be the "var message" defined above
};
})
.controller('DiaplayingProductController', function(MessageService){
var instance = this;
this.data = {
message: MessageService.returnMessage.addedItem
};
})
.controller('ProductController', function($scope, $http, MessageService) {
var instance = this;
this.data = {
message: MessageService.message,
//There are other stuff here
};
this.addItem = function(productName) {
$http({
//other tasks
}).then(function addSucces(response) {
instance.data.message.addedItem = productName;
});
};
});
<span ng-controller="DiaplayingProductController as dpc" ng-bind="dpc.data.message"></span>
<div ng-controller="ProductController as pc">
#foreach ($products as $index => $product)
<div class="product">
<button ng-click="pc.addItem({{$product->name}})>
Add it to Cart
</button>
</div>
#endforeach
</div>
I use Laravel, so {{$product->name}} and #foreach are Laravel's expression.
In a nutshell,
There are one <span> and multiple <button>s, based on the result of #foreach (Again, I use Laravel, so this is basically the same thing as php's foreach)
When one of the <button> is pressed, the content of <span> is supposed to be updated.
The event is triggered in ProductController, which is supposed to update message of DiaplayingProductController, via MessageService.
The message is not going to be sent to the span tag.
This question may be silly. However, there are not many information resources out there which deal with this as statements, so I'd like to ask some advice here. Thank you in advance!
What's this #foreach?
There's a coma in your attributes. Shouldn't be there.
The expression in your ng-click has a missing parenthesis. Also, it should be an expression, therefore the {{}} have nothing to do here.
The data object are not shared between the controllers. You should:
use directives and pass the data using attributes ('=').
set the data in the $scope, which is not as good a solution
use a service as an intermediary (each controller can set/get the value
from that service)

Ng-model with Cookie

I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/

Submit form on page load in Angular

I would like to submit a search form on page load if search terms were specified in the route. The problem is that searchForm isn't defined yet when search() runs. I've seen others get this to work by putting ng-controller right on the form element, but I have multiple forms on this page, so the controller has to be a parent of the forms.
How can I ensure the form is defined when I call search()?
myModule.controller('MyController', ['$scope', '$routeParams',
function($scope, $routeParams){
$scope.model = {searchTerms: ""};
$scope.search = function(){
if($scope.searchForm.$valid){
...
}
};
if($routeParams.terms !=""){
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
}
}]);
View:
<div ng-controller="MyController">
<form name="searchForm" ng-submit="search()">
...
</form>
<form name="detailForm" ng-submit="save()">
...
</form>
</div>
This seems to work:
$scope.$on('$routeChangeSuccess', function () {
if($routeParams.terms !=""){
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
}
});
Have you tried just using $watch on searchForm?
if($routeParams.terms != "") {
var unregister = $scope.$watch(function() {
return $scope.searchForm;
}, function() {
// might want to wrap this an if-statement so you can wait until the proper change.
unregister(); //stop watching
$scope.model.searchTerms = $routeParams.terms;
$scope.search();
});
}

Using angularjs without $routeProvider & $location?

I have been playing with angularjs for couple of days and like it so far. I am trying to build a chrome extension which attaches a small widget below every gmail message when the user is on gmail.com. So far so good. As part of authentication code, I handle 401 error in this way. Whenever there is a 401 error, I use $location.path( "/login" ) to redirect the user to the login screen/template. This changes browser address bar which seems to be the default behavior. So, if the current address was https://mail.google.com/mail/u/0/, it becomes https://mail.google.com/mail/u/0/#/login. But mine is not standalone app, its more like widget that attaches to a div when on gmail.com site. My app should not mess with the browser address bar. I am now starting to think if I can really use angularjs for my app as I am going against the default behavior. Should I use angularjs at all?
I also posted it here
https://groups.google.com/forum/?fromgroups#!topic/angular/TrT54r_IYmg
You can emit/broadcast events on rootScope and subscribe to them in your login directive.
Here is little clue http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application
it uses interceptors to catch 401
myapp.config(function($httpProvider) {
var interceptor = ['$rootScope','$q', function(scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
}
scope.requests401.push(req);
scope.$broadcast('event:loginRequired');
return deferred.promise;
}
// otherwise
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
});
And simplest directive could be this
myapp.directive("loginForm",function($http){
return function(scope,element,attrs){
element.hide();
scope.$root.$on('event:loginRequired', function(event) {
element.show();
});
scope.login=function(){
// You can set controller for this directive, but I skiped that part for sake of simplicity
var payload = $.param({username: scope.username, password: scope.password});
var config = {
headers: {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}
}
$http.post('/some/login/url', payload, config).success(function(data) {
if (data === 'AUTHENTICATION_SUCCESS') {
element.hide();
}else{
alert("username or password was wrong, please try again");
elements.find("form").reset(); // reset form, or you coud reset just password field
}
});
};
};
});
Now, directive in action
<div login-form>
<form ng-submit="login()">
<label for="username">Username</label>
<input type="text" id="username" ng-model="username"/>
<br />
<label for="password">Password</label>
<input type="password" id="password" ng-model="password" />
<hr/>
<input type="submit" value="Login" />
</form>
</div>
Please note code above is not tested, probably there is some misspell or something. But let me know if you have trouble implementing this, I will try to take some time and effort to make it work.

Categories

Resources