Data-binding between ngInclude and ngView - angularjs

I want to make a sidebar with list item that can be dynamically changed based on the settings page.
My app request settings.json via factory() and then called it in a controller. The controller will be used by settings.html (ngView) and sidebar.html (ngInclude).
The json will return Boolean value that also can be changed on setting page that contain checkbox which return true if check and false if not checked. I use ngShow on the sidebar to display/hide the list items.
How can I made the sidebar to reflect the changes as I tick the checkbox?
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
return settingsFactory;
}]);
controller
var settingsControllers = angular.module('settingsControllers', ['settingsFactory']);
settingsControllers.controller('SettingsFilterController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
$scope.settings;
$scope.status;
getSettings();
function getSettings() {
SettingsFilterFactory.getSettings()
.then(function (response) {
$scope.settings = response.data;
}, function (error) {
$scope.status = 'Unable to load: ' + error.message;
});
}
}]);
app.js
var app = angular.module('app', ['ngRoute', 'settingsControllers']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/settings', {
title: 'Settings',
templateUrl: 'app/components/settings/settings.html',
controller: 'SettingsFilterController'
}).
otherwise({
redirectTo: '/home'
});
}]);
My index.html is something like this:
...
<body>
<section class="sidebar">
<div ng-include="'app/components/sidebar/sidebar.html'"></div>
</section>
<section class="content">
<div ng-view></div>
</section>
</body>
...
sidebar.html
<ul class="sidebar-menu" ng-controller="SettingsFilterController">
<li ng-show"settings.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settings.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...

Try something like this (untested):
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
settingsFactory.hiddenMenu= true;
settingsFactory.someOtherSetting = {};
return settingsFactory;
}]);
sidebar controller
settingsControllers.controller('SidebarController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
//do this in each controller, so that the factory becomes a property of $scope and can be seen in the HTML
$scope.settingsFactory = SettingsFilterFactory;
}
sidebar.html
<ul class="sidebar-menu" ng-controller="SidebarController">
<li ng-show"settingsFactory.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settingsFactory.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Essentially, you are binding the settingsFactory object which is a singleton to each $scope that is provided by each controller. Each controller is able to change the property on the factory object, which is then visible in all other controllers that have injected this object.

Related

When I used ng-include to load html dynamically, the controller of that html is not loaded

I have following scenario
assemblyHeader.js
$scope.AssemblyHeaderDetails = function () {
if {
mixpanelEventTrack('Quote Details Clicked');
var RFQDetailInstance = $modal.open({
templateUrl: 'Scripts/ng/View/QuoteDetailPopup.html',
windowClass: 'QuoteDetail',
controller: 'QuoteDetailPopupController',
resolve: {
AssemblyId: function () { return $scope.AssemblyInfo.assemblyid; },
parentScope: function () {
return $scope;
}
}
});
RFQDetailInstance.result.then(function (result) {
});
}
}
Following is my QuoteDetailPopup.html
<link href="Content/PageCss/requestFormView.css" rel="stylesheet" />
<div>
<div ng-include src="TemplateUrl" >
hello world!!!
</div>
</div>
Following is my controller for html QuoteDetailPopupController.js
(function () {
'use strict';
var controllerId = 'QuoteDetailPopupController';
angular.module('CalcQouteModule').controller(controllerId,
['$scope', 'DataConstants', '$filter', 'MessageConstant', '$modalInstance', 'parentScope', '$modal', 'urlConstant', '$timeout', QuoteDetailPopupController]);
function QuoteDetailPopupController($scope, DataConstants, $filter, MessageConstant, $modalInstance, parentScope, $modal, urlConstant, $timeout) {
$scope.TemplateUrl = 'Scripts/ng/View/requestFormView.html';
}
}());
The problem is when modal popup opens it renders HTML page but didn't render the controller of requestFormView.html so the controls on HTML are not loaded properly.
For requestFormView.html the controller name is requestFormController.js
Following popup is opened when I clicked.
How can I load controller?
Add controller file in index.html and in app.js when you hit the url mention controller name over there. Hope this will help you.

how to display AngularJS ng-repeat is empty after ionicLoading

