ng-table going to first page when data changes - angularjs

I'm working on a meteor app that uses angular-meteor and ng-table. The dataset of the ng-table is from a collection. You can add rows to the table dynamically via a modal form. The dataset is reactive and changes the view once there's a change in the collection. I'm watching for status changes of the item (QUEUED, PROCESSING, CREATED).
Thing is, if the table is on page 2 and the status changes (any change in the dataset), the table is redrawn and goes back to page 2. Is there a way to make it stay on the current page even with dataset changes?
Here's a sample of what I have so far:
(function () {
'use strict';
function myController($scope, $reactive, ngTableParams, ngTableEventsChannel, myService) {
$reactive(this).attach($scope);
this.subscribe('myCollection');
this.currentPage = 1;
this.helpers({
tableParams: () => {
return new ngTableParams({
page: this.currentPage
}, {
//this is just a simple collection call
dataset: myService.getItems().fetch()
});
}
});
$scope.$watch('vm.tableParams', (params) => {
//not triggered :(
ngTableEventsChannel.onDatasetChanged(() => {
console.log('data changed');
tableParams.page($scope.currentPage);
}, $scope, tableParams);
ngTableEventsChannel.onPagesChanged(() => {
$scope.currentPage = tableParams.page();
}, $scope, tableParams);
});
}
angular.module('MyApp').controller('MyController', myController);
})();
The onDatasetChanged event isn't being triggered. I was going to change the page there but I'm not sure if that's a good idea anyway.

Related

Implementing notification alerts in angularjs

