Angular nested controller initialization - javascript

I have simple angular app with a Main Controller and a child controller. Maincontroller loads the login page and on successfull login, a welcome page is loaded in using the child controller. The app is simple one and is working. My issue is to set the logged in user name after successfull login.
.controller('mainController', function($rootScope, $location, Auth) {
var vm = this;
vm.loggedinUser = Auth.getActiveUser();
console.log(">>>>>> ", vm.loggedinUser);
//check to see user is logged in for every request made
$rootScope.$on('$routeChangeStart', function() {
vm.loggedIn = Auth.isLoggedIn();
});
My html page is simple:
<body ng-app="bookapp" ng-controller="mainController as main">
..
<li class="navbar-text">Hello {{ main.loggedinUser }}! >
Issue is that loggedinUser value is not displayed.
1) For it to work i have to initialize the loggedinUser value within the routechange listener.(moving the line to within listener works). Why is it so?
2) Also note that the console.log statement prints the real value after log in and when the welcome page is displayed. But variable value is set in main Controller during the application load itself(login page) and can only be changed through route change listener. It is not re-initialized during welcome page load.

You can either pass an object reference to the controller or use the $watch service to evaluate the the getActiveUser function every digest and update the controller scope.
angular.module("bookapp", []);
angular.module("bookapp").controller("mainCtrl", function(Auth) {
var vm = this;
vm.userData = Auth.data;
vm.login = function() {
Auth.data.loginTimeStamp = new Date();
Auth.data.user = "TestUser";
};
});
angular.module("bookapp").factory("Auth", function() {
var data = {
loginTimeStamp: null,
user: null,
};
return {
data: data
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="bookapp">
<div ng-controller="mainCtrl as main">
<p>Hello {{ main.userData.user }}!</p>
<button ng-click="main.login()">Log In</button>
</div>
</body>
angular.module("bookapp", []);
angular.module("bookapp").controller("mainCtrl", function($scope, Auth) {
var vm = this;
$scope.getUser = Auth.getActiveUser;
vm.login = function() {
Auth.setActiveUser("TestUser");
debugger;
};
$scope.$watch(function(scope) {
return scope.getUser();
}, function(newValue) {
vm.loggedinUser = newValue;
debugger;
});
});
angular.module("bookapp").factory("Auth", function() {
var data = {
loginTimeStamp: null,
user: null,
};
return {
getActiveUser: function() {
return data.user;
},
setActiveUser: function(userName) {
data.user = userName;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<body ng-app="bookapp">
<div ng-controller="mainCtrl as main">
<p>Hello {{ main.loggedinUser }}!</p>
<button ng-click="main.login()">Log In</button>
</div>
</body>

Related

Why can't I print the value from input type using AngularJS?

Here is a very simple application that lets the user change his name. However, there is some bug in this code. Whenever the user writes something into input, the change is not reflected in the welcome header.
app.js
'use strict';
angular
.module('angularScopesBug', [])
.controller("WelcomeController", function ($scope) {
$scope.name = 'John Doe';
$scope.getName = function() {
return $scope.name;
};
})
.controller("EditingController", function($scope) {
$scope.editMode = false;
$scope.changeName = function() {
$scope.editMode = true;
};
$scope.closeEditor = function() {
$scope.editMode = false;
};
})
.directive("nameEditor", function () {
return {
template: 'Write your name: <input type="text" ng-model="name">'
};
});
index.html
<div ng-app="angularScopesBug" ng-controller="WelcomeController">
<h1>Hello, {{ getName() }}</h1>
<div ng-controller="EditingController">
<button ng-click="changeName()" ng-show="!editMode">Change your name</button>
<div ng-show="editMode">
<name-editor ng-show="editMode"></name-editor>
<button ng-click="closeEditor()" ng-show="editMode">Close name editor</button>
</div>
</div>
</div>
The header should change according to the input.
Always use an object in ng-model.
Primitives have no inheritance and the nested controller is using a child scope. When that child scope is created it gets the primitive name as value.
When it is an object the object is inherited so that updating property values will be reflected in all references to that object
angular
.module('angularScopesBug', [])
.controller("WelcomeController", function($scope) {
$scope.model = {
name: 'John Doe'
};
})
.controller("EditingController", function($scope) {
// simplified for clarity
})
.directive("nameEditor", function() {
return {
template: 'Write your name: <input type="text" ng-model="model.name">'
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" ></script>
<div ng-app="angularScopesBug" ng-controller="WelcomeController">
<h1>Hello, {{ model.name }}</h1>
<div ng-controller="EditingController">
<button ng-click="editMode = true" ng-show="!editMode">Change your name</button>
<div ng-show="editMode">
<name-editor ng-show="editMode"></name-editor>
<button ng-click="editMode = false" ng-show="editMode">Close name editor</button>
</div>
</div>
</div>
Your two controllers have different $scope values. Since the editor is inside the EditingController $scope, it is changing a .name value in the lower scope, not in the higher WelcomeController $scope.
Try giving yourself a parent method to modify the value;
$scope.changeName = function(str) {
$scope.name = str;
};
And then calling that method with the new name from the child.

ng-show - using a service as a scope parameter

I'm writing an angular 1.5.0-rc0 application using bootstrap for a nav bar component.
I want to show the user an added items to his navigation bar if his user group id is 1.
first I created a service:
app.factory('UserService', function() {
return {
userGroupId : null
};
});
I created the nav bar as a directive, so i included it in the main html file
<nav-bar></nav-bar>
and the nav-bar directive code:
(function () {
angular.module('myalcoholist').directive('navBar', function () {
return {
restrict: 'E',
templateUrl: 'views/nav.html',
controller: ['$scope','$auth', 'UserService',function ($scope,$auth,UserService) {
$scope.user=UserService;
$scope.isAuthenticated = function()
{
return $auth.isAuthenticated();
};
}]
}
});
})();
as you can see I set $scope.user as the returned object from UserService.
in my login controller, after a successful login I set the userGroupId.
angular.module('myalcoholist').controller('LoginController',['$scope','$auth','$location', 'toastr','UserService',function ($scope,$auth,$location,toastr,UserService) {
$scope.authenticate = function (provider) {
$auth.authenticate(provider).then(function (data) {
var accessToken = data.data.token;
apiKey=accessToken;
UserService.userGroupId=data.data.user_group_id;
...
now.. my nav-bar template file is as the following code:
<li ng-show="user.userGroupId == 1">
Admin Drinks
</li>
even after the authentication, when I uset userGroupId to 1 the element is still not shown.
any ideas?
update
I debugged and noticed that UserService.userGroupId is still null. so
I changed the UserService to have the following code:
app.factory('UserService', function() {
var user = {userGroupId:null};
return {
setUserGroupId: function (userGroupId) {
user.userGroupId=setUserGroupId;
},
getUserGroupId: function () {
return user.userGroupId;
}
};
});
in my LoginController I now try to execute setUserGroupId:
angular.module('myalcoholist').controller('LoginController',['$scope','$auth','$location', 'toastr','UserService',function ($scope,$auth,$location,toastr,UserService) {
$scope.authenticate = function (provider) {
$auth.authenticate(provider).then(function (data) {
var accessToken = data.data.token;
apiKey=accessToken;
UserService.setUserGroupId(data.data.user_group_id);
...
when I debug i see that userService is an object with two functions as I defined, but when the javascript chrome debugger tries to execute this line:
UserService.setUserGroupId(data.data.user_group_id);
I get the following error:
ReferenceError: setUserGroupId is not defined
at Object.setUserGroupId (app.js:21)
at login-controller.js:12
at angular.js:15287
at m.$eval (angular.js:16554)
at m.$digest (angular.js:16372)
at m.$apply (angular.js:16662)
at g (angular.js:11033)
at t (angular.js:11231)
at XMLHttpRequest.v.onload (angular.js:11172)
I have created a fiddle showcasing your requirement (as close as possible), and it seems to work fine.
http://jsfiddle.net/HB7LU/21493/
My guess is that you aren't actually setting the value when you think you are, and will likely require some debugging. Here is the code for brevity.
HTML
<div ng-controller="MyCtrl">
<div ng-click="clicked()">
Click ME, {{user.value}}!
</div>
<test-dir></test-dir>
</div>
JS
angular.module('myApp',[])
.service('TestService', function(){
return {
value: 2
};
})
.directive('testDir', function(){
return {
restrict: 'E',
template: '<div ng-show="user.value === 1">Here is some text</div><div>Some more always showing</div>',
controller: function ($scope, TestService) {
$scope.user = TestService;
}
};
})
.controller('MyCtrl', function($scope, TestService){
$scope.user = TestService;
$scope.clicked = function(){
TestService.value = 1;
};
});

How to bind and automatically update view when $scope is updated in 2 different controllers in Angularjs

Here is a fiddle: http://jsfiddle.net/a0eLhcbm/
I have a simple setup:
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<div ng-controller="UsernameController">
{{ user.name }}
</div>
</div>
There is a function that will, say, get the user.name from somewhere else using ajax, and that function belongs to controller PageController.
Question: Is there anyway I can make the {{ user.name }} within the UsernameController to update itself as soon as the controller PageController receives the information?
Here is my javascript setup:
var app = angular.module( 'demo', [] );
function_that_fetches_for_username = function() {
//Some function that fetch for username asynchronously
};
app.controller( 'PageController', function ( $scope ) {
//Initial data
$scope.page = {};
$scope.page.time = Date();
function_that_fetches_for_username();
//How can I make the UsernameController to update its view from this Controller as soon as this controller receives the information?
});
app.controller( 'UsernameController', function( $scope ) {
//Initial data
$scope.user = {};
$scope.user.name = "";
//How can I automatically updated the $scope.user.name in view as soon as the PageController receives the information about the username?
});
There are probably a lot of ways to solve this problem, my share to this is to use either of the two below:
[1] Create a service that you can share to any part of your application (Controllers, Services, Directives, and Filters).
In relation to your problem, you can simply create a User service that can be shared across your controllers. The solution below assumes that the function_that_fetches_for_username() is a service UserResource that has a method get() that simulates fetching data from a server. The User service is an empty object that is shared across all your controllers.
DEMO
JAVASCRIPT
angular.module('demo', [])
.service('UserResource', function($timeout) {
this.get = function() {
return $timeout(function() {
return {
id: 'w3g45w34g5w34g5w34g5w3',
name: 'Ryan'
};
}, 2000);
};
})
.service('User', function() {
return {};
})
.controller('PageController', function($scope, UserResource, User) {
$scope.page = {
time: Date()
};
UserResource.get().then(function(data) {
angular.extend(User, data);
});
})
.controller('UsernameController', function($scope, User) {
$scope.user = User;
});
HTML
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<hr>
<div ng-controller="UsernameController">
<div ng-if="user.name">
{{ user.name }}
</div>
<div ng-if="!user.name" style="color: red">
Waiting for Response...
</div>
</div>
</div>
[2] Use the controllerAs syntax for declaring controllers. Use this type of notation for child controllers to access parent controllers using their aliases.
DEMO
JAVASCRIPT
angular.module('demo', [])
.service('UserResource', function($timeout) {
this.get = function() {
return $timeout(function() {
return {
id: 'w3g45w34g5w34g5w34g5w3',
name: 'Ryan'
};
}, 2000);
};
})
.controller('PageController', function(UserResource) {
var ctrl = this;
ctrl.page = {
time: Date()
};
ctrl.user = {};
UserResource.get().then(function(data) {
angular.extend(ctrl.user, data);
});
})
.controller('UsernameController', function() {
this.getUser = function(user) {
console.log(user);
};
});
HTML
<div ng-app="demo" ng-controller="PageController as PC">
{{ PC.page.time }}
<hr>
<div ng-controller="UsernameController as UC">
<div ng-if="PC.user.name">
{{ PC.user.name }}
</div>
<div ng-if="!PC.user.name" style="color: red">
Waiting for Response...
</div>
<button type="button" ng-click="UC.getUser(PC.user)"
ng-disabled="!PC.user.name">
Access user from Page Controller
</button>
</div>
</div>
You can do one of these for sharing the same value through multiple controllers:
Promote the value to a higher level scope all the interested controllers have access to. Controllers will get it through scope inheritance because angular automatically searches the value through the scope hierarchy.
Whoever gets the value broadcasts it through the higher level scope all the controllers have access to. All the controllers listening for this broadcast will get the value.
you can define your user in pageController(that is parent controller to UsernameController) now whenever you change it in pageController it will also be updated in usernameController
second solution is to have ng-view in parent, and in route use controller for UsernameController
index file
<div ng-app="demo" ng-controller="PageController">
{{ page.time }}
<ng-view></ng-view>
</div>
user.html
<div ng-controller="UsernameController">
{{ user.name }}
</div>
route codee
.when("/user",{
controller:"usernameController",
templateUrl : 'user.html'
})
Third solution is to make a service
.factory("userFactory",function(){
var user = {};
return{
setUser : function(usern){
user = usern;
},
getUser : function(usern){
return user;
},
}
})
now you can get user from service and set to service .

Revealing module for AJAX Angular Service

Below I've got an angular app and controller where the controller have data access inside of it (bad idea, I know)
var app = angular.module('app',[]);
app.controller('HomeController',function($scope,$http){
$scope.people = null;
$scope.get = function() {
$http({
url: 'largeTestData.json',
method: 'GET'
}).then(function(data){
console.log('request successful, here is your data: ');
console.log(data['data']);
$scope.people = data['data'];
},function(reason){
console.log('this failed, this is the reason: ');
console.log(reason);
})
}
});
app.controller('ControllerWithService',function($scope, MyService){
$scope.get = MyService.get;
$scope.get(function(data){
console.log('you succeeded');
},function(reason){
console.log('you failed');
console.log(reason);
})
})
This will work in retrieving data and putting it onto the page. Knowing that having data Access in the controller is no bueno I tried to abstract that out into a service:
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
Here my 'data layer' is a service that only has one method: get from the above listed URL.
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
and my HTML
<body>
<script src="libs/angular-1.2.15.js"></script>
<script src="app/app.js"></script>
<script src="app/DocumentService.js"></script>
<script src="libs/jQuery-2.1.1.js"></script>
<div ng-controller="HomeController">
<button ng-click="get()" href="#">Get data</button>
<div>{{message}}</div>
<!--<div ng-repeat="p in people" >-->
<!--<b>Business Doc ID: </b><h1>{{p['busDocId']}}</h1>-->
<!--<b>DOC ID: </b>{{p['docId']}}-->
<!--<b>FILE NAME: </b><div style="color: green">{{p['fileName']}}</div>-->
<!--</div>-->
</div>
<div ng-controller="ControllerWithService">
{{message}}
<button ng-click="get()">get data</button>
<div>{{data}}</div>
</div>
</body>
I'm not getting any error messages, and the commented out out stuff in my HomeController works as expected. What am I doing wrong in trying to make my AJAX calls a service?
working solution changes:
app.service('MyService',function($http,$q){
this.get = function(){
return $http.get('test.json')
}
})
app.controller('ControllerWithService',function($scope, MyService){
$scope.data = null;
$scope.get = function() {
MyService.get().then(function (data) {
console.log('this is the success data: ');
console.log(data)
$scope.data = data;
}, function (reason) {
console.log('this is the fail reason');
console.log(reason);
$scope.data = reason;
})
}
})
It looks like it could be a couple different things. I'll post an example I have working in one of my projects right now. It should be extremely similar and simple with what you're goal is.
Service:
'use strict';
angular.module('srcApp')
.service('Getlanguage', function Getlanguage($location, $http, $log, $state, $rootScope) {
this.getContent = function() {
var language = $location.path().split('/'),
languageCulture = language[1];
if (!languageCulture) {
languageCulture = 'en';
}
$rootScope.cultureCode = languageCulture;
return $http({method: 'GET', url: '/languages/' + languageCulture + '.json'})
.error(function() {
// If service cannot find language json file, redirect to index
$state.go('lang', {lang: 'en'});
});
};
});
Controller Call to service:
After passing in the service as a dependency into the controller.
Getlanguage.getContent().then(function(res) {
$scope.content = res.data;
});
Hope this helps.

use AngularJs NgResource to load JSON file from localhost

Overview
I am building an app (running on MAMP) that holds contact information that will expand to hold more data such as project name & deadline, once this part is functional.
Questions
When the user visits /projects.php#/project/ I would like them to see a list of all the project names with a link to their detail page.
How should I write the following to access all of my data?
Do I need the .json at the end?
What does the #id do?
return $resource('data/project.json/:id', {id: '#id'});
When the user visits /projects.php#/project/a-gran-goodn I would like them to see the details about this project(for now, just the name & address).
How should I write the following to return my data by Id?
$scope.project = $routeParams.id ? Project.get({id: $routeParams.id}): new Project();
plunkr file
http://plnkr.co/edit/7YPBog
project.json
This file lives on http://localhost:8888/angularjs/ProjectsManager/data/project.json
[
{ "address" : [ " 3156 Dusty Highway",
" Teaneck New Jersey 07009-6370 US"
],
"id" : "a-gran-goodn",
"name" : "Grania Goodner",
"phone" : " (862) 531-9163"
},
{ "address" : [ " 62 Red Fawn Moor",
" Rodney Village West Virginia 25911-8091 US"
],
"id" : "b-aime-defranc",
"name" : "Aimery Defranco",
"phone" : " (681) 324-9946"
}
]
app.js
var projectsApp = angular.module('projects', ['ngResource']);
projectsApp.config(function($routeProvider) {
$routeProvider
.when('/', {
controller: 'ProjectListCtrl',
templateUrl: 'partials/projectlist.html'})
.when('project/:id', {
controller: 'ProjectDetailCtrl',
templateUrl: 'partials/projectdetail.html'
})
.otherwise('/');
});
projectsApp.factory('Project', function($resource) {
return $resource('data/project.json/:id', {id: '#id'});
});
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
console.log($scope.projects);
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get({id: $routeParams.id})
: new Project();
});
partials/projectlist.html
Add new item
<ul class="unstyled">
<li ng-repeat="project in projects">
<div class="well">
<h2><small>{{project.id}}</small> {{project.name}}</h2>
View Info for {{project.name}}
</div>
</li>
</ul>
partials/projectdetails.html
<h3>Information</h3>
<p>Name: {{project.name}}</p>
<p>Phone Number: {{project.phone}}</p>
<p ng-repeat="line in project.address">{{line}}</p>
index.php
<?php
header('Access-Control-Allow-Origin: *');
?>
<!doctype html>
<html ng-app="projects">
<head>
<meta charset="utf-8">
<title ng-bind="title" ng-cloak>Restaurant —</title>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
</head>
<body ng-controller="ProjectListCtrl">
<a class="brand" href="#">Projects Manager</a>
<div id="app-container" class="container-fluid">
<div class="row-fluid">
<div class="span12" id="main" ng-view>
</div><!--/.span12-->
</div><!--/.row-fluid-->
<footer>Copyright Projects © 2013</footer>
</div><!--/.container-->
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<!-- Don't forget to load angularjs AND angular-resource.js -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.js></script>
<!--Controllers-->
<script src="app.js"></script>
</body>
</html>
Since you can't query against a raw JSON file like you can with RESTful-style URLs (which is what $resource is built to do), you can instead get a copy of the JSON and then build your own query, get, etc. that looks at the data and returns the right thing. It's a bit tricky because you also want to support new Project, which doesn't really make sense when using a file-backed store, but this example supports it:
projectsApp.factory('Project', function($http) {
// Create an internal promise that resolves to the data inside project.json;
// we'll use this promise in our own API to get the data we need.
var json = $http.get('project.json').then(function(response) {
return response.data;
});
// A basic JavaScript constructor to create new projects;
// passed in data gets copied directly to the object.
// (This is not the best design, but works for this demo.)
var Project = function(data) {
if (data) angular.copy(data, this);
};
// The query function returns an promise that resolves to
// an array of Projects, one for each in the JSON.
Project.query = function() {
return json.then(function(data) {
return data.map(function(project) {
return new Project(project);
});
})
};
// The get function returns a promise that resolves to a
// specific project, found by ID. We find it by looping
// over all of them and checking to see if the IDs match.
Project.get = function(id) {
return json.then(function(data) {
var result = null;
angular.forEach(data, function(project) {
if (project.id == id) result = new Project(project);
});
return result;
})
};
// Finally, the factory itself returns the entire
// Project constructor (which has `query` and `get` attached).
return Project;
});
You can use the results of query and get like any other promise:
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get($routeParams.id)
: new Project();
});
Note the change to Project.get($routeParams.id); also, the updated Plunker also fixes a problem in your $routeProvider configuration.
This is all demonstrated here: http://plnkr.co/edit/mzQhGg?p=preview
i will paste here a generic code i use to fetch json from your local or a remoteserver maybe it will help you:
it uses a factory that you can call when you need it.
app.factory('jsonFactory', function($http) {
var jsonFactory= {
fromServer: function() {
var url = 'http://example.com/json.json';
var promise = $http.jsonp(url).then(function (response) {
return response.data;
});
return promise;
},
hospitals: function() {
var url = 'jsons/hospitals.js';
var promise = $http.get(url).then(function (response) {
return response.data;
});
return promise;
}
};
return jsonFactory;
});
Then when you need to call it:
function cardinalCtrl(jsonFactory, $scope, $filter, $routeParams) {
jsonFactory.hospitals().then(function(d){
$scope.hospitals=d.hospitals;
});
jsonFactory.fromServer().then(function(d){
$scope.fromServer=d.hospitals;
});
}

Categories