I'm using $ionicLoading for loading the news page, if no news found then this will run on my template: <div ng-show="!news.length" class="empty"> Nothing to show ! </div>
Problem: The empty div (Nothing to show !) is showing on the loading time, so if the news come then that div will hide. now I need to display that message after loading completed if no article found.
My Controller:
.controller('detailsCtrl', function ($scope, $ionicHistory, $stateParams, $http, $ionicLoading, $ionicFilterBar, $rootScope) {
$scope.init = function () {
var id = $stateParams.id;
$ionicLoading.show({
template: '<ion-spinner icon = "ripple" class="spinner-light"></ion-spinner>',
duration: 20000
});
$http.get('http://localhost/json/article_details.php?id=' + id).then(function (output) {
$ionicLoading.hide();
$scope.news = output.data;
console.log(angular.toJson(output.data));
});
};
});
My HTML Template:
<ion-view ng-init="init()">
<!-- If news not found -->
<div ng-show="!news.length" class="empty"> Nothing to show ! </div>
<!-- If news found -->
<div ng-repeat="news in news">
...
There is a simple solution. You can create a boolean variable on the scope that is set to true in the callback of your $http call. You add this as a condition to your Nothing to show message.
Controller
.controller('detailsCtrl', function ($scope, $ionicHistory, $stateParams, $http, $ionicLoading, $ionicFilterBar, $rootScope) {
$scope.isLoadingComplete = false;
$scope.init = function () {
var id = $stateParams.id;
$ionicLoading.show({
template: '<ion-spinner icon = "ripple" class="spinner-light"></ion-spinner>',
duration: 20000
});
$http.get('http://localhost/json/article_details.php?id=' + id).then(function (output) {
$ionicLoading.hide();
$scope.isLoadingComplete = true;
$scope.news = output.data;
console.log(angular.toJson(output.data));
});
};
});
Template
<ion-view ng-init="init()">
<!-- If news not found -->
<div ng-show="isLoadingComplete && !news.length" class="empty"> Nothing to show ! </div>
<!-- If news found -->
<div ng-repeat="news in news">
...

How to load a new page and pass data from api?

I have page with list of teams. On click I need title and members of that team on another page. I just loaded teams in the list. Code is here:
angular.module('my-app').controller('MainController', function($scope, $http) {
$http.get("http://...").success(function(response) {
$scope.details = response;
});
});
and
<article ng-app="seed-angular">
<section ng-controller="MainController as mc">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
As mentioned in the comments by Claies, you need to use a router in your app. From the docs:
Application routes in Angular are declared via the $routeProvider, which is the provider of the $route service. This service makes it easy to wire together controllers, view templates, and the current URL location in the browser. Using this feature, we can implement deep linking, which lets us utilize the browser's history (back and forward navigation) and bookmarks.
There are some libraries in which you can do that. I'll use ngRoute as an example as it comes with Angular.js.
To do so, you need to add the angular-route.js and load the ngRoute module:
var app = angular.module('seed-angular', [
'ngRoute'
]);
You also need to configure your $routeProvider to setup your URL routes, like this:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/team', {
templateUrl: 'team-list.html',
controller: 'MainController'
}).
when('/team/:teamId', {
templateUrl: 'team-detail.html',
controller: 'TeamDetailController'
}).
otherwise({
redirectTo: '/team'
});
}
]);
In the routes above, we've configured /team to route to a template page team-list.html. Note that the <a href=""> now passes a parameter teamId:
<article>
<section ng-controller="MainController">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
We also configured /team/:teamId to route to a template page team-detail.html (note that :teamId means it's a parameter):
<article>
<section ng-controller="TeamDetailController">
Back
<div ng-repeat="x in details">
<strong>{{x._id}}</strong>
<ul>
<li>
{{x.firstName}} {{x.lastName}}
</li>
</ul>
</div>
</section>
</article>
In which you will be able to access teamId via $routeParams.teamId to retrieve the team details:
app.controller('TeamDetailController', function($scope, $http, $routeParams) {
var url = "http://est-teams.estudent.hr/api/teams/" + $routeParams.teamId + "/members";
$http.get(url).success(function(response) {
$scope.details = response;
});
});
See this fiddle for a working example.
if you want to pass the data you can create service and create function that store or set your data, then call it in your first controller that called the data and call it again in your next page controller here is the code :
this is the service :
myApp.service('sharedProperties', function() {
var stringValue = 'test string value';
var objectValue = {
data: 'test object value'
};
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
getObject: function() {
return objectValue;
}
};
});
you can call it in your controller like this :
myController.controller('UserUpdateController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties ) {
$scope.updateUser = function() {
.....
id = sharedProperties.getString();
};
}
]);
and set the data from your previous page like this :
myController.controller('UserListController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties) {
$scope.updateUser = function(id){
console.log("from index "+id);
sharedProperties.setString(id);
};
}
]);

Delay of scope's update after controller's resolve

