AngularJS: Append to url the page number while paginating - angularjs

I am working on a application where i am paginating through some records by making calls to the server like random/api/endpoint?page=1/2/3
Now i while i paginate,
i need to append the page i am requesting to the url like http://www.paginate.com/somedata/{1/2/3} and on opening this url it should also fetch that specific page in the view {meaning if i navigate to hhtp://www.paginate.com/somedata/4 then the app/view should reflect data from the api call random/api/endpoint?page=4}.
Presently i am using angular-route 1.4.12 with the same version of AngularJS. Very new to angular (2 days), any help will be greatly appreciated.
EDIT : What i want to do ?
When i click next while paginating, it should append the pageNumber to the url.
route.js
angular
.module('mainRouter', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/somedata/:page', {
templateUrl: 'partials/somedata.html',
controller: 'PaginationCtrl',
controllerAs: 'vm',
reloadOnSearch: false
}).
otherwise( { redirectTo: "/somedata/1" });
}
]);
PaginationCtrl.js
angular
.module('controllers.Pagination', [])
.controller('PaginationCtrl', PaginationCtrl);
PaginationCtrl.$inject = ['$routeParams', 'paginationService'];
function PaginationCtrl ($routeParams, paginationService) {
var vm = this;
vm.paginationData = {
perPage: 10,
currentPage: 1
};
vm.isLoading = false;
vm.paginate = paginate;
paginate(vm.paginationData.currentPage);
calculateTotalPages();
function calculateTotalPages () {
paginationService.findAll(0, 0)
.success(function (res) {
var paginationData = vm.paginationData || {};
paginationData.totalPages = Math.ceil(res.count / paginationData.perPage);
})
.error(function (res) {
console.log('Error trying to get the total number of pages', res);
});
}
function paginate (pageNumber, perPage) {
vm.isLoading = true;
var paginationData = vm.paginationData || {};
if (! perPage) {
perPage = paginationData.perPage;
}
console.log($routeParams);
paginationService.findAll(perPage, pageNumber)
.success(function (res) {
paginationData.items = res.documents;
vm.isLoading = false;
})
.error(function (res) {
console.log('Error fetching more Logs', res);
});
}
}
PaginationService.js
angular
.module('services.Pagination', [])
.service('paginationService', PaginationService);
PaginationService.$inject = ['$http', 'Constants'];
function PaginationService ($http, Constants) {
// console.log($http);
this.findAll = function (perPage, page) {
var url = Constants.baseUrl + '/sms/get/data';
if (page > 0) {
url += '?page=' + page;
}
return $http.get(url);
};
}
directive being used
var app = angular.module('directives.Pagination', []);
app.directive('pagination', [function () {
return {
restrict: 'E',
template: '<div class="ui pagination menu"> \
<a class="icon item" ng-click="vm.previous()"><i class="left arrow icon"></i></a> \
<div class="icon item">{{ vm.paginationData.currentPage }} / {{ vm.paginationData.totalPages }}</div> \
<a class="icon item" ng-click="vm.next()"><i class="right arrow icon"></i></a> \
</div>',
scope: '=',
link: function (scope, element, attrs) {
var vm = scope.vm;
vm.paginationData.currentPage = 1;
vm.next = function () {
vm.paginationData.currentPage++;
if (vm.paginationData.currentPage > vm.paginationData.totalPages) {
vm.paginationData.currentPage = vm.paginationData.totalPages;
}
vm.paginate(vm.paginationData.currentPage);
};
vm.previous = function () {
vm.paginationData.currentPage--;
if (vm.paginationData.currentPage < 1) {
vm.paginationData.currentPage = 1;
}
vm.paginate(vm.paginationData.currentPage);
};
}
};
}]);

