URL from $routeChangeStart route params in angularjs routes - angularjs

How would it be possible to get the URL hash fragment from route params in $routeChangeStart.
$scope.$on('$routeChangeStart', function (event, next, current) {
// trying to get the url hash fragment from <next> param here
// e.g. to_url_function(next) -> '/my_path/1'
});
Receiving the URL hash fragment would be easy using $locationChangeStart but this is not an option for me.

dasboe: I think I'm answering your question.
I have a app with an authentication/authorization check in the $routeChangeStart event handler. If not authenticated, I present user with modal login page. I want a successful login to send them to their original destination (Beauty of $routeChangeStart is that it will run again and check authorization after the successful login). I save the path built from the next in a user session service that is injected into the modal login controller.
here is the event handler
//before each route change, check if the user is logged in
//and authorized to move onto the next route
$rootScope.$on('$routeChangeStart', function (event, next, prev) {
if (next !== undefined) {
if ('data' in next) {
if ('authorizedRoles' in next.data) {
var authorizedRoles = next.data.authorizedRoles;
if (!SessionService.isAuthorized(authorizedRoles)) {
event.preventDefault();
SessionService.setRedirectOnLogin(BuildPathFromRoute(next));
if (SessionService.isLoggedIn()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
}
}
}
}
});
Here is the function that builds the path from the next object
function BuildPathFromRoute(routeObj)
{
var path = routeObj.$$route.originalPath;
for (var property in routeObj.pathParams)
{
if (routeObj.pathParams.hasOwnProperty(property))
{
var regEx = new RegExp(":" + property, "gi");
path = path.replace(regEx, routeObj.pathParams[property].toString());
}
}
return path;
}
Notes:
I'm not keen on my $$route reliance, but I couldn't find any other way to do it. Maybe I missed something easier. I may be inviting trouble in the long term.
The preventDefault() will not work on AngularJS versions before 1.3.7 (see event.preventDefault() not working for routeChangeStart in angularjs app).
Standard caveat: This is all client side and subject to abuse. Make sure authentication/authorization happens server side.
The next Route object (from the event handler) also has a params property. I'm not sure if I should spin through its properties like I do with pathParams.

If you don't want to use hasOwnProperty, you could take advantage of the $$route.keys to get the names of the pathParams fields names:
function getPathFromRoute(routeObj)
{
var path = routeObj.$$route.originalPath;
var keys = routeObj.$$route.keys;
var value;
for (var i = 0; i < keys.length; i++) {
if(angular.isDefined(keys[i]) && angular.isDefined(keys[i].name)){
value = routeObj.pathParams[keys[i].name];
var regEx = new RegExp(":" + keys[i].name, "gi");
path = path.replace(regEx, value.toString());
}
}
return path;
};

Don't use object fields with $$ prefix like in previously given answers, because it's a prefix used by AngularJS for private properties. Use this method for get url from route (not tested):
var buildPathFromRoute = function (route) {
// get original route path
var path = route.originalPath;
// get params keys
var keysLength = route.keys.length;
for (var i=0; i<keysLength; i+=1) {
var param = route.keys[i];
// optional params postfix is '?'
var postfix = param.optional ? '\\?' : '';
var replaceString = ':' + param.name + postfix;
var regex = new RegExp(replaceString, 'g');
var paramValue = route.params[param.name].toString();
// replace param with value
path = path.replace(regex, paramValue);
}
path = path.replace(/\:\S+?\??/g, '');
return path;
};

Related

Load images as a service

I have service that pulls an object from an API. Some of that object may contain image URLs. The backend currently scans for these, and processes them, (in PHP) by get_file_contents() and translating them to inline data. This is heavily loading the throughput on my server. The reason I am doing this is because I want to cache the images for being offline later, but in a way that I can still just use regular angular to render the object.
I can't do the processing in Javascript in the browser with $http.get() because the site hosting the images is blocking the cross-site request. What I thought to do, then, was to create an <IMG> element in the browser, that called the service back once it was loaded so I can extract the data and process the object with it.
I can't control the service worker to store the get from inside the app, and the URL's are not known by the app at any time before it downloads the API object anyway.
I did think about redoing the service worker to store gets from off my site as well, but that seemed a little bit wrong, and I'm not sure how well it would work anyway, plus, while developing, I switch off the service worker as it means I have to let the entire site load twice for it to refresh completely.
Can anyone help me with a way to get image data via the browser into my service?
If I had found a CORS supportive image host in the first place I may not have needed this and could probably have just used the $http call.
A directive, service and controller are required, as well as a host that supports CORS (Imgur for example). I also used this base64 canvas code.
Here is the javascript code:
// Using this from https://stackoverflow.com/questions/934012/get-image-data-in-javascript
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL;
// return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
// Used on the img tag to handle the DOM notification feeding into the service
app.directive('notifyimgsvc', function() {
return {restrict : 'A', link : function(scope, element, attrs) {
element.bind('load', function() {
console.log('imgSvc::notify() image is loaded');
console.log("imgSvc::notify(): " + this.src);
imgSvc.notifyLoad(this.src, getBase64Image(this));
});
element.bind('error', function() {
console.log('imgSvc::notify() image could not be loaded');
console.log("imgSvc::notify(): " + this.src);
});
}};
});
// A core service to handle the comms in both directions from requests to data
app.service('imgSvc', [function(netSvc) {
imgSvc = this; // to avoid ambiguoity in some inner function calls
imgSvc.images = {}; // a cache of images
imgSvc.requests = []; // the requests and their callbacks
imgSvc.handlers = []; // handlers that will render images
console.log("imgSvc::init()");
// Allows a controller to be notified of a request for an image and
// a callback to call when an image is added. There should only ever
// be one of these so an array is probaby not needed and any further
// requests should probably throw an error.
imgSvc.registerHandler = function(callback) {
console.log("imgSvc::registerHandler()");
if (imgSvc.requests.length) {
// Already have image requests, so tell the new handler about them
for ( var i in imgSvc.requests) {
callback(imgSvc.requests[i].url);
}
}
// Add the new handler to the stack
imgSvc.handlers.push(callback);
};
// The usage function from your code, provide a callback to get notified
// of the data when it loads.
imgSvc.getImg = function(url, callback) {
console.log("imgSvc::getImg('" + url + "')");
// If we have pre-cached it, send it back immediately.
if (imgSvc.images[url] != undefined) {
console.log("imgSvc::getImg('" + url + "'): Already have data for this one");
callback(url, imgSvc.images[url]);
return;
}
// push an object into the request queue so we can process returned data later.
// Doing it this way als means you can have multiple requests before any data
// is returned and they all get notified easily just by looping through the array.
var obj = {"url" : url, "callback" : callback};
if (imgSvc.handlers.length) {
console.log("imgSvc::getImg('" + url + "'): informing handler");
for ( var i in imgSvc.handlers) {
imgSvc.handlers[i](obj.url);
}
}
imgSvc.requests.push(obj);
};
// Notification of a successful load (or fail if src == null).
imgSvc.notifyLoad = function(url, src) {
console.log("imgSvc.notifyLoad()");
// Save the data to the cache so any further calls can be handled
// immediately without a request being created.
imgSvc.images[url] = src;
// Go though the requests list and call any callbacks that are registered.
if (imgSvc.requests.length) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): scanning requests");
for (var i = 0; i < imgSvc.requests.length; i++) {
if (imgSvc.requests[i].url == url) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): found request");
// found the request so remove it from the request list and call it
var req = imgSvc.requests.splice(i, 1)[0];
i = i - 1;
console.log("imgSvc.notifyLoadCallback('" + url + "')");
req.callback(url, src);
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): skipping request for '" + imgSvc.requests[i].url + "'");
}
}
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): No requests present??");
}
};
// The notifiy fail is just a logging wrapper around the failure.
imgSvc.notifyFail = function(url) {
console.log("imgSvc.notifyFail()");
imgSvc.notifyLoad(url, null);
};
}]);
// A simple controller to handle the browser loading of images.
// Could probably generate the HTML, but just doing simply here.
app.controller('ImageSvcCtrl', ["$scope", function($scope) {
$scope.images = [];
console.log("imgSvcCtrl::init()");
// Register this handler so as images are pushed to the service,
// this controller can render them using regular angular.
imgSvc.registerHandler(function(url) {
console.log("imgSvcCtrl::addUrlHandler('" + url + "')");
// Only add it if we don't hqve it already. The caching in the
// service will handle multiple request for the same URL, and
// all associated malarkey
if ($scope.images.indexOf(url) == -1) {
$scope.images.push(url);
}
});
}]);
The HTML you need for this is very simple:
<div data-ng-controller="ImageSvcCtrl" style="display:none;">
<img data-ng-repeat="img in images" data-ng-src="{{img}}" alt="loading image" crossorigin="anonymous" notifyimgsvc />
</div>
And you call it within your controllers like this:
var req_url = "https://i.imgur.com/lsRhmIp.jpg";
imgSvc.getImg(req_url, function(url, data) {
if(data) {
logger("MyCtrl.notify('" + url + "')");
} else {
logger("MyCtrl.notifyFailed('" + url + "')");
}
});

