MVC3 Custom Route has issues with Virtual Directory - javascript

I have this in my global
//custom route
routes.MapRoute(
"DownloadInstall", // Route name
"{controller}/{action}/{id}/{logonserver}", // URL with parameters
new { controller = "Software",
action = "DownloadInstall" } // Parameter defaults
);
//custom route
routes.MapRoute(
"DownloadHelp", // Route name
"{controller}/{action}/{id}/{logonserver}", // URL with parameters
new { controller = "Software",
action = "DownloadHelp" } // Parameter defaults
);
//default route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Software", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
and I invoke custom routes in javascript (which works great) like this:
window.location.href = '/Software/DownloadHelp/' + #Model.ID +'\/' +
getLogonServer();
However, as soon as I moved this to an IIS7 box which has a virtual directory, my default routes were smart enough to prepend with the virtual name...however my javascript based routes aren't found because the virtual directory isn't prepended.

I would try and use the Url helper if I were you, but I realize the javascript function result will be a problem.
I'm not sure if that will work, but you could try and build up your link like this:
var server = getLogonServer();
window.location.href = '#Url.Action("DownloadHelp", "Software",
new { Model.Id, logonserver = ""})' + '/' + getLogonServer();
What definitely would work is making getLogonServer() an Html helper function instead of a javascript function, but I don't know if that's an option for you.

I resolved it by using #Url.Content helper as such:
window.location.href = '#Url.Content("~/Software/DownloadInstall/")' + #Model.ID +'\/' + getLogonServer();

Related

Passing params throught pages with ui-router and angularjs

I would like to pass an object to a controllerA to another controllerB and display that object. To do it, I'm using ui-router with angularjs.
This is my controllerA which build the URL using $state.href():
const url = $state.href('home.stateA', {
objectA: objectA
});
window.open(url, '_blank');
Now, this my route file:
.state('home.stateA', {
url: '/stateA',
template: '<template-b></template-b>',
params: {
// object: null
objectA: null
}
})
And finnaly, I try to get my object in my controllerB like that:
// $scope.object = $stateParams.object;
$scope.object = $stateParams.objectA;
When I console.log $scope.object, I'm getting null which is the default value in my route file.
So what's going wrong ? I'm wondering if $window.open would not be the problem.
Thanks for helping me.
window.open(url, '_blank');
You are opening a new window and trying to pass an object.
Passing a String/Number
You can pass a string/number say id, as part of the URL, if your state URL is defined like '/stateUrl/:id'
and then you can access this using $stateParams.id
Sharing an object
You can use localStorage along with JSON.stringify and JSON.parse to share the object.
Set data
localStorage.setItem('object', JSON.stringify(object));
Get data
var object = JSON.parse(localStorage.getItem('object'));
I have just found the solution ->
ControllerA
const url = $state.href('stateA');
let newTab = $window.open(url);
newTab.objectA = objectA;
ControllerB:
$scope.objectA = $window.objectA
And the state is very simple :
.state('home.stateA', {
url: '/stateA',
template: '<template-b></template-b>',
})
I don't know if it's the best way to implement what I needed but at least it works. it may help someone else.
Thanks guys and have a nice day !
I suggest you use url parameter.
.state('home.stateA', {
url: '/stateA?object',
template: '<template-b></template-b>',
params: {
object: null
}
})
Do when open new tab page
const url = $state.href('home.stateA', {
objectA: JSON.stringify(objectA)
});
In controllerB
$scope.object = JSON.parse($stateParams.objectA);
Because when you are open new tab. The state param is lost

Parsing UI router states to URLs without Angular

