persist Data Between angularjs Controllers - angularjs

I want to recover my data from one controller to another, knowing that I change my view, let me explain I have my roads, for each road I have a controller, I managed to passed the data controller to another but I have to be on the right view. my first check my data recovered via ng-click on a dropdown and he goes to the other controller, the problem that when I'm on the right sight it works but if I'm on another view I select an item and I spend on the right view, nothing happens
var routeAppControllers = angular.module('routeAppControllers', []);
// Sharing Data of venues Between Controllers
routeApp.factory('mySharedService', function ($rootScope) {
var sharedService = {};
sharedService.venues = '';
sharedService.prepForBroadcast = function(venues){
this.venues = venues;
this.broadcastItem();
};
sharedService.broadcastItem = function(){
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
routeAppControllers.controller('GlobalController', ['$scope','$http','mySharedService', function GlobalController($scope, $http, mySharedService)
{
var venuesName = [];
$http.get('/ajax/venuesNameClient').success(function(data){
angular.forEach(data, function(value,key){
venuesName.push({name : value});
});
$scope.venuesName = venuesName
});
$scope.venueName = {};
var venues = [];
$scope.go = function(name) {
$http.get('/ajax/venuesClient').success(function(data){
angular.forEach(data, function(value,key){
if (value != "ok") {
for (var i=0;i<value.name.length;i=i+1)
{
if (value['name'][i] == name)
{
venues = [];
venues.push(value['name'][i]);
venues.push(value['email'][i]);
venues.push(value['address'][i]);
venues.push(value['long_description'][i]);
venues.push(value['creation_date'][i]);
venues.push(value['wifi_network'][i]);
}
}
};
});
mySharedService.prepForBroadcast(venues);
});
}
$scope.$on('handleBroadcast', function() {
$scope.venues = mySharedService.venues;
});
}
]);
// WelcomeController
routeAppControllers.controller('WelcomeController', ['$scope','mySharedService', function WelcomeController($scope, mySharedService)
{
$scope.$on('handleBroadcast', function() {
$scope.venues = mySharedService.venues;
console.log($scope.venues);
});
}
]);
<ui-select ng-model="venueName.selected" theme="select2" ng-disabled="disabled" style="min-width: 300px; padding-top:10px;">
<ui-select-match placeholder="Select a venue in the list ...">{{$select.selected.name}}</ui-select-match>
<ui-select-choices repeat="venueName in venuesName | propsFilter: {name: $select.search}">
<div ng-click='go(venueName.name)'>{{venueName.name}}</div>
</ui-select-choices>
</ui-select>
any help please, this code work but i have to be in the right view

The Angular way to store and pass data between controllers and directives is to use services. As the services are singletons the store the data and can be injected into both directives and controllers.

Related

Sharing scope data in controller

My spring mvc controller returns an object.
My scenario is:
On click of a button from one page say sample1.html load a new page say sample2.html in the form of a table.
In sample1.html with button1 and controller1--> after clicking button1-->I have the object(lets say I got it from backend) obtained in controller1.
But the same object should be used to display a table in sample2.html
How can we use this object which is in controller1 in sample2.html?
You can use a service to store the data, and inject it in your controllers. Then, when the value is updated, you can use a broadcast event to share it.
Here is a few example:
HTML view
<div ng-controller="ControllerOne">
CtrlOne <input ng-model="message">
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerTwo">
CtrlTwo <input ng-model="message">
</div>
Controllers
function ControllerOne($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
Service
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
JSFiddle demo
you can use factory to share data between controllers
<div ng-controller="CtrlOne">
<button ng-click="submit()">submit</button>
</div>
<div ng-controller="CtrlTwo">
{{obj}}
</div>
.controller('CtrlOne', function($scope, sampleFactory) {
$scope.sampleObj = {
'name': 'riz'
}; //object u get from the backend
$scope.submit = function() {
sampleFactory.setObj($scope.sampleObj);
}
})
.controller('CtrlTwo', function($scope, sampleFactory) {
$scope.obj = sampleFactory.getObj();
})
.factory('sampleFactory', function() {
var obj = {};
return {
setObj: function(_obj) {
obj = _obj;
},
getObj: function() {
return obj;
}
}
})

Using objects in child controller from parent controller in Angular

I have a Parent Angular Controller that has a method that needs to be shared with some other controller. The parent controller looks like this:
"use strict";
(function() {
var ParentCtrl = function($scope, atomico, Asset) {
var _this = this;
_this.busy = atomico.identity == null;
_this.oldestTimestamp = null;
_this.assets = [];
/**
* Infinite scrolling, fetches more assets when the user scrolls down.
*/
_this.fetch = function() {
if (_this.noMoreAssets) { return; }
_this.busy = true;
Asset.all(atomico.metadata['campaign'].id, _this.oldestTimestamp, $scope.dates.start, $scope.dates.end, function(assets) {
_this.busy = false;
if (assets.length > 0) {
_this.assets = _this.assets.concat(assets);
_this.oldestTimestamp = moment(assets[assets.length - 1].start).unix();
} else {
_this.noMoreAssets = true;
}
});
};
};
ParentCtrl.$inject = [ '$scope', 'atomico', 'Asset' ];
angular.module('myModule').controller('ParentCtrl', ParentCtrl);
})();
I am extending this controller in another one to have infinite scrolling to work in a view. This is the child controller:
"use strict";
(function() {
var ChildCtrl = function(atomico, userState, $controller, $scope) {
var _this = this;
angular.extend(_this, $controller('ParentCtrl', {$scope: $scope}));
// Fetch assets after user, campaign and account data is available.
atomico.ready(function(){
var dates = userState.getCampaignViewData(atomico.metadata['campaign'].id).list_view;
$scope.dates = _.isEmpty(dates) ? {start: moment(), end: moment()} : dates;
_this.busy = false;
});
};
CampaignListCtrl.$inject = [ 'atomico', 'userState', '$controller', '$scope' ];
angular.module('myModule').controller('ChildCtrl', ChildCtrl);
})();
And in my view i have this:
<div id='agenda_viewer' ng-controller="ChildCtrl as ctrl">
<p class="at-text-center at-block-center c-empty-list" ng-hide='ctrl.assets.length || ctrl.busy'>
There are no assets to show for this day
</p>
<div class="agenda-flight__content at-row" infinite-scroll='ctrl.fetch()' infinite-scroll-disabled='ctrl.busy' infinite-scroll-parent="true">
<div class="agenda-flight at-row agenda-asset__live" ng-repeat='asset in ctrl.assets' ng-init='asset.collapsed = false'>
<directive-list-row asset='asset'></directive-list-row>
<directive-list-expanded asset='asset' ng-if='asset.collapsed'></directive-list-expanded>
</div>
</div>
<div class='c-loading' ng-show='ctrl.busy'>Loading data...</div>
</div>
The problem i am having is that the ctrl.assets is always empty even thought the service returns the data. Is this an issues with ctrl.assets being defined in the parent controller and not visible in the child controller? How can i make that assets object shared to the child controller so i can see the data in the UI?
What I ended up doing is moving some of this controller variables into the $scope and now seems to be working good. $scope is being shared across children

Angular binding not working properly with ngInfiniteScroll

Basically I have a timeline with posts that is a $firebaseArray and any change to this array is getting binded properly. But when I want to bind any other data it only binds when ngInfiniteScroll is trying to retrieve more data from firebase, so only when I scroll down.
In the code bellow I'm calling {{getMoreDetails()}} and this data is binded when the first set of data is being retrieved with ngInfiniteScroll but as soon as it is loaded the bind breaks and only binds again when scrolling.
My concerns here are:
Was ngInfiniteScroll designed to work this way?
Is there any workaround in this scenario?
Stack:
"firebase": "2.4.2","angularfire": "~1.2.0","firebase-util": "0.2.5","ngInfiniteScroll": "1.2.2"
timeline.html
<div ng-controller="TimelineController">
<section class="entrys main-content" infinite-scroll="posts.scroll.next(3)" infinite-scroll-distance="0.3">
<div class="inner">
<div ng-repeat="post in filteredPostsResults = (posts | filter:postIdFilter)">
<article class="entry">
<img ng-if="post.sourceType=='IMAGE'" data-ng-src="{{getPostData(post)}}"/>
<div class="entry-info">
<h3><div ng-bind-html="post.description | emoticons"></div></h3>
<small>posted on <time>{{getDateInFormat(post.createdAt)}}</time></small>
{{getMoreDetails()}}
</div>
</article>
</div>
</div>
</section>
</div>
timeline.js
(function (angular) {
"use strict";
var timeline = angular.module('myApp.user.timeline', ['firebase', 'firebase.utils', 'firebase.auth', 'ngRoute', 'myApp.user.timelineService']);
timeline.controller('TimelineController', [ '$scope', '$routeParams', 'TimelineService', '$publisherServices', '$securityProperties', function ($scope, $routeParams, TimelineService, $publisherServices, $securityProperties) {
if (!$scope.posts){
$scope.posts = TimelineService.getPosts($routeParams.userId);
}
$scope.posts.$loaded(function(result) {
$scope.isPostsLoaded = true;
});
$scope.getMoreDetails = function() {
console.log("LOGGED ONLY WHEN SCROLLING");
return $publisherServices.getDetails();
};
$scope.getPostData = function(post) {
if (!post.dataUrl){
post.dataUrl = $publisherServices.getAwsFileUrl(post.fileName);
}
return post.dataUrl;
};
$scope.postIdFilter = function(post) {
if ($routeParams.postId){
if (post.$id == $routeParams.postId) return post;
} else { return post; }
};
$scope.getDateInFormat = function(timestamp){
var date = new Date();
date.setTime(timestamp);
return date;
};
}]);
})(angular);
timelineService.js
(function (angular) {
"use strict";
var timelineService = angular.module('myApp.user.timelineService', []);
timelineService.service('TimelineService', ['$routeParams', 'FBURL', '$firebaseArray', function ($routeParams, FBURL, $firebaseArray) {
var posts;
var currentUserIdPosts;
var postsRef;
var self = {
getPosts: function(userId){
if (!posts || userId != currentUserIdPosts){
currentUserIdPosts = userId;
postsRef = new Firebase(FBURL).child("posts").child(userId);
var scrollRef = new Firebase.util.Scroll(postsRef, "createdAtDesc");
posts = $firebaseArray(scrollRef);
posts.scroll = scrollRef.scroll;
}
return posts;
}
}
return self;
}]);
})(angular);
I am assuming that you want the post details updated when the data from your Firebase changes.
When Firebase changes are applied to your scope, it seems that it doesn't trigger a digest cycle, so you probably need to do it manually every time you get updates from Firebase.
Take a look at $$updated in $firebaseArray.$extend (see docs).
// now let's create a synchronized array factory that uses our Widget
app.factory("WidgetFactory", function($firebaseArray, Widget) {
return $firebaseArray.$extend({
// override the update behavior to call Widget.update()
$$updated: function(snap) {
// we need to return true/false here or $watch listeners will not get triggered
// luckily, our Widget.prototype.update() method already returns a boolean if
// anything has changed
return this.$getRecord(snap.key()).update(snap);
}
});
});
I hope this helps.

ng-select with lazy population

It's an edit form with parent record first populated then dependent select list is populated, and then it's expected the value from parent record pre-select the combo box.
html
<select ng-model="data.trackId" >
<option ng-repeat="track in tracks" value="{{track.id}}">{{track.name}}</option>
initial result once parent record is pulled.
if(data) {
this.$scope.data.id = data.id;
this.$scope.data.name = data.name;
this.$scope.data.room = data.room;
this.$scope.data.start = data.start;
this.$scope.data.end = data.end;
this.$scope.data.dayId = data.day_id;
this.$scope.data.trackId = data.track_id;
this.$scope.data.color = data.color;
this.$scope.data.description = data.description;
this.$scope.$apply();
this.$element[0].removeAttribute("style");
}
//later track results were pulled
trackResult: function(data, status, headers, config) {
for(var i=0; i<data.length; i++) {
this.$scope.tracks.push(data[i]);
}
this.$scope.$apply();
},
Problem:
List gets populated from the second call trackResult but default value from the $scope.trackId never sets the combo box to a value.
Edit: Controller Body
controller: function($scope, $element) {
var self = this;
this.$scope = $scope;
this.$element = $element;
this.$scope.data = {};
this.$scope.days = [];
this.$scope.tracks = [];
this.$scope.submit = function() {self.submit()};
this.$scope.cancel = function() {self.cancel()};
},
Edit : Updated with setting the data from outside the scope (OP request)
Use ng-options & ng-model
this is how i think it should be done in angularjs.
use the built in databinding capabilities to simplify your code and make it less complicated
for binding a list into a <select> and controlling the selected item, this snippet below should do the trick.
http://jsfiddle.net/72em40j4/
js
var myapp = angular.module('myapp', []);
myapp.controller('Ctrl', function ($scope) {
$scope.options = [];
$scope.selectedOption = null;
});
html
<script>
function clickFromOutside() {
var controllerElement = document.getElementById('container');
var controllerScope = angular.element(controllerElement).scope();
var firstTrack = {
id: 1,
first: 'First',
last: 'Track'
};
var secondTrack = {
id: 2,
first: 'Second',
last: 'Track'
};
controllerScope.options.push(firstTrack);
controllerScope.options.push(secondTrack);
controllerScope.selectedOption = secondTrack;
controllerScope.$apply();
}
</script>
<button onclick="clickFromOutside();">outside</button>
<div ng-app="myapp">
<fieldset id="container" ng-controller="Ctrl">
<select ng-options="p.first + ' ' + p.last for p in options" ng-model="selectedOption"></select> <pre>{{ selectedOption }}</pre>
</fieldset>
</div>

AngularJS - RestAngular - after post add new category, update category list in other controller

what is the best way to update a list of categories (in a nav for example) after adding a category with a different controller?
Here is my code
// add new category
app.controller('AddCategoryController', ['$scope', 'CategoryService', function($scope, CategoryService) {
$scope.category = {};
$scope.added = false;
$scope.addCategory = function() {
CategoryService.addCategory($scope.category).then(function(response) {
if(response == 'ok') {
$scope.added = true;
}
});
};
}]);
and here is the controller for showing the categories
app.controller('CategoriesController', ['$scope', 'CategoryService', function($scope, CategoryService) {
CategoryService.getCategories().then(function(categories) {
$scope.categories = categories;
});
}]);
Categories are shown in a nav
<nav>
<div class="list-group" ng-controller="CategoriesController">
<a ng-href="#category/{{category.id}}" class="list-group-item" ng-repeat="category in categories" ng-bind="category.name"></a>
</div>
<div class="list-group">
Add category
</div>
</nav>
EDIT This is the service
services.factory('CategoryService', ['$route', 'Restangular', function($route, Restangular) {
var Category = Restangular.all('categories');
return {
getCategories: function() {
return Category.getList();
},
getCategory: function(id) {
id = id ? id : $route.current.params.categoryId;
return Category.get(id);
},
addCategory: function(category) {
return Category.post(category);
},
editCategory: function(category) {
return category.put()
},
removeCategory: function(id) {
id = id ? id : $route.current.params.categoryId;
return Category.remove(id);
}
};
}]);
Services are singleton in AngularJS. Therefore, after you called CategoryService.addCategory you can update the category list in your service and it will be available for other controllers.
You can also enrich your service to cache the categories. This will help you to avoid unnecessary requests to your backend.
Either you build your own caching logic or use:
RestangularProvider.setDefaultHttpFields({cache: true});
In addition you can use $rootScope.$on and $rootScope.$emit to receive and send events. This helps you to communicate between components in real-time fashion.
// send event
$rootScope.$emit(nameOfEvent, args...);
In some other controller/ service
// subscription
var unbind = $rootScope.$on(nameOfEvent, function(event, args...) { /* do stuff */ });
// don't forget to unbind
$scope.$on('$destroy', function() {
unbind();
});

Resources