I'm downloading some data using firebase before presenting new controller's view, but the template "blinks" before showing the data. I have no idea why, it should show the data instantly without any delay. I've recorded it and marked each individual frame (http://i.imgur.com/pUsMCqX.gif). Console logs the data that resolve object returns. You can see that when data is being logged, the template is being shown with no data at frames 2 and 3.
Template:
<div ng-cloak class="wrapper select-character">
<div>
<div>
<h1>Select character</h1>
<div>
<button ng-click="createNewCharacter()">create new character</button>
</div>
characters' list
</div>
</div>
<div ng-repeat="character in characters">
<div>
Name: {{ character.name }}
<br>
Level: {{ character.level }}
<br>
<button ng-click="enterWorld(character.name)">Choose</button>
</div>
</div>
</div>
Controller:
'use strict';
angular.module('App')
.controller('SelectCharacterCtrl', function($scope, $firebaseSimpleLogin, $location, characters) {
$scope.createNewCharacter = function() {
$location.path("/create-character");
};
$scope.enterWorld = function(name) {
alert(name);
};
$scope.characters = characters;
});
App:
'use strict';
angular.module('App', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'firebase',
'angularfire.firebase',
'angularfire.login'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/log-in.html',
controller: 'LogInCtrl'
})
.when('/select-character', {
templateUrl: 'views/select-character.html',
controller: 'SelectCharacterCtrl',
resolve: {
characters: function($q, $timeout, $firebase, $firebaseSimpleLogin) {
var deferred = $q.defer();
var loginObj = $firebaseSimpleLogin(new Firebase("https://<id>.firebaseio.com"));
loginObj.$getCurrentUser()
.then(function(user) { // get login data
var userSync = $firebase(new Firebase('https://<id>.firebaseio.com/users/' + user.uid));
return userSync.$asObject().$loaded();
})
.then(function(user) { // get all characters
var promises = [];
angular.forEach(user.characters, function(name) {
var promise = $firebase(new Firebase('https://<id>.firebaseio.com/characters/' + name));
promises.push(promise.$asObject());
});
$q.all(promises).then(function(sth) {
console.log(sth);
deferred.resolve(sth);
});
});
return deferred.promise;
}
}
})
.when('/create-character', {
templateUrl: 'views/create-character.html',
controller: 'CreateCharacterCtrl'
})
});
Why does the template "blinks" with no data for 2 frames before updating the scope? Any ideas?
Since $asObject does not return a promise, your list of promises are immediately resolved (instead of after all the data is downloaded). Change your list to return promises too:
angular.forEach(user.characters, function(name) {
var promise = $firebase(new Firebase('https://<id>.firebaseio.com/characters/' + name));
promises.push(promise.$asObject().$loaded());
});

How to resolve main controller first then its child controller

I have a controller as main controller and a its child controller. I want to execute first the main controller and then after its child controller.
I am collecting some userdata and want to keep it in $rootScope so that i could access it within the whole application.
How could i do this ?
<body ng-app="angularApp">
<div ng-controller="MainAppController">
<div ng-controller="childController">
Here i want to access data from "MainAppController" stored in $rootScope
But problem is "childController" runs before "MainAppController"
</div>
</div>
</body>
Here is the angular code
angular.module('MyApp').controller('MainAppController', function($rootScope, service){
$rootScope.userData = [];
service.getUserData().success(function(userDetails){
$rootScope.userData = userDetails;
});
});
angular.module('MyApp').controller('childController', function($scope, $rootScope){
$scope.userInfo = $rootScope.userData;
//Here $scope.userInfo is null because $rootScope.userData gets assigned to $scope.userInfo in this controller before userDetails gets assigned to $rootScope.userData in MainAppController controller.
I want that untill "$rootScope.userData = userDetails;" is not finished , "$scope.userInfo = $rootScope.userData;" must not run.
How can i do this ?
});
There are two ways i know you can do this.
1 way ::
Use $rootScope.userData directly in html and when your parent controller will run $rootScope.userData will get updated and by dual binding feature of angular, your view will get updated accordingly
Code::
//angularjs Code
var app = angular.module('MyApp', []);
app.controller('MainController', function($rootScope, $timeout) {
$rootScope.userData = {};
$timeout(function() {
$rootScope.userData = {name:'myName'};
}, 3000);
});
app.controller('childController', function() {
});
//html code
<div ng-app="MyApp" ng-controller="MainController">
<div ng-controller="childController">
{{userData.name}}
</div>
</div>
2 way::
You can attach $watch on $rootScope.userData in childController so when $rootScope.userData will get updated $watch will run and inside $watch you can update your $scope variable
Code::
//Angularjs Code
var app = angular.module('MyApp', []);
app.controller('MainController', function($rootScope, $timeout) {
$rootScope.userData = {};
$timeout(function() {
$rootScope.userData = {name:'myName'};
}, 2000);
});
app.controller('childController', function($scope, $rootScope) {
$scope.$watch(function(){
return $rootScope.userData;
}, function(){
$scope.userInfo = $rootScope.userData;
});
});
//HTML Code
<div ng-app="MyApp" ng-controller="MainController">
<div ng-controller="childController">
{{userInfo.name}}
</div>
</div>

Resources