I have a Angular 1.5.9 web application and a Node.js/Sails.js 0.12 backend.
Inside Angular runs UI router 0.4 to handle states.
The state definitions might look like this (quite vanilla, I'd say):
$stateProvider.state('dogs', {
url: '/ourdogsarecute_{specialIDofDog}
}).
state('dogs.specialDogState', {
url: '/specialinfo_{specialInfoOfDog}'
});
Now, the following situation arises: In the backend (i.e. outside of Angular), I have to transform an Angular UI router state link like
{stateName: 'dogs.specialDogState', stateParams: {specialIDofDog: 11212, specialInfoOfDog: 'likesbones' } } into a valid URL like https://www.our-app.dog/ourdogsarecute_11212/specialinfo_likesbones.
I have no idea how to do that without a lot of manual work. Is there a kind of parser for UI router states as a node module?
I can access the front-end code where the state definitions lie from the backend somehow. that's not the problem. The problem is the transformation from state links into URLs.
UI-Router 1.0 split the code up into ui-router core and ui-router angularjs. You can use ui-router core (which has no external dependencies) on your node backend to generate these urls. Since you already have your states available as a JSON file, you can simply register the states with ui-router core in your backend and then use the state objects to generate URLs.
In your node backend, add ui-router core
npm install --save #uirouter/core
// The library exports most of its code
var UIR = require('#uirouter/core');
// Create the router instance
var router = new UIR.UIRouter();
// Get the state registry
var registry = router.stateRegistry;
var states = [
{ name: 'dogs', url: '/ourdogsarecute_{specialIDofDog}' },
{ name: 'dogs.specialDogState', url: '/specialinfo_{specialInfoOfDog}' },
];
states.forEach(state => registry.register(state));
var params = { specialIDofDog: '11212', specialInfoOfDog: 'lovesbones' };
// Get the internal state object
var stateObj = registry.get('dogs.specialDogState').$$state();
// Generate the URL
console.log(stateObj.url.format(params));
For reference: my solution now looks like this.
First of all, I've put my state definitions into a separate file, making it easier to access it from outside:
var myStates = [
{
name: 'dogs', stateProperties: {
url: '/ourdogsarecute_{specialIDofDog}'
}
}, {
name: 'dogs.specialDogState', stateProperties: {
url: '/specialinfo_{specialInfoOfDog}'
}
}];
And then in my app.config
for(var i = 0; i < myStates.length; i++) {
$stateProvider.state(myStates[i].name, myStates[i].stateProperties);
}
In the backend, I've created this function:
/**
* #description Turns state name and state params into a URL string, using stateLinks definition synchronized from front end (being UI router state definitions)
* #param {string} stateName Something like 'dogs.info.specialAttributes'
* #param {object} stateParams Something like {dogID: 34346346, dogStatus: 'private', dogInfo: 'food'}
* #returns {string} URL
*/
stateLinkResolve: function(stateName, stateParams) {
if(!(stateName && stateName.length > 0)) {
return '/';
}
var resultUrl = '';
var splittedSubStates = stateName.split('.');// split "dogs.info.specialAttributes" into ["dogs","info","specialAttributes"]
var currentStateInHierarchy = '';
for(var i = 0; i < splittedSubStates.length; i++) {
/* Add dot if "in between": not the first, not the last. So that "dogs" remains "dogs", but when going to "dogs.info", we want the dot in between */
if(i > 0 && i < (splittedSubStates.length + 1) ) {
currentStateInHierarchy += '.';
}
currentStateInHierarchy += splittedSubStates[i]; // Add current splitted name (being only the last name part) to the state name in its context. I.e. when doing "info", we want to access "dogs.info"
var currState = _.find(stateDefs,{name: currentStateInHierarchy});
var urlRaw = currState.stateProperties.url;
/* uiRouter URLs may contain wildcards for parameter values like /ourdogs_{dogID:int}_{dogStatus}/:dogInfo.
We go through each of these three types and replace them with their actual content.
*/
for(var currentParam in stateParams) {
urlRaw = urlRaw.replace(':' + currentParam, stateParams[currentParam]); // search for ":paramName" in URL
urlRaw = urlRaw.replace('{' + currentParam + '}', stateParams[currentParam]); // search for "{paramName}" in URL
// search for "{paramName:paramType}" in URL
var uiRouterParamTypes = ["hash", "string", "query", "path", "int", "bool", "date", "json", "any"];
for(var j = 0; j < uiRouterParamTypes.length; j++) {
urlRaw = urlRaw.replace('{' + currentParam + ':' + uiRouterParamTypes[j] + '}', stateParams[currentParam]);
}
}
resultUrl += urlRaw;
}
return resultUrl;
}
The problem is: This might fail for edge cases and it will definitely fail for new features being implemented to UI state router and the way URLs are built there. So, still hoping for a solution which directly uses UI router magic.

Angular Factory Variables Won't Update - Instance gets old values

I have a resource factory that builds objects for accessing our API. I use an environment variable to determine the base part of the URL - whether or not to include 'account/id' path segments when the admin user is 'engaging' a client account.
The sessionStorage item that holds the 'engagedAsId' doesn't get read, though for instances created after engaging an account. It requires a full reload of the app to pick up that change. Here is the factory code:
myapp.factory('ResourceSvcFactory',
['$rootScope', '$resource',
function ($rootScope, $resource) {
function ResourceSvcFactory (endpoint) {
// vvv problem is here vvv
var accountId = sessionStorage.getItem('engagedAsId');
var apiPath = (accountId != null)
? '/api/account/' + accountId + endpoint
: '/api' + endpoint;
var Resource = $resource(apiPath+':id/',{
// default params
id:''
},{
// custom actions
update: {method: 'PUT'}
});
return Resource;
}
return ResourceSvcFactory;
}]);
myapp.factory('AssetsResource', ['ResourceSvcFactory', function (ResourceSvcFactory) {
var endpoint = '/assets/';
var Resource = ResourceSvcFactory(endpoint);
return Resource;
}]);
I implement this in my Controller like this:
myapp.controller('AssetGroupListCtrl', [ 'AssetgroupsResource', function (AssetgroupsResource) {
var AssetGroups = AssetgroupsResource;
// ... rest of controller
}]);
When i run this it works fine. But, if i change the engaged status in the sessionStorage without a full reload, the instance in the controller does not pick up the new path.
Is there a way to 'refresh' the instance? ...automatically?
After hours of research, it appears that the fundamental flaw in what I'm trying to do in the question is this: I'm trying to use a 'singleton' as a 'class'. from the docs:
Note: All services in Angular are singletons. That means that the injector uses each recipe at most once to create the object. The injector then caches the reference for all future needs.
http://docs.angularjs.org/guide/providers
My work around was to create the $resource inside a method of a returned object. Here is an example:
MyApp.factory('AssetgroupsResource',
['$rootScope', '$resource',
function ($rootScope, $resource) {
return {
init: function () {
var accountId = sessionStorage.getItem('engagedAsId');
var apiPath = (accountId != null)
? '/api/account/' + accountId + endpoint
: '/api' + endpoint;
// default params
id:''
},{
// custom actions
});
return Resource;
}
}
}]);
This made it possible to build the object at the right time in the controller:
MyApp.controller('AssetGroupListCtrl', ['Assetgroups', function (Assetgroups) {
var Assetgroups = AssetgroupsResource.init();
// now I can use angular's $resource interface
}]);
Hope this helps someone. (or you'll tell me how this all could've been done in 3 lines!)
You can always call $scope.$apply(); to force an angular tick.
See a nice tutorial here: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
I think $resource uses promise which might be an issue depending on how you implement your factory in your controller.
$scope.$apply() can return an error if misused. A better way to make sure angular ticks is $rootScope.$$phase || $rootScope.$apply();.