You should be able to access your :page parameter via $routeParams, which you've already injected in your controller.
Just call paginate with $routeParams.page instead of your default of 1.
In order to update the url as you go (in such a way that allows the user to copy the url for later use), without updating the route and re-initializing the controller, you can just call $location.search({page: page}). When this is called with reloadOnSearch set to false (as you've already done) it shouldn't re-initalize the controller.
Lastly, in case its not clear, you'll have to update the URL at the same time you make your API call. There isn't a built in angular way to do this, but it should be pretty straightforward.

Related

How to trigger mailto in AngularJS controller without changing window location

I have an angular controller that triggers a "mailto" link when a mail icon is clicked
angular
.module('app')
.component('header', {
template: '<i class="fa fa-envelope" aria-hidden="true"></i>',
controller: HeaderController,
controllerAs: 'headerCtrl'
});
function HeaderController() {
var vm = this;
vm.sendMail = sendMail;
function sendMail(response) {
var email = 'test#mail.com';
var subject = 'email%20subject';
var body = 'email%20body';
vm.mailToUri = 'mailto:' + email + '?subject=' + subject + '&body=' + body;
$window.location = vm.mailToUri;
}
}
Unfortunately, this is running in the same application as a directive that calls a logout url when the browser is closed
angular
.module('app')
.directive('onCloseLogout', onCloseLogout);
function onCloseLogout($http, $window) {
return {
restrict: 'AE',
link: function () {
$window.onbeforeunload = function () {
$http.get('/logout');
};
}
};
}
So, when the email link is clicked, the onCloseLogout directive is triggered, and the user is logged out.
Is there another way to open an email in angular, or to send a get request to the logout URL on browser close?
Turns out I had over engineered the solution. I added the mailToUri to the anchor tag as the ng-href attribute and it all works fine now
angular
.module('app')
.component('header', {
template: '<a ng-href="{{headerCtrl.mailToUri}}"><i class="fa fa-envelope" aria-hidden="true"></i></a>',
controller: HeaderController,
controllerAs: 'headerCtrl'
});
function HeaderController() {
var vm = this;
var email = 'test#mail.com';
var subject = 'email%20subject';
var body = 'email%20body';
vm.mailToUri = 'mailto:' + email + '?subject=' + subject + '&body=' + body;
}

AngularJS: after-select-item not triggering

I am using angularjs version 1.6.4 with angular-multiple-select module for multi selecting. Every thing is working fine. I am able to select from suggestions but whenever i do selection "after-select-item" directive is not triggering. According to angular-multiple-select module documentation
afterSelectItem : Listen for event before adding an item
<div class="form-group float-label-control">
<label>Skills</label>
<multiple-autocomplete ng-model="model.user.skills"
object-property="name"
after-select-item="model.afterSelectItem"
suggestions-arr="model.skills">
</multiple-autocomplete>
</div>
My controller few code lines:
(function () {
"use strict";
var module = angular.module(__appName);
function fetchSkills($http) {
return $http.get(__apiRoot + "/skills")
.then(function (response) {
return response.data;
})
}
function controller($http) {
var model = this;
model.$onInit = function () {
fetchSkills($http).then(function (skills) {
model.skills = skills;
});
};
model.afterSelectItem = function (item) {
console.log("after select item");
console.log(item);
}
}
module.component("userEdit", {
templateUrl: "components/user-edit/user-edit.template.html",
bindings: {
userId: "<",
onUserSaved: "&"
},
controllerAs: "model",
controller: ["$http", controller]
});
}());

Why my service does not share data between controllers?

I built a factory to get Data from the Database and pass to all controllers in my application like this:
(function () {
angular.module('appContacts')
.factory('dataService', ['$http', dataService]);
function dataService($http) {
return {
getCurrentOrganization: getCurrentOrganization,
};
function getCurrentOrganization(id) {
return $http({
method: 'GET',
url: '/api/organization/' + id + '/contacts'
})
}
}
})();
And I have a view like this:
<div ng-app="myapp">
<div ng-controller="contactController">
<a ui-sref="organization({Id: organization.id})" ng-click="vm.setCurrentOrganization(organization)"> {{organization.organizationName }}</a>
</div>
</div>
That link redirect from a view the view contactsView.html to a detail view organizationDetail.html managed by a second controller:
....
.state("home", {
url: "/",
templateUrl: "views/contactsView.html",
controller: "contactsController",
controllerAs: "vm"
})
.state("organization", {
url: "/organization/:Id",
templateUrl: "views/organizationDetail.html",
params: { Id: null },
controller: "organizationsController",
controllerAs: "vm"
})
...
My problem is that I get the data, I see in the console, but when the new URL comes into place, the Data is gone and the view is shown empty.
How could I use the data produced in the factory in the second Controller?
EDIT:
Here are the Controllers:
//organizationsController.js
(function () {
"use strict";
angular.module('appContacts')
.controller('organizationsController', function organizationsController(dataService) {
var vm = this;
vm.setCurrentOrganization = function (organization) {
vm.theOrganization = organization;
vm.visible = true;
dataService.getCurrentOrganization(vm.theOrganization.id).then(function (result) {
vm.organizationData = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
}
});
})();
And the contactsController:
//contactsController.js
(function () {
"use strict";
angular.module('appContacts')
.controller('contactsController', function contactsController(dataService) {
var vm = this;
vm.visible = false;
activate();
function activate() {
dataService.getAllContacts().then(function (result) {
vm.allcontacts = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
dataService.getAllOrganizations().then(function (result) {
vm.organizations = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
}
});
})();
The problem is that I click the llink in the view A (contactsView.html/ContactsViewController) and I should end in the VIEW B (OrganizationDetails.html/organizationController), using the Data fetch in the service.
You are doing it wrong here
<div ng-app="myapp">
<div ng-controller="contactController">
<a ui-sref="organization({Id: organization.id})" ng-click="vm.setCurrentOrganization(organization)"> {{organization.organizationName }}</a>
</div>
</div>
Your contactController does not have the function setCurrentOrganization. Instead its in another controller. you can remove the code ng-click="vm.setCurrentOrganization(organization)" from the HTML. and read the id using $stateParams in the organizationsController. After getting the id, use it call the service as below:
dataService.getCurrentOrganization(id).then(function (result) {
vm.organizationData = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});

Back Arrow and Angular Routing - Press Back Twice

Angularv1.1.5
Site: http://tilsa.azurewebsites.net
I have a very simple route setup however when the user goes from the default/home route to the detail (pregunta) route and then clicks the back button nothing happens. The 2nd/3rd time the back button is clicked the user returns (chrome) to the default/home route. I'm not sure as to how or why this is happening.
$routeProvider.
when('/', {
templateUrl: '/js/app/partial/index.html',
controller: 'IndexCtrl'
})
.when('/pregunta/:id', {
templateUrl: '/js/app/partial/detalle.html',
controller: 'PreguntaDetalleCtrl'
}).
otherwise({
redirectTo: '/'
});
Here are the two relevant controllers. I've removed some of the code that doesn't seem relevant (polling for new info/etc):
// load the index list of questions, the actual questions are loaded in parent scope
.controller('IndexCtrl', ['$scope', 'services', 'data', '$modal', 'navigation', 'timeFunctions', function ($scope, services, data, $modal, navigation, timeFunctions)
{
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/';
$scope.url = encodeURIComponent($scope.noEncodeUrl);
// controls the back arrow visibility to go back
navigation.setReturn(false);
}])
.controller('PreguntaDetalleCtrl', ['$scope', '$routeParams', 'services', 'navigation', 'graphService', 'stringFx', '$timeout', 'timeFunctions', function ($scope, $routeParams, services, navigation, graphService, stringFx, $timeout, timeFunctions) {
$scope.notas = [];
$scope.comentario = '';
navigation.setReturn(true);
$scope.loadPregunta = function (id, loadComments)
{
services.preguntas.getDetalle(id).then(function (data)
{
$scope.safeApply(function ()
{
$scope.pregunta = data;
graphService.setProp('title', $scope.pregunta.pregunta);
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/pregunta/' + id;
$scope.url = encodeURIComponent($scope.noEncodeUrl);
$scope.preguntaText = stringFx.removeAccent('¿'+$scope.pregunta.pregunta+'?');
});
if (loadComments)
{
$scope.commentTracker = {
defaults: { },
skip: 0,
take: 20
};
$scope.$on('$destroy', function (e)
{
$scope.stopPolling();
});
$scope.startPolling = function ()
{
// scrollTimeout will store the unique ID for the $setInterval instance
return $scope.scrollTimeout = timeFunctions.$setInterval(poll, 10000, $scope);
// Function called on interval with scope available
function poll($scope)
{
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
}
}
$scope.stopPolling = function ()
{
return timeFunctions.$clearInterval($scope.scrollTimeout);
}
$scope.startPolling();
$scope.cargarAnteriores = function ()
{
//$scope.commentTracker.skip++;
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas, true).then(function (data)
{
$scope.safeApply(function ()
{
$scope.notas = $scope.notas.concat(data.notas);
$scope.masNotas = $scope.notas.length > 0;
});
});
}
$scope.cargarAnteriores();
}
});
}
$scope.notaNueva = function () {
//$scope.commentario;
if ($scope.comentario.length < 3)
{
alert('Escribe algo mas, no seas tacano con tus palabras');
return;
}
$scope.processing = true;
services.preguntas.insertNota($scope.pregunta.id, $scope.comentario, $scope.notas, false).then(function (data)
{
$scope.comentario = '';
$scope.processing = false;
$scope.loadPregunta($scope.pregunta.id, false);
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
});
}
$scope.loadPregunta($routeParams.id, true)
$scope.$on('updatedpregunta', function (event, obj)
{
$scope.loadPregunta(obj, false)
});
}]);
I had this issue as well! Turned ut that artur grzesiak was right! I had a iframe on my page that had a binding for its src-attribute.
<iframe src="{{selected.url}}"></iframe>
Since the default value of $scope.selected.url was null the first thing that happened was that it was loading a url called null.
After some research I found that there was a special directive for the iframe:
<iframe ng-src="{{selected.url}}"></iframe>
This change solved my is
It seems that the Angular side of your app is fine.
99% the problem is caused by some external library. For sure there is some problem with this script kVEquaeit4R (it seens to be a facebook plugin), as it fails to load some resource (404 error): The resource you are looking for has been removed, had its name changed, or is temporarily unavailable. and as a consequence a couple of further errors are generated (look at the console). And in turn it prevents the app from calling window.location.hostname.replace what actually is present in the kVEquaeit4R script.
So my suggestion is as follow: remove this fb plugin from your site and check if the routing works properly...

Forcing a ng-src reload

How can I force angularjs to reload an image with an ng-src attribute, when the url of the image has not changed, but its contents has?
<div ng-controller='ctrl'>
<img ng-src="{{urlprofilephoto}}">
</div>
An uploadReplace service that performs a file upload, is replacing the content of the image, but not the url.
app.factory('R4aFact', ['$http', '$q', '$route', '$window', '$rootScope',
function($http, $q, $route, $window, $rootScope) {
return {
uploadReplace: function(imgfile, profileid) {
var xhr = new XMLHttpRequest(),
fd = new FormData(),
d = $q.defer();
fd.append('profileid', profileid);
fd.append('filedata', imgfile);
xhr.onload = function(ev) {
var data = JSON.parse(this.responseText);
$rootScope.$apply(function(){
if (data.status == 'OK') {
d.resolve(data);
} else {
d.reject(data);
}
});
}
xhr.open('post', '/profile/replacePhoto', true)
xhr.send(fd)
return d.promise;
}
}
}]);
When the uploadReplace returns, I don't know how I can force the image to reload
app.controller('ctrl', ['$scope', 'R4aFact', function($scope, R4aFact){
$scope.clickReplace = function() {
R4aFact.uploadReplace($scope.imgfile, $scope.pid).then(function(){
// ?? here I need to force to reload the imgsrc
})
}
}])
An easy workaround is to append a unique timestamp to ng-src to force image reload as follows:
$scope.$apply(function () {
$scope.imageUrl = $scope.imageUrl + '?' + new Date().getTime();
});
or
angular.module('ngSrcDemo', [])
.controller('AppCtrl', ['$scope', function ($scope) {
$scope.app = {
imageUrl: "http://example.com/img.png"
};
var random = (new Date()).toString();
$scope.imageSource = $scope.app.imageUrl + "?cb=" + random;
}]);
Perhaps it could be as simple as adding a decache query string to the image URL? ie.
var imageUrl = 'http://i.imgur.com/SVFyXFX.jpg';
$scope.decachedImageUrl = imageUrl + '?decache=' + Math.random();
This should force it to reload.
An "angular approach" could be creating your own filter to add a random querystring parameter to the image URL.
Something like this:
.filter("randomSrc", function () {
return function (input) {
if (input) {
var sep = input.indexOf("?") != -1 ? "&" : "?";
return input + sep + "r=" + Math.round(Math.random() * 999999);
}
}
})
Then you can use it like this:
<img ng-src="{{yourImageUrl | randomSrc}}" />
Try This
app.controller('ctrl', ['$scope', 'R4aFact', function($scope, R4aFact){
$scope.clickReplace = function() {
R4aFact.uploadReplace($scope.imgfile, $scope.pid).then(function(response){
$scope.urlprofilephoto = response + "?" + new Date().getTime(); //here response is ur image name with path.
});
}
}])
I resorted to make a directive to put random param in the src, but only if the image changes, so I don't mess that much with the caching.
I use it to update the user's profile pic in the navbar when they update it via AJAX, which doesn't happen that often.
(function() {
"use strict";
angular
.module("exampleApp", [])
.directive("eaImgSrc", directiveConstructor);
function directiveConstructor() {
return { link: link };
function link(scope, element, attrs) {
scope.$watch(attrs.eaImgSrc, function(currentSrc, oldSrc) {
if (currentSrc) {
// check currentSrc is not a data url,
// since you can't append a param to that
if (oldSrc && !currentSrc.match(/^data/)) {
setSrc(currentSrc + "?=" + new Date().getTime());
} else {
setSrc(currentSrc);
}
} else {
setSrc(null);
}
})
function setSrc(src) { element[0].src = src; }
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="exampleApp">
<div>
<img ea-img-src="src"></img>
</div>
<button ng-click="src = 'http://placehold.it/100x100/FF0000'">IMG 1</button>
<button ng-click="src = 'http://placehold.it/100x100/0000FF'">IMG 2</button>
<button ng-click="src = 'http://placehold.it/100x100/00FF00'">IMG 3</button>
</div>

Resources