I was wondering how an error alert would be implemented using angularjs.
Required functionality:
An alertQueue consists of all the alerts to be displayed to the user. These alerts are deleted from the queue after a span of 3 seconds. The user himself can close the alert by clicking the close button.
This AlertService must be the core service. Alerts are rendered in the view as <alert-list></alert-list>i.e using a component alertList.
Should be able to update alerts from other controllers like: AlertService.alert("my alert").
so far what I have done?
angular.
module('core').
factory('AlertService', [function() {
var alertQueue = [];
var addAlert = function(message, type){
message = {message: message, type: type};
alertQueue.push(message)
};
var deleteAlert = function(alert){
alertQueue.splice(alertQueue.indexOf(alert), 1);
};
return{
warning: function(msg){
addAlert(msg, "warning");
},
success: function(msg){
addAlert(msg, "success");
},
removeAlert: function(alert){
deleteAlert(alert);
},
getAlerts: function(){
return alertQueue;
}
}
}]);
angular.
module('alertApp').
component('alertList', {
templateUrl: '/static/js/app/aurora-alert/aurora-alert.template.html',
controller: ['$routeParams','$scope', 'Aurora',
function AlertController($routeParams, $scope, AlertService) {
var self = this;
self.alertQueue = AlertService.alertQueue;
self.alert = function(){
var message = arguments[0];
AlertService.warning(message);
};
self.removeAlert = function(alert) {
AlertService.removeAlert(alert);
};
}
]
});
I know that I'm doing something wrong in the above code and in its logic. I said above that I require the <alert-list></alert-list> component. So the alertService is injected as a dependency into alertController. But how am I going to raise the alert from other controllers? I know we can use $scope.$broadcast but that doesn't feel right.
Please explain how to achieve this? No third party libraries are to be used.
I think you are going about it only slightly incorrectly. Your alert-list should be responsible only for displaying and removing alerts, not for creating them. Leave the creation of alerts to your controllers
So for example, if you run into an error with an ApiSerivce:
DemoCtrl(AlertService, ApiService) {
ApiService.submitForm({some:data}).then(function() {
//something successfull happened
}).catch(function(error) {
AlertService.warning("Something bad happened calling the API serivce");
});
}
Then you can change your AlertService to broadcast an event when a new alert is created that the alert-list can listen to:
factory('AlertService', ["$rootScope", function($rootScope) {
var alertQueue = [];
var addAlert = function(message, type){
message = {message: message, type: type};
alertQueue.push(message)
$rootScope.$broadcast("new-alert"); //notify the list that there are new alerts
};
This is how you would listen to it in your alert-list:
$scope.$on("new-alert", function() {
self.alertQueue = AlertService.alertQueue;
});
This way, as soon as an alert is created, the alert-list is instantly updated with the latest queue of alerts.
You would probably want to do the same thing for alert deletion.

how to refresh data in cache on state reload- angular 1.5 / ui-router

I am working on a task where I have a bunch of widgets on a dashboard. When the user changes the customer on the dashboard the $state is reloaded, and the widgets' position should be saved to a cache, but the data relevant to the widgets should be refreshed. There are two relevant components, a header component where the customer is changed, and the dashboard component where the widgets are. I'd like to accomplish this without touching the header component.
Previously, I wrote a function to remove all data related properties from the cache after saving in the cache, but I think that might not be the right approach and too complicated. what is the best way to accomplish this?
so suppose each widget looks like this:
{
widgetType: chart,
position: someCoordinatesThatStayTheSameOnStateChange,
chart: containsDataThatNeedsToBeRefreshed,
chartData: containsDataThatNeedsToBeRefreshed
}
this is a $resolve'd function in my routes file which exposes $ctrl.widgets in the dashboard controller:
widgets($http, $q, CacheFactory, WidgetsService) {
'ngInject';
let mockWidgetData = WidgetsService.getAllWidgets();
let widgetsCache = CacheFactory.get('widgets');
if (!widgetsCache) {
CacheFactory.createCache('widgets', {
storageMode: 'localStorage',
});
}
return mockWidgetData;
},
relevant places where I save data in a cache in the dashboard controller:
let widgetsCache = CacheFactory.get('widgets');
let widgetsCacheItems = widgetsCache.get('widgets');
gridsterConfig.resizable.stop = function(event, $element, widget) {
widgetsCache.put("widgets", $ctrl.widgets);
//clearDataFromCache()
}
gridsterConfig.draggable.stop = function(event, $element, widget) {
widgetsCache.put("widgets", $ctrl.widgets);
//clearDataFromCache()
}
$ctrl.toggleVisibility = function(widget) {
widget.hidden = !widget.hidden;
widgetsCache.put("widgets", $ctrl.widgets);
//clearDataFromCache()
}
old function I wrote:
function clearDataFromCache() {
let widgetObjectsInCache = widgetsCache.get('widgets');
widgetObjectsInCache.forEach((widget) => {
if (widget.chart) delete widget.chart;
if (widget.chartConfig) delete widget.chartConfig;
})
console.log(widgetObjectsInCache, "THE CACHE AFTER REMOVING ANY DATA RELATED STUFF");
}
You can use resolve function for state to refetch the data you want. Please check https://medium.com/opinionated-angularjs/advanced-routing-and-resolves-a2fcbf874a1c?swoff=true#.bkudisx52

Triggering an Angular $broadcast from a factory

I'm running a Dashboard system, where widgets on a page will render some grids and charts. I'm now setting up something we call "Widget Linking", where clicking on a grid will go off to a chart widget and display the data related to the grid row I just clicked on.
Example: "linking a treelist (parent widget) to a bar chart (child widget) "
In this example, I have Kendo treelist select event, which will pass the currently-selected row off to my widgetLinkingFactory factor. This factory should iterate through all available widgets on my page to see if any charts (child widgets) are linked to this treelist (parent widget).
I'd like to setup an Angular watcher, then trigger a $broadcast in my treelist select event. However, I keep running into this issue where I cannot inject $scope into a service or factory.
Factory code with treelist select event (see dataModelOptions.change):
'use strict';
angular.module('app')
.factory('TreeListDataModel', function (WidgetDataModel, widgetLinkingFactory) {
function TreeListDataModel() {
}
TreeListDataModel.prototype = Object.create(WidgetDataModel.prototype);
TreeListDataModel.prototype.constructor = WidgetDataModel;
angular.extend(TreeListDataModel.prototype, {
init: function () {
// Kendo datasource object is initialized here...
// KENDO TREELIST SELECT EVENT !!
this.dataModelOptions.change = function (e) {
var row = this.dataItem(this.select());
var parentObj = _.find(this.dataItems(), { id: row.parentId });
var dataModelOptions = {
row: row,
parentObj: parentObj,
dimensions: this.options.dimensions,
fieldTypes: this.options.fieldTypes
};
// CANNOT DO THIS HERE - $SCOPE NOT AVAILABLE !!!
$scope.$broadcast('refreshLinkedWidgets', dataModelOptions);
widgetLinkingFactory.linkCharts(row, parentObj, this.options.dimensions, this.options.fieldTypes);
},
this.updateScope(this.dataModelOptions);
},
updateScope: function (data) {
this.dataModelOptions = data;
},
destroy: function () {
WidgetDataModel.prototype.destroy.call(this);
}
});
return TreeListDataModel;
});
And in my main directive code, I have no problem setting up the watcher:
scope.$on('refreshLinkedWidgets', function (event, dataModelOptions) {
// call my widgetLinkingFactory update code
});
I'm experimenting with some ideas, so advice is appreciated.
regards,
Bob
Factory/service don't have scope. You can't do it on scope
But you can do it via injecting rooScope in your service.
Like this
Declare a method inside TreeListDataModel named refreshLinkedWidgets
then
$rootScope.$on("refreshLinkedWidgets", TreeListDataModel .refreshLinkedWidgets);
Sample code
angular.module('app')
.factory('TreeListDataModel', function (WidgetDataModel, widgetLinkingFactory,$rootScope) {
var TreeListDataModel={
refreshLinkedWidgets:refreshLinkedWidgets
};
function refreshLinkedWidgets(e,a){
// some code
}
$rootScope.$on("refreshLinkedWidgets", TreeListDataModel .refreshLinkedWidgets);
return TreeListDataModel;
}
Inject $rootScope and $broadcast from it.

AngularJS Data sharing between two controllers

I have a issue in sharing data between 2 controllers and 2 views. I have 2 views. I created 2 separate controllers and bind with 2 different views. Now I have 2 share data between 2 controllers so I created a service. Issues is one controller get data from remote source and other controller is consuming that data. But the view that consumes data loads first, so pulled data from remote source is not exactly utilize by first one. Eg.
//My Services
as.service('songGenreService', function () {
var genreList = [];
var addGenres = function (newObj) {
genreList = newObj;
};
var getGenres = function () {
return genreList;
};
return {
addGenres: addGenres,
getGenres: getGenres
};
});
as.controller('SongListController', ['$scope', 'Song', "$stateParams", 'Category', 'Album', 'base64', 'langConversion', 'CONFIG', 'Poet', "songGenreService",
function ($scope, Song, $stateParams, Category, Album, base64, langConversion, CONFIG, Poet, songGenreService) {
$scope.getCategories = function () {
Category.find({
filter: {
fields: ["id", "name"],
where: {
parentId: 2
}
}
}).$promise.then(function (categories) {
$scope.categories = categories;// Here I am giving data to other source.
songGenreService.addGenres(categories);
$scope.genreId = $scope.categories[0].id;
$scope.genreName = $scope.categories[0].name;
});
}();
}
]);
as.controller('SongGenreController', ['$scope', 'Song', "songGenreService",
function ($scope, Song, songGenreService) {
$scope.categories = songGenreService.getGenres();
console.log($scope.categories);
}
]);
Issue is "SongGenreController" loads first because of HTML as it loads first. I wish to populate it when data loads successfully. "songGenreService.getGenres();" doesn't run with remote source.
The way I fixed a similar issue is by using a publish subscribe mechanism.
In your service you can put a publish when genres are added like so:
var addGenres = function (newObj) {
genreList = newObj;
$rootScope.$broadcast('genresUpdated, genreList);
};
then in your two controllers you subscribe to the event :
$scope.$on('genresUpdated', function(event, genreList){
$scope.genres = genreList;
// and other code you want to have triggered when the genreList changes
});

Getting the array value of angular ng-repeat

I have a list that looks like this:
Store 1
Section A
Section B
and so on...
Store 2
Section A
and so on...
So I open a modal window to create a new store. I return the store when I close the window, and so far that's working great!
<div ng-repeat="store in global.user.company2.store">
I need to push the callback store to $scope.global.user.company2.store[?]
modalInstance.result.then(function (newStore) {
// How do I get the IndexOf value of store?
// This is one of the stores in the ng-repeat
$scope.global.user.company2.store[???].section.push(newStore)
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
The way I'm sending a selected store into the modal is with resolve
$scope.createSection = function (size) {
var modalInstance = $modal.open({
templateUrl: 'createSection.html',
controller: 'SectionModal',
size: size,
resolve: {
items: function () {
// $scope.radio.model === store
// $scope.radio.model2 === section
return $scope.radio;
}
}
});
UPDATE: Here's a basic plunker http://plnkr.co/edit/UGN4niAO9nQETqhg8lxn
The radio model buttons aren't working. If you change resolve items to $scope.radio.model, the modal breaks. So I left it as is. I think maybe it has to do with the btn-radio being part of angular-ui-bootstrap?
When resolving your modal, box your returned object with the array index of the object.
For example:
$modalInstance.close({ radio: $scope.radio, index: $scope.items.indexOf($scope.radio) } );
Then, when resolving your modal's promise, simply unbox your object:
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem.radio;
$scope.selectedIndex = selectedItem.index;
}, function () {});
See the Angular-Bootstrap docs for more details.

Resources