Retrieve the Url parameter from a function

I need to access the parameters of the current url with Durandal.
Actually from the activate function, I'm able to do so:
function activate(routeData) {
var type = routeData.type;
var id = parseInt(routeData.id);
}
and retrieve the following parameters project and 1 of my url:
http://localhost:3231/#/next/project/1
But how can I do so from another function within my view model?
NB: I need also to retrieve next from the url.
var params = window.location.hash.split('/');
Define an action param in your router.map function
something like:
router.map([
{ route: '', title:'Your title', moduleId: ':action/project/:id', nav: true }
])
So you can access the value in your activate function via
routeData.action

Why links generated with #Url.Action in JavaScript are not in lowercase when using AttributeRouting?

I have this code in a JavaScript function:
var url = '#Url.Action(MVC.Membership.User.ActionNames.Update, MVC.Membership.User.Name)';
url += "?userName=" + userName;
ul.append("<li><a href=" + url + "\>" + userName + "</a></li>");
Membership is an Area. I'm using T4MVC to refer to Controller and Action names to avoid magic strings... :)
This JavaScript code is part of View that resides in the Membership Area.
UserController is decorated this way:
[RouteArea("Membership")]
public partial class UserController : BaseController
and the Action method is this one:
[GET("Users/Update/{userName}")]
public virtual ActionResult Update(string userName)
The route I get in the link is this:
http://localhost:8087/membership/User/Update?userName=leniel
I expected it to be:
http://localhost:8087/membership/users/update?userName=leniel
So my question is: why the link is not in lowercase since all other links in the app are being generated with lower case letters? Is this not supported or am I forgetting some config related to AttributeRouting or the Area setup?
After the feedback from AttributeRouting creator... turns out it was my bad.
Now I understand the problem...
If I do this:
var url = '#Url.Action(MVC.Membership.Permission.ActionNames.GrantRevoke, MVC.Membership.Permission.Name, new { area = "Membership", roleName= "Teste" }, null)';
The URL is generated correctly:
var url = '/membership/permissions/grantrevoke/teste';
but if I do this:
var url = '#Url.Action(MVC.Membership.Permission.ActionNames.GrantRevoke, MVC.Membership.Permission.Name, new { area = "Membership" }, null)';
I get this:
var url = '/Membership/Permission/GrantRevoke';
It's clear that I need to pass the roleName parameter.

Categories