SPA from url not on root (and including a filename.aspx) routing and refresh - asp.net

I have a SPA, I want to use routing for ng-view.
I have the code included in a page at domain.com/folder/dashboard.aspx
This is just a piece of that existing page, I can't move it elsewhere.
When I use route /list it alters my url to domain.com/folder/list/ which works, but breaks the ability to refresh the page (and gives a 404 since dashboard.aspx is not a default page, nor can it be)
How can I keep the url as domain.com/folder/dashboard.aspx/list?
I did try to setup my routes as dashboard.aspx/list and other various similar adjustments, but didn't have any luck.

Just like what #Claies said, it should be handled in your server config, just gonna drop my route config here in case you haven't tried this yet
var routeWithoutResolving = function (template: string, title?: string, style?: string) {
var name;
var slashIdx = template.indexOf('/');
if (slashIdx !== -1) {
name = template.substring(0, slashIdx);
template = template.substring(slashIdx + 1);
} else {
name = template;
}
var templateUrl = '/folder/' + template + '.aspx/';
return {
templateUrl: templateUrl,
title: title,
style: style,
area: _.capitalize(name),
page: template,
reloadOnSearch: false
}
}
Usage
.when('/domain.com/folder/dashboard.aspx/list', routeWithoutResolving ('folder/dashboard.aspx'))

I figured it out.
You can't use HTML5 mode, you have to be using Hashbang.
I set my routes as normal, /list and /list/item
For my links, I just used full urls, with the Dashboard.aspx#!/list/item and /list
I also removed the base tag from the html page

Related

How to change meteor router paths/slugs without reloading the template?

I have two router paths, one is /topic/:postId and one is /topic/:postId/:commentId. Both paths are pointing to the template "topic".
The first path opens the template with a list of all the post's comments. Whenever I select a comment, I filter some contents on the page based on the comment and I want the route to change to the second path (allowing users to copy the link to this filtered page directly), but I want meteor to stay on the same template and not reload the page.
In short, how can I change the URL/slugs without reloading the page?
Edit: These are my routes pointing to the same template and I'd like to jump between them without (noticeable) reload. I left out waitOn and multiple data lines for simplicity' sake:
Router.route("/topic/:postId", {
name: "Topic",
data: function () {
return {
content: "topic",
post: this.params.postId
};
}
});
Router.route("/topic/:postId/:commentId", {
name: "Comment",
data: function () {
return {
content: "topic",
post: this.params.postId,
comment: this.params.commentId
};
}
});

grails controller/action/id automagically turning into controller/index

