I have a Backbone App with the following structure
rootfolder
- assets
- js
app
- app.js
- router.js
- config.js
modules
- all my views in this folder
templates
- all my html templates here
- node_modules
- mobilepages (folder with all mobile pages, e.g. mobile BackboneJS setup)
How can I detect mobile users and load the mobilepages folder? Do I have to do this with the router?
Something like this?:
return Backbone.Router.extend({
routes: {
'': 'index'
}
index: function(){
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent))
{
this.navigate("/mobilepages", {replace:true})
};
}
Please help...
If there isn't any functional difference beetween mobile and non-mobile versions then you can do two differend things:
make adaptive css styles by using #media query.
In your view's render method make your check, which you specified in ansver, and based on its results change template to mobile version
But if you want explicitly define url for mobile devices, you should check for mobile users in your router like this:
routes: function(){
if(!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent))
{
return {
// routes for mobile devices
};
}
else
{
return {
// routes for non-mobile devices
};
}
}
Related
Is there any way to load JS selectively depending on module?
I want to have Bootstrap.css included in templates that are in frontend and Angular Material for temaplates that are in backend of my app.
Currently if I import bootstrap.css in one component it's loaded globally which I don't want. This also means that backend.css and ngMaterial will also be loaded in frontend components...
So the way to conditionally load CSS I came up with is not very elegant but works.
I'm using Angular UI Router and when it comes to resolve route I append/remove link tag with CSS sheet using angular.element(). In my case it's abstract routes for backend and frontend, therefore it resolves (and appends/removes) link tag only once when browsing children routes.
// Frontend - add Bootstrap
$stateProvider
.state('front', {
url: "",
abstract: true,
templateUrl,
resolve: {
importBootstrapCss() {
let head = angular.element(document.querySelector('head'));
head.append("<link rel='stylesheet' type='text/css' href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' id='bootstrap-css'>");
}
}
});
// Backend - remove Bootstrap
$stateProvider
.state('backend', {
url: "/backend",
abstract: true,
templateUrl,
resolve: {
deleteBootstrapCss() {
let link = angular.element(document.getElementById('bootstrap-css'));
link.remove();
}
}
});
I'm building a react app with webpack and i need to incorporate arcgis maps into a react component. I have know idea how to bring this into my project. I've tried creating an arcgis directory with an index.js of the built javascript and trying to reference that:
import {Map} from 'arcgis/index'
That doesn't work. I then just tried to include the css/js script tags directly into my index.html but when I try to require them, like in the example, webpack obviously can't find them. Is there some way to tell webpack to ignore require calls in my src file so it gets handled by the browser? I'm trying and failing at doing the following:
import React from 'react'
export default class EsriMap extends React.Component {
componentDidMount() {
const _this = this
require(["esri/map", "dojo/domReady!"], function(Map) {
var map = new Map(_this.refs.map, {
center: [-118, 34.5],
zoom: 8,
basemap: "topo"
});
});
}
render() {
return (
<div ref="map"></div>
)
}
}
You may want to try this https://github.com/tomwayson/esri-webpack-babel .
This method is nice because it doesn't bog down the build. You pull in the ESRI Api from the CDN, and tell webpack that it's an external.
//Add this...
externals: [
// Excludes any esri or dojo modules from the bundle.
// These are included in the ArcGIS API for JavaScript,
// and its Dojo loader will pull them from its own build output
function (context, request, callback) {
if (/^dojo/.test(request) ||
/^dojox/.test(request) ||
/^dijit/.test(request) ||
/^esri/.test(request)
) {
return callback(null, "amd " + request);
}
callback();
}
],
//And this to you output config
output: {
libraryTarget: "amd"
},
When your app loads you bootstrap you webpack modules using Dojo in a script tag.
<!-- 1. Configure and load ESRI libraries -->
<script>
window.dojoConfig = {
async: true
};
</script>
<script src="https://js.arcgis.com/4.1/"></script>
<!-- Load webpack bundles-->
<script>
require(["Angular/dist/polyfills.bundle.js", "Angular/dist/vendor.bundle.js", "Angular/dist/app.bundle.js"], function (polyfills, vendor, main) { });
</script>
I've got it working with an Angular 2 App I'm working on. The only downside is I haven't yet got the unit tests to run right using Karma. I've only been working on that a few hours now.. Hope to have a solution to the testing issue soon.
#getfuzzy's answer will work well as long as you don't need to lazy load the ArcGIS API (say for example only on a /map route).
For that you will want to take the approach I describe in this answer
This blog post explains why you need to use one of these two approaches and explains how they work as well as the pros/cons of each.
I think you can try using bower version of esrijsapi. Doc link
I have a sample MVC6 single page app with one view in which I want to load 2 Angular partials using ngRoute. You can have a look at it at GitHub
There are 3 URLs in the app:
localhost - Index.cshtml
localhost/games - Index.cshtml with Angular's gamelist.html partial
localhost/games/2 - Index.cshtml with Angular's game.html partial
The routes config is the following:
MVC:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
routes.MapRoute("gamelist", "games", new { controller = "Home", action = "Index"});
routes.MapRoute("gameWithId", "games/2", new { controller = "Home", action = "Index" });
});
Angular:
myApp.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/games', {
templateUrl: 'partials/gameslist.html',
controller: 'GameController',
controllerAs: 'ctrl'
})
.when('/games/:gameId', {
templateUrl: 'partials/game.html',
controller: 'GameController',
controllerAs: 'ctrl'
});
$locationProvider.html5Mode(true);
}]);
It all works perfectly fine as long as I start the app from the home page '/' and then navigate to the partials using the links on the page. The problem is that the URL #3 (localhost/games/2) does not work if I start the app from it, by typing it in the address bar. The URL #2 (/games/) does work.
The reason why #3 does not work is that MVC removes '/games' part from the URL and what Angular gets is just '/2'. If you run the sample app, you will see that '$location.path = /2'. Of course Angular cannot map using that path and no partial is rendered. So my question is - how to make MVC return the full path to the client so that Angular can map it?
You can get it to work with HTML5 mode, you just need to ensure that every request maps back to your Index.cshtml view. At that point the AngularJS framework loads, client-side routing kicks in and evaluates the request URI and loads the appropriate controller and view.
We've done this with multiple Angular apps inside MVC with different .cshtml pages, though we use attribute routing with the wildcard character, e.g.
[Route("{*anything}")]
public ActionResult Index()
{
return View("Index");
}
The wildcard operator (*) tells the routing engine that the rest of the URI should be matched to the anything parameter.
I haven't had chance to get to grips with MVC6 yet but I think you can do something like this with the "new" version of attribute routing?
[HttpGet("{*anything:regex(^(.*)?$)}"]
public ActionResult Index()
{
return View("Index");
}
To make link #3 work from the browser's address bar, I turned off "html5Mode" in Angular and made links #-based.
kudos to this blog
I think it is a better solution.
His solution is rewriting the request that doesn't fit to any route and doesn't have any extension to the landing page of angular.
Here is the code.
public class Startup
{
public void Configure(IApplicationBuilder app, IApplicationEnvironment environment)
{
// Route all unknown requests to app root
app.Use(async (context, next) =>
{
await next();
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
// Rewrite request to use app root
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/app/index.html"; // Put your Angular root page here
await next();
}
});
// Serve wwwroot as root
app.UseFileServer();
// Serve /node_modules as a separate root (for packages that use other npm modules client side)
app.UseFileServer(new FileServerOptions()
{
// Set root of file server
FileProvider = new PhysicalFileProvider(Path.Combine(environment.ApplicationBasePath, "node_modules")),
// Only react to requests that match this path
RequestPath = "/node_modules",
// Don't expose file system
EnableDirectoryBrowsing = false
});
}
}
I am using Hot Towel AngularJs SPA that John Papa created. I wanted to know how can I change the current menu to have sub menus.
[Edited]
The base implementation of HotTowel menu is looping into all routes and show it on the side bar. What I wanted to do is if there are sub-menu for one of the routes then it's content will be grouped in one menu item. similar to dropdowns sub menu
Don't forget that you're working with Models, and Controllers.
The Angular model and controller work in concert with the View John built.
public class HotTowelController : Controller
{
//
// GET: /HotTowel/
public ActionResult Index()
{
return View();
Notice that the Index is the View.
Ok.
look at the url when you run the page.
name
notice that its not loading another page.
That's because it's a SPA framework.
The Index has a Javascript method where we are defining the roles outside of the .NET Framework / MVC
So, we have a modeljs and a controllerjs. Just like MVC we have to use all three. But use those in the APP folder.
The APP Folder
Admin
Common
dashboard
layout
services
viewmodels
views
etc...
So
Go to the Layout Folder
open topnav.html
THen read the viewmodels
shelljs
var routes = [
{ route: '', moduleId: 'home', title: 'Reserve a Seat', nav: 1 },
{ route: 'details', moduleId: 'details', title: 'Poker Details', nav: 2 }];
return router.makeRelative({ moduleId: 'viewmodels' }) // router will look here for viewmodels by convention
.map(routes) // Map the routes
.buildNavigationModel() // Finds all nav routes and readies them
.activate(); // Activate the router
}
Seee what's happening here. We are mapping, knockout and angular have very easy mappings.
http://knockoutjs.com/documentation/plugins-mapping.html
This is an easy way to learn, and the vm is the smae for both only angular your using npm with a much more defined library in open source. both are mit so can't go wrong there.
then look at the config config.route mainjs
What do you see: Are you getting it?
Need more?
I am working on a web application that runs through backbone and makes use of the router. On success of certain events I want to redirect users to a static success page.
What is a good naming convention to clearly separate routing url paths from a static pages such as my success page?
the routing object in my route backbone object looks like this:
App.MyRouter = Backbone.Router.extend({
routes: {
"": "index",
"!/search/*search": "search",
"!/profile/*user": "profile",
"!/business/:id": "business"
}
});