I would like to use AngularJS for a single page webapp.
I am concerned if there is an elegant way to "send" different templates based on whether the client is a mobile or desktop.
Is there any way to do it ? Is it recommended that web server "understand" what the browser is and send the view accordingly so the browser always asks for template.html OR you write javascript so the browser will tell webserver to get the mobile/template.html ?
if you wanted to use the same URL but serve two different sets of HTML (say swap out large images and inpage videos for something else) I would do something like this
'use strict';
angular.module('MyApp', []).config(function ($routeProvider) {
// Magic sauce, imediate so the value is stored and we don't need to lookup every check
var _isNotMobile = (function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
return !check;
})();
// Swap out different HTML because you want to say, hide a video etc.
$routeProvider
.when('/', {
templateUrl: (_isNotMobile )? 'views/MY_DESKTOP_VIEW.html':'views/m/MY_MOBILE_VIEW.html',
controller: (_isNotMobile )?'MyHomeCtrl':'MyMobileCtrl'
})
.otherwise({
redirectTo: '/'
});
});
How I would go about with this is to display one template to the user and make the template Responsive. Just because you are using AngularJS templates, I do not see a reason why you would not want to make the template responsive. I would not go for the solution that involves displaying a different template to the user based on the device browser.
That said, one way that I would do is:
To have a simple script for the home / landing page of the web application that determines the browser / device. This can be found here.
Next, depending on the browser / device, you redirect the user to a different route
Have different routes based on the browser / device type - display a different template based on the route and thus identify if it s a mobile device or not based on the route.
The last step would be something like:
angular.module('myApp', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
//Display desktop version
when('/desktop/homePage', {
//Template for Desktop based browsers
templateUrl: 'partials/desktop/home-page.html'
}).
//Display mobile version
when('/mobile/homePage', {
//Template for Mobile based browsers
templateUrl: 'partials/mobile/home-page.html'
}).
otherwise({redirectTo: '/desktop/homePage'});
}]);
Responsive design will work well for smaller apps but gets rather messy when you move to bigger applications.
I'd personally suggest detecting the user agent on page load, and redirecting him to a separate mobile app if needed. You can still use most of your code base for both apps (simply import individual modules).
If your interested in detecting the user agent using javascript I suggest this solution (simply select javascript): http://detectmobilebrowsers.com/ the most extensive solution I've found so far
use boostrap. and it could be easily done.
<!-- Display Only Screen > Big -->
<div class="hidden-xs">
<div ng-view class="section" ng-class="animate"></div>
</div>
<!-- Display Only Screen < Small -->
<div class="visible-xs">
<div ng-swipe-right="openSlide()">
<div ng-view class="section" ng-class="animate"></div>
</div>
</div>
and in landing.html
<div class="visible-xs" ng-include="'{{template path}}/desktop.html'" ></div>
<div class="hidden-xs" ng-include="'{{template path}}/mobile.html'" ></div>
ANd in config
$routeProvider
.when(/,{
templateUrl : "landing.html"
controller : "landingCtrl"
});
it works floawlessley for me. Its not of the way. maybe there could be another using pure JS. This just happened pout of the box since i am using boostrap and leverage it to my advantage.
A bit late, but in something like your header, or nav controller, you could set the initial width:
angular
.module('myApp')
.controller('navCtrl', ['$rootScope', '$window',
function($rootScope, $window) {
$rootScope.is_mobile = ($window.innerWidth < 480);
And if you want checking on resize, go ahead and bind it:
angular.element($window).bind('resize', function() {
$scope.$apply();
});
Then watch it:
$scope.$watch(function () {
return $window.innerWidth;
}, function (innerWidth) {
$rootScope.safeApply(function () {
$rootScope.is_mobile = innerWidth < 480 // went with max device width
});
});
Then in your HTML:
<div ng-if="$root.is_mobile">Show me only in mobile</div>
BOOSTRAP + ANGULARJS solution to this problem:
You can check out the angular-match-media library. It is extremely small in size but very helpful and elegant.
https://github.com/jacopotarantino/angular-match-media
**Watch out: This is installed with bower install angular-media-queries; however, the path to the js file is /path/to/library/angular-media-queries/match-media
Related
I have an angular app which has a ng-view which (like any good MVC should) manipulates how the model is shown. The data (model) comes from a database, and I call it into the app's component. From there I want to propagate (if that's the right word) the model into ng-view, which loads a template to display the data based on the route. I also want to be able to filter the data/model that goes into the view with a "top-bar"
I.e:
INDEX.HTML:
<html ng-app="app">
<head>...</head>
<body ng-controller="appController">
<top-bar></top-bar>
<div ng-view></div>
</body>
</html>
APP.JS:
angular.module('app', ['top-bar','view-one','view-two', 'ngRoute']);
angular.module('app').controller('appController', function() {
var self = this;
this.myData = [];
$http.get('theQuery').then(res => self.myData = res.data);
});
angular.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/view-one', {template:'<view-one></view-one>'})
.when('/view-two', {template:'<view-two></view-two>'});
});
angular.module('top-bar', ['ngRoute']);
angular.module('top-bar').component('top-bar', {
templateUrl: './app/top-bar/top-bar.template.html',
controller: function(filterFilter) {
this.filters = filterFilter(...);
}
});
angular.module('view-one', ['ngRoute']);
angular.module('view-one').component('view-one', {
templateUrl: './app/view-one/view-one.template.html',
controller: function(filterFilter) {
// appController.data and topBar.filters would somehow
// need to be gotten from those respective modules.
this.data = appController.data;
this.filter = topBar.filters;
}
});
What I am trying to figure out is how to get the data from the main app's controller (appController) and the top-bar component, and send it to whatever view is currently loaded into ng-view.
I've been searching the web, I cannot find if the better way to do this would be to use binding (i.e. binding: {data:'<'})in the view-one controller/component, a system of $scopes, a custom service or something else. I also can't find out I would accomplish using either one to get the data in there. Thus any answers that also include a) code samples and b) links to further documentation I could read up on would be would be much appreciated.
The recommended way for doing this is to create a service, and let the different controllers work with the reference to the objects provided by the service.
Possible duplicate of enter link description here
This question may have been asked before. But here goes:
My website is http://thecheeknee.com.
The site basic structure is as follows:
`index.html
js|-app.js
js|ctrls|pageLoad.js -common controller for all pages
js|srvc|Datamap.js
templates/-about.html, etc`
The HTML basic view is as shown below:
<section ui-view></section>
The UI router angular code is as follows:
angular
.module('app',[
'ui.router',
'ngStorage'
])
.config(['$urlRouterProvider','$stateProvider',function($urlRouterProvider, $stateProvider){
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home',{
url:'/',
templateUrl:'templates/home.html',
controller:'pageLoad'
})
The controller is very basic.
angular
.module('app')
.controller('pageLoad',[ '$scope', 'Datamap', function($scope, Datamap){
$scope.title = "thecheeknee";
//$scope.$storage = $localStorage;
//$scope.$storage.counter = $scope.$storage.counter+1 || 0;
Datamap.getData().then(function(response){
$scope.datamap = response.data;
});
The Data map service is as follows:
angular.module('app')
.factory('Datamap', function($http) {
//debugger;
return{
getData: function(){
return $http.get('data/data.json',{ cache: true});
}
}
});
The site works perfectly on the desktop across multiple browsers. But Chrome App on Android seems to load a blank page.
(I had designed the site to be responsive and had tested the UI thoroughly using Chrome's browser tool)
On observing, I noticed that the browser adds a #!/ and loads the view into the page. On the mobile browser however, it seems to be stopping at the site name itself (#!/ is not being added). So I assume the UI router is unable to load the route into the page.
I am relatively new to Angular, so is there anything minor I am missing out here?
Full source code at: https://github.com/thecheeknee/sitebase.git
This question might sound very generic to some of you but as a newbie i am having trouble in this. Its evident to use ng-view within the home page in order to display other html files within the page but how should i redirect to a new page present in the web app. I mean how to route to completely different web page in a multipage web application.
Import AngularJs-Route File
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>
Then you must add the ngRoute as a dependency in the application module:
var app = angular.module("myApp", ["ngRoute"]);
Use the $routeProvider to configure different routes in your application:
app.config(function($routeProvider) {
$routeProvider
.when("/", {
templateUrl : "main.htm"
})
.when("/red", {
templateUrl : "red.htm"
})
.when("/green", {
templateUrl : "green.htm"
})
.when("/blue", {
templateUrl : "blue.htm"
});
});
STructure Your HTML
<body ng-app="myApp">
<p>Home</p>
Red
Green
Blue
<div ng-view></div>
</body>
For nested views you can use https://github.com/angular-ui/ui-router
Follow https://github.com/angular-ui/ui-router/wiki/Nested-States-&-Nested-Views for reference
Try searching angular ui-roter how its works and its mechanism . Since angular is a single page application your app needs to be on one base template then expand from their. From base template route in different page but if you want to route to different application use normal hyper link or ui-serf . Go though u-router basic. Also look into ui-serf .
I'm looking for a good approach to render 404 page instead of redirecting 404 page in angularjs. Many solutions I have found is all about redirecting to another page. It will create a problem that if the user click on browser's back button I will create another redirect to 404 page. So I am looking for a solution that renders 404 page instead.
Thanks for reading, I hope it's understandable for you guys.
Usage of otherwise might be what are you looking for.
angular.module('MyApp', [])
.config(function($routeProvider) {
$routeProvider.
when('/', {templateUrl:'/home.html'}).
// add as many as you want here...
otherwise({templateUrl:'/404.html'}); // Render 404 view
});
Update: After reading more carefully the OP question (sorry, quite early around here), I think I have an alternate solution (actually two):
1) A $scope variable to the main UI of your ng-view that hides the content
This requires ng-show in your view and resolve in your params, then do a $emit to the other controllers in order to tell "Hey, this guy hit a 404, don't display your view"
$routeProvider.otherwise({
controller: 'masterController',
resolve: {
404: function(){
return true;
};
});
angular.module('MyApp', [])
.controller('masterController', function($scope, 404) {
$scope.isOn404 = 404
...
})
// In the view
<div ng-controller="masterController">
<div ng-hide="isOn404">
<!-- Actual content -->
</div>
<div ng-show="isOn404">
<!-- 404 Page -->
</div>
</div>
Now, this requires that you have a master controller that helps you to manage the rest of your UI. Also, you most likely would need to do some coding to handling the rest of the page instead of just using ng-view (e.g. some controllers that show the current header, body, etc).
2) A custom routing system
I actually have done this for a specific project: you have a service that sets up a "BackURL" and "FordwardURL"; each $onRouteChange you store where do you go and where do you come from; if the user is about to hit a 404, you can still render it through my original example, but when the user hits back, catch that through AngularJS and render the actual "Back" page. In this case I'm using a library that helps me with the routing on mobile devices called Lungo and a library that the company I work for uses, the L.A.B (Lungo Angular Bridge).
I'm new to AngularJS, so this may not be an ideal solution, but it works for showing a 404 page, or similar uses such as a login page:
See Working Example
Redirect everything to the same master template:
$routeProvider
.when('/', {
controller: 'homeController',
templateUrl: 'partial.master.html'
})
.when('/cust/:custid', {
controller: 'custController',
templateUrl: 'partial.master.html'
})
master.html template refers to the masterController and has a subpage:
<div ng-controller="masterController">
<h2>{{title}} - Header</h2>
<hr>
<div ng-include="subPage"></div>
<hr>
<h3>{{title}} - Footer</h3>
</div>
masterController has conditional sub-page logic:
controllers.custController = function($scope, $rootScope, $routeParams){
$rootScope.subpage = 'cust';
$scope.cust = getCustomer( $routeParams.custid );
};
controllers.masterController = function($scope, $rootScope) {
switch($rootScope.subpage) {
case 'home':
$scope.subPage = 'partial.home.html';
break;
case 'cust':
if($scope.cust) {
$scope.subPage = 'partial.cust.html';
} else {
$scope.subPage = 'partial.404notfound.html';
}
break;
You could also use ui-router as previously answered here: https://stackoverflow.com/a/23290818/2723184. IMHO, you not only get an awesome library but you get a better solution.
I´m developing an Angular-App. The user has to enter data on different pages whereas it´s possible to switch between the pages. Furthermore there is an "overview page" where the user can see the entered data.
So now im thinking about how to show the entered data on the overview page. Should i just use $rootScope to get the data or is it better to store it in JSON-objects or - but this is not suggested by Angular - store data in a service?
So where to get the entered data from the different pages?
Thanks!
If you want to persist data between actual HTML pages and not routes within a single page, you could use LocalStorage. Here is a service that will make that a little easier.
Another approach would be to use cookies. You can read more about using cookies in Angular here.
If you are wanting to persist the data across different routes, you will need to create a shared service. Here is an example of such an approach:
<div ng-app="myApp">
one | two
<div ng-view></div>
<script id="one.html" type="text/ng-template"><div><h1>Template One</h1>foo = {{shared.foo}}</div></script>
<script id="two.html" type="text/ng-template"><div><h1>Template Two</h1>foo = {{shared.foo}}</div></script>
</div>
angular.module('myApp', []).
config(function($routeProvider){
$routeProvider.
when('/one', {templateUrl: 'one.html', controller: 'OneCrtl'}).
when('/two', {templateUrl: 'two.html', controller: 'TwoCrtl'}).
otherwise({redirectTo: '/one'});
}).
service('sharedService', [function() {
this.foo = 'bar';
}]).
controller('OneCrtl', function($scope, sharedService){
$scope.shared = sharedService
}).
controller('TwoCrtl', function($scope, sharedService){
$scope.shared = sharedService
});
Here's a fiddle.