My problem is that the backend server (written in grails) is automatically converting my request URL to be a different URL. Specifically, it is changing it from /UXChallengeAwards/processSelectedNotifications to /UXChallengeAwards/index.
--
In a template gsp file, I have defined a button that makes a jQuery ajax call when clicked on:
<button class="blue-link"
onclick="jQuery.ajax({type:'POST',
data:jQuery(this).parents('.multiSelectForm').serialize(),
url: '/ici/UXChallengeAwards/processSelectedNotifications/${challenge.id}',
success:function(data,textStatus){},
error:function(xhr,textStatus,errorThrown){}
})" >
The method UXChallengeAwardsController.processSelectedNotifications exists. It performs some work and then redirects to another action in the controller. In fact, this used to work. But somehow in the process of adding a second button I made a change which seems to have broken things.
When the button is now clicked, the request URL gets switched to /ici/UXChallengeAwards/index and a 404 is returned because index does not exist as an action in this controller.
I've googled, and the most common answer for when this happens is that a controller must return some results for the view. But I've seen plenty of examples of redirects in controllers, and I do not see what I am doing wrong. (I did try variants of rendering results, but with no success.)
Here is what my controller action looks like:
def processSelectedNotifications = {
def challenge
def checkboxes = params.list('selectCheckbox');
for (checkbox in checkboxes) {
// the checkbox contains the id of a ChallangeAward that should be published
ChallengeAwards challengeAwards = ChallengeAwards.get(checkbox.toInteger())
if (challengeAwards) {
// grab a challenge for use in the redirect, they are all the same
challenge=challengeAwards.challenge
publish(challengeAwards)
}
}
if (challenge) {
redirect action: 'challengeAwardsRemote', id: challenge.id
return
}
// render a failure message if we got here
render messageNS(code:"UX.ChallengeAwards.Publish.failure")
}
I would really appreciate any insights into what might be wrong, or how to go about tackling this issue. I've checked my UrlMappings, and this is the rule that should handle this controller/method request:
"/$controller/$action?/$id?"{ constraints {} }
Thank you very much!
I'm going to go ahead and answer my own question, in case it is helpful for other newbies.
It turns out that I was not getting an automatic redirect. Rather, I had an error in the button setup code, so that grails was using its default link behavior. (Which is to go to the controller that matches the view, and if no action is specified, use the index method.)
The code above was originally created using a remoteSubmit tag, but I found that the generated code did not support handling multiple forms on a single page very well. So, I copied that generated code and then tweaked it to handle the multiple forms. However, I wanted the styling to match up with what was already in place on the page, so I switched it to be a button. That's when things went awry.
Eventually, I ended up specifying an onClick function for the button, and then writing the ajax submit code in javascript. Which turned out to be much simpler.
Here is what the button specification ended up looking like:
<button type="submit" id="notifications" class="blue-link" >
<i class="fa fa-envelope-o"></i>
<g:messageNS
code="UX.DiscussionBoard.ChallengeAward.Button.notify" />
</button>
And the associated JavaScript:
jQuery(document).ready(function() {
var clkBtn = "";
jQuery('button[type="submit"]').click(function(evt) {
clkBtn = evt.target.id;
});
jQuery('.multiSelectForm').submit(function() {
var url = '/ici/UXChallengeAwards/processSelectedNotifications';
if (clkBtn == 'deletes') {
url ='/ici/UXChallengeAwards/processSelectedDeletes';
}
var errorTarget = jQuery(this).parents().find('.recipientMessage').val();
var requestData = jQuery(this).parents('.multiSelectForm').serialize();
var options = {
data : requestData,
type : 'POST',
url : url,
target : '#awardsTab',
error : function(data) {
jQuery('#' + errorTarget).html(data.responseText).show();
},
success : function(data) {
console.log("in success");
}
};
jQuery(this).ajaxSubmit(options);
return false;
});

ASP.NET Route config for Backbone Routes with PushState

I have run into an issue recently where we have been told to remove the hash symbols from our Backbone applications. This presents two problems: (a) the ASP.NET routes need to handle any remotely linked URL (currently this is no problem with the hash symbols) so that we're not hitting a 404 error and (b) the proper route needs to be preserved and passed on to the client side (Backbone) application. We're currently using ASP.NET MVC5 and Web API 2 for our backend.
The setup
For an example (and test project), I've created a test project with Backbone - a simple C# ASP.NET MVC5 Web Application. It is pretty simple (here is a copy of the index.cshtml file, please ignore what is commented out as they'll be explained next):
<script type="text/javascript">
$(document).ready(function(event) {
Backbone.history.start({
//pushState: true,
//root: "/Home/Index/"
});
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
//appRouter.navigate("/test/sometext", { trigger: true });
//appRouter.navigate("/help", { trigger: true });
});
</script>
<div class="jumbotron">
<h3>Backbone PushState Test</h3>
</div>
<div class="row"></div>
Now, without pushState enabled I have no issue remote linking to this route, ie http://localhost/Home/Index#test/sometext
The result of which is that the div with a class of .row is now "Hello, sometext".
The problem
Enabling pushState will allow us to replace that pesky # in the URL with a /, ie: http://localhost/Home/Index/test/sometext. We can use the Backbone method of router.navigate("url", true); (as well as other methods) to use adjust the URL manually. However, this does not solve the problem of remote linking. So, when trying to access http://localhost/Home/Index/test/sample you just end up with the typical 404.0 error served by IIS. so, I assume that it is handled in in the RouteConfig.cs file - inside, I add a "CatchAll" route:
routes.MapRoute(
name: "CatchAll",
url: "{*clientRoute}",
defaults: new { controller = "Home", action = "Index" }
);
I also uncomment out the pushState and root attributes in the Backbone.history.start(); method:
Backbone.history.start({
pushState: true,
root: "/Home/Index/"
});
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
//appRouter.navigate("/test/sometext", { trigger: true });
//appRouter.navigate("/help", { trigger: true });
This allows me to at least let get past the 404.0 page when linking to these routes - which is good. However, none of the routes actually "trigger" when I head to them. After attempting to debug them in Chrome, Firefox, and IE11 I notice that none of the events fire. However, if I manually navigate to them using appRouter.navigate("/help", { trigger: true }); the routes are caught and events fired.
I'm at a loss at this point as to where I should start troubleshooting next. I've placed my Javascript inside of the $(document).ready() event as well as the window.onload event also (as well as not inside of an event); none of these correct the issue. Can anyone offer advice on where to look next?
You simply have to move Backbone.history.start after the "new Route" line.
var Route = Backbone.Router.extend({
routes: {
"test/:id": function (event) {
$(".row").html("Hello, " + event);
},
"help": function () {
alert("help!");
}
}
});
var appRouter = new Route();
Backbone.history.start({
pushState: true,
root: "/Home/Index/"
});
Make sure you go to ".../Home/Index/help". If it doesn't work, try temporarily removing the root and go to ".../help" to see if the root is the problem.
If you still have troubles, set a js breakpoint in Backbone.History.loadUrl on the "return" line. It is called from the final line of History.start to execute the current browser url on page load. "this.matchRoot()" must pass then, "fragment" is matched against each "route" or regexp string in "this.handlers". You can see why or why not the browser url matches the route regexps.
To set to the js breakpoint, press F12 in the browser to open the dev console, press Ctrl-O or Ctrl-P to open a js file, then type the name of the backbone js file. Then search for "loadUrl:". You can also search for "Router =" to find the start of the router class definition (same as for "View =" and "Model =" to find the backbone view/model implementation code). I find it quite useful to look at the backbone code when I have a question like this. It is surprisingly readable and what better place to get answers?
If your js files happen to be minified/compressed, preferably turn this off. Alternately you can try the browser unminify option. In Chrome this is the "{}" button or "pretty print". Then the js code is not all on 1 line and you can set breakpoints. But the function and variable names may still be mangled.
I have solved my own problem using what feels to be "hackish", via the following. If anyone can submit a better response it would be appreciated!
My Solution:
I globally override the default Backbone.Router.intilaize method (it is empty) with the following:
$(document).ready(function (event) {
var _root = "/Home/Index/";
_.extend(Backbone.Router.prototype, {
initialize: function () {
/* check for route & navigate to it */
var pathName = window.location.pathname;
var route = pathName.split(_root)[1];
if (route != undefined && route != "") {
route = "/" + route;
this.navigate("", { trigger: false });
this.navigate(route, { trigger: true });
}
}
});
});

deep linking to embedded iframe content in wordpress

Is there a WordPress plugin that will enable deep linking to an embedded iframe? I'd like to be able, for example, to tweet a URL to a post that has extra information that will be passed down to the iframe.
An example would be an iframe that plays a video. The extra information in this case might be the time offset to start playing the video.
The extra info could be passed as query params, fragments, or some other way.
Probably not via a WordPress plugin, unless you are looking to develop a custom plugin.
It is best to avoid iframes whenever you can for these reasons.
That said, the solution is pretty simple using the window.postMessage method and works in most browsers, including IE8 and up.
Notes:
All messages should be sent as strings to avoid a nasty bug in IE8/9. If you want to pass an object, pass it in JSON format.
You can't JSON.serialize() the window.location object in IE8. If you are trying to pass that object, you have to copy the properties one by one.
IE only supports el.contentWindow.postMessage(), not el.postMessage().
Outer page
window.onload = function()
{
var child = document.getElementById('deep_link_frame');
var msg = {
"location" : {
"hash" : window.location.hash,
"host" : window.location.host,
"hostname" : window.location.hostname,
"href" : window.location.href,
"origin" : window.location.origin,
"pathname" : window.location.pathname,
"port" : window.location.port,
"protocol" : window.location.protocol,
"search" : window.location.search
}
};
child.contentWindow.postMessage(JSON.stringify(msg), '*');
};
Inner page
function bindEvent(el, eventName, eventHandler)
{
if (el.addEventListener)
{
el.addEventListener(eventName, eventHandler);
}
else
{
el.attachEvent('on' + eventName, eventHandler);
}
}
bindEvent(window, 'message', function(e)
{
if (e.origin === "http://your-domain.com")
{
var message = JSON.parse(e.data);
alert(message.location.href);
}
});

Excluding bootstrap from specific routes in Meteor

I was hoping anyone could give some input on this,
I'm creating a meteor app in which I would like to use bootstrap to creating the admin environment, but have the visitor facing side using custom css. When I add the bootstrap package to my app using meteor it's available on every page, is there a way to restrict the loading of bootstrap to routes that are in '/admin' ?
When you add bootstrap package it's not possible. You can, however, add bootstrap csses to public directory and then load them in a header subtemplate that will only be rendered when you're in the dashboard.
EDIT
But then how would you go about creating seperate head templates?
Easy:
<head>
...
{{> adminHeader}}
...
</head>
<template name="adminHeader">
{{#if adminPage}}
... // Put links to bootstrap here
{{/if}}
</template>
Template.adminHeader.adminPage = function() {
return Session.get('adminPage');
}
Meteor.router.add({
'/admin': function() {
Session.set('adminPage', true);
...
}
});
DISCLAIMER: I am unsure of a 'meteor way' to do this, so here is how I would do it with plain JS.
jQuery
$("link[href='bootstrap.css']").remove();
JS - Credit to javascriptkit
function removejscssfile(filename, filetype){
var targetelement=(filetype=="js")? "script" : (filetype=="css")? "link" : "none" //determine element type to create nodelist from
var targetattr=(filetype=="js")? "src" : (filetype=="css")? "href" : "none" //determine corresponding attribute to test for
var allsuspects=document.getElementsByTagName(targetelement)
for (var i=allsuspects.length; i>=0; i--){ //search backwards within nodelist for matching elements to remove
if (allsuspects[i] && allsuspects[i].getAttribute(targetattr)!=null && allsuspects[i].getAttribute(targetattr).indexOf(filename)!=-1)
allsuspects[i].parentNode.removeChild(allsuspects[i]) //remove element by calling parentNode.removeChild()
}
}
removejscssfile("bootstrap.css", "css")
However, doing that would complete remove it from the page. I am not sure whether meteor would then try to readd it when a user goes to another page. If that does not automatically get readded, then you have an issue of bootstrap not being included when someone goes from the admin section to the main site, which would break the look of the site.
The way I would get around that would be to disable and enable the stylesheets:
Meteor.autorun(function(){
if(Session.get('nobootstrap')){
$("link[href='bootstrap.css']").disabled = true;
}else{
$("link[href='bootstrap.css']").disabled = false;
}
});
There my be other bootstrap resources which may need to be removed, take a look at what your page is loading.
To use jQuery in the same way but for the javascript files, remember to change link to script and href to src
From my tests, Meteor does not automatically re-add the files once they have been removed so you would need to find some way of re-adding them dynamically if you want the same user to be able to go back and forth between the main site and the admin site. Or simply if the http referrer to the main site is from the admin, force reload the page and then the bootstrap resources will load and everything will look pretty.
P.s. make sure you get the href correct for the jQuery version
If somebody is interested in including any js/css files, I've written a helper for it:
if (Meteor.isClient) {
// dynamic js / css include helper from public folder
Handlebars.registerHelper("INCLUDE_FILES", function(files) {
if (files != undefined) {
var array = files.split(',');
array.forEach(function(entity){
var regex = /(?:\.([^.]+))?$/;
var extension = regex.exec(entity)[1];
if(extension == "js"){
$('head').append('<script src="' + entity + '" data-dynamicJsCss type="text/javascript" ></script>');
} else if (extension == "css"){
$('head').append('<link href="' + entity + '" data-dynamicJsCss type="text/css" rel="stylesheet" />');
};
});
}
});
Router.onStop(function(){
$("[data-dynamicJsCss]").remove();
});
}
Then simply use:
{{INCLUDE_FILES '/css/html5reset.css, /js/test.js'}}
in any of your loaded templates :)

Resources