How to log all properties from an object in google script

I am trying to get all the properties of array object routes to be logged 1 by 1, however, it's giving undefined error.
function testRoutes () { //Calling the maps API and new direction finder.
var directions = Maps.newDirectionFinder()
.setOrigin('Kharadar, Karachi')
.setDestination('Guru Mandir, Karachi')
.setMode(Maps.DirectionFinder.Mode.DRIVING) //By default driving.
.getDirections();
//We have got all the directions now from origin to destination in test variable.
// var route = directions.routes[0].legs[0].steps[1];
// var route2 = directions.routes[0];
var route = directions.routes;
for (var propt in route) {
Logger.log(route[0].propt);
}
//Logger.log(route[0].summary);
}
If you loop through properties of variable route it should be
for (var propt in route) {
Logger.log(route[propt]);
}

Download a file in Angular Environment

I need to download a file from the server. The file is stored in the database. I have a cs controller that serves a file back to UI. The server GET call looks like this:
http://server/api/controllername/fileid/data
It does work when I run that link in the Browser - the file comes down and goes into the download area (Chrome). But when I send the same command from my Angualar code I dont see any file. The console reports that my request was successful (code 200), but I just dont see anything. Please let me know what code fragments to post to make it easier to help.
Thanks
Create a link to the resource, and don't handle it with ajax.
If you make the link open in a new tab, the tab will automatically close after it realises it was just opened to download a file in most modern browsers.
Try this code:
var a = document.createElement('a');
a.href = "http://server/api/controllername/fileid/data";
a.click();
You can compose the address concatenating variables and text.
The file probably downloads correctly as a byte[] to the calling it but that would be useless to the user - as was my problem.
In my case I needed to download a file with a complex set of parameters. This example JavaScript uses a post request and creates a form (and posts it) with any JavaScript object that you give it. This code may help if you simplified it:
private open(verb, url, data, target)
{
var form = document.createElement("form");
form.action = url;
form.method = verb;
form.target = target || "_self";
if (data) {
this.createFormParameters(form, "", data);
}
form.style.display = 'none';
document.body.appendChild(form);
form.submit();
}
private createFormParameters(form, key, value) {
// recursive algorithm to add parameters (including objects and arrays of objects) to a custom form
if (typeof value === "object") {
for (var item in value) {
if (Array.isArray(value[item])) {
for (var arrayItem in value[item]) {
this.createFormParameters(form, (item + "[" + arrayItem + "]."), value[item][arrayItem]);
}
continue;
}
var input = document.createElement("textarea");
input.name = key + item;
input.value = value[item];
form.appendChild(input);
}
}
else
{
var input = document.createElement("textarea");
input.name = key;
input.value = value;
form.appendChild(input);
}
}

How the react router can handle request url like: localhost:9000/blah/#token=albasdf

We have a requirement that needs to handle some URL like following:
localhost:9000/blah/#token=albasdf
It's weird to use #token=abcd not ?token=abcd, but it's requirement.
Any idea how to handle this?
Use window.location.hash like this:
// window.location.hash = #token=abcd
var match = window.location.hash.match(/\#(.+)=(.+)/)
if (match){
var key = match[1]; // token
var value = match[2]; // abcd
}

Angular - update services object during asynchronous function

Folks: Creating an app in angular and node webkit - where users queue up files for downloading, navigate to their dashboard view and this initiates the downloads.
I've created a service which holds an object of the files data:
..
var downloadObj = {};
// fileObj = {'name':'The file name'; 'download_progress' : dlProgress}
showcaseFactory.myDownloads = function(eventId, fileObj) {
if(eventId){
console.log('update the object');
downloadObj['event_'+eventId] = fileObj;
}
console.log(downloadObj);
};
showcaseFactory.getDownloads = function() {
return downloadObj;
};
..
When the dashboard view loads - ng-repeat loops over $scope.downloadFiles which references this object returning the data.
<div ng-repeat="file in downloadFiles">
<div><span>{{file.name}}</span> [{{file.download_progress}}%]</div>
</div>
I've created a custom module which utilises node_modules to perform the download of the files:
nwjsDownloadFactory.commenceDownload = function(event_id, url, dest, cb) {
var http = require('http');
var fs = require('fs');
var statusBar = require('status-bar');
var path = require('path');
// THIS UPDATES THE OBJECT AND DISPLAYS FINE --------- >>
var id = 7;
var testFileObj = {
'name' : 'This is the file name prior to the download...',
'download_progress' : 10
};
ShowCase.myDownloads(id, testFileObj);
// <<< THIS UPDATES THE OBJECT AND DISPLAYS FINE ---------
var file = fs.createWriteStream(dest);
var request = http.get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(cb); // close() is async, call cb after close completes.
});
bar = statusBar.create({ total: response.headers['content-length'] })
.on('render', function (stats) {
// var percentage = this.format.percentage(stats.percentage);
// console.log(event_id + '....' + percentage);
var id = 7;
var testFileObj = {
'name' : 'This is the new file name during the download...',
'download_progress' : 35 // this will be replaced with percentage
};
ShowCase.myDownloads(id, testFileObj);
});
response.pipe(bar);
}).on('error', function(err) { // Handle errors
fs.unlink(dest); // Delete the file async. (But we don't check the result)
if (cb) cb(err.message);
});
}
QUESTION: Prior to the line var request = http.get(url, function(response) the object gets updated, and the changes are reflected in the UI. However, I need to constantly update the object with download complete % so I can create a progress bar.. However, as this asynchronous function executes, the object
appears to be updating - see the attached screen shot - but the UI is not reflecting this.
Can somebody please steer me in the right direction - I need the object to update during the function bar = statusBar.create({ and for the changes to reflect in the UI..
Call $scope.$apply() after making changes to your model to notify Angular that it has to update the UI.
showcaseFactory.myDownloads = function(eventId, fileObj) {
if(eventId){
console.log('update the object');
downloadObj['event_'+eventId] = fileObj;
$scope.$apply();
}
console.log(downloadObj);
};
If you use Angular's $http object, this is handled automatically for you, but if you update your model from other asynchronous callbacks, you have to take care of it yourself.
See this blog post and this documentation page for more in-depth explanations about what's going on.

Resources