I trying to learn web technologies (HTML, JS, angular, etc. what is needed to create pages). For practice I downloaded some kind of website template and noticed that there is a lot of same code in different html files(pages).
For example we have pages: login,main,about. Each of this page has same <header/> and <footer> and difference is only in <section/> between them.
So is it possible to have such structure: Main page with header and footer and on menu click changes only section between them? It will look like single page app.
As I use angularjs I know that there is such attribute as a ng-view but I am not sure if it will be suitable here, when, lets call, inner pages have this attribute inside also. (I tried I got RangeError: Maximum call stack size exceeded not sure probably some kind of infinitive loop appeared).
So what would be best solution in particular situation, I am not sure that my suggested structure is good, but I do not want to have same blocks of code in each page or it should be in HTML?
I use HTML, angular, JS, bootsrap for theme.
Examle
I copied two pages as example to plunker, so As you can see a lot of code is same, so I want to show login.html inside index.html (line: 172), but in login.html I also have ng-view (line:177). Maybe someone can with small code sample can show me how it is made?
http://plnkr.co/edit/iJrg2FJgwr9xxKTWMouX?p=preview
Yes, it's possible with AngularJs, and in fact very easy using ng-view or ui-view (from ui-router) .
Depending on your level of expertise I would suggest taking a look at ui-router which has all the ng-router functionalities and even more.
You can use ui-router to achieve this. You can define your states and templates using ui-router. Then for each state you can inject your views into sections marked as ui-view in your templates. Check out the example.
var myApp = angular.module("myApp", ["ui.router"]);
myApp.config(function($stateProvider) {
$stateProvider
.state('main', {
url: '/',
template: 'Main Page'
})
.state('list1', {
url: 'list1',
template: '<div>List1 {{test}}</div>',
controller: function($scope) {
$scope.test = "List 1";
}
})
.state('list2', {
url: 'list2',
template: '<div>List2 {{test}}</div>',
controller: function($scope) {
$scope.test = "List 2";
}
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
<html ng-app="myApp">
<body>
<div><a ui-sref="main">Header</a> <a ui-sref="list1">List1</a> <a ui-sref="list2">List2</a>
</div>
<div ui-view=""></div>
<div>Footer</div>
</body>
</html>
A simple routing setup for an app using ngRoute would be something like this.
angular.module('stack-app', ['ngRoute']);
// set up the views at configure time
angular.module('stack-app').configure(['$routeProvider',
function($routeProvider) {
$routeProvider.when('/view1', { // http://website.com/view1
controller: 'View1Controller', // the controller for view1
templateUrl: 'view1.html' // this is a partial view
});
$routeProvider.when('/view2', { // http://website.com/view2
controller: 'View2Controller', // controller for view 2
template: '<div>inline template should do just fine</div>'
});
$routeProvider.otherwise('/view1'); // http://website.com/anythingelse points to /view1
}
]);
angular.module('stack-app').controller('StackMainController', function() {});
angular.module('stack-app').controller('View1Controller', function() {});
angular.module('stack-app').controller('View2Controller', function() {});
index.html here you would put the common parts, headers etc.
<html ng-app="stack-app">
<body ng-controller="StackMainController">
<!-- this will be replaced by the partial views -->
<ng-view></ng-view>
</body>
</html>
You will have to configure whatever server you are using to serve index.html when receiving requests for /view1 or /view2.
Also don't forget to import the angular-route.js file in your project.
You can also have other modules register routes in their own configure method, there's no need to have a central config.
Related
Angular module is defined as :
angular.module('module1',['ngRoute']);
Route for module1 is defined as
module1.config(function($routeProvider)
{
$routeProvider
.when('/Application1',
{
templateUrl: 'Application1/Application1_HomePage.html',
})
.otherwise({
templateUrl: 'MainPage.html',
controller: 'ctrl'
)}
});
And the controller ctrl is defined as:
module1.controller('ctrl',function($scope,$location)
{
$scope.goToApp1 = function()
{
$location.path('/Application1',true);
}
});
The shell page is as :
<html ng-app="module1">
<body>
<div ng-view> </div>
</body>
</html>
Application1_HomePage.html contains another ng-app than the shell page ng-app module1.
From the shell page shown above, when a call to Application1_HomePage.html is made through routeProvider, initialization of ng-app written inside Application1_HomePage.html is failing. The browser inspector clearly shows nothing out of the DOM.
Considering the above scenario how can I make a navigation from one ng-app to another ng-app?
Assuming both of the modules have an entrance page you can just put a link to the other app page, for example:
you load module1Index.html and on that page you put an link to module2Index.html:
If you need both modules to sit on the same page , just include one in the other:
angular.module('module1', ['module2']);
I'm new to Angular and just started to build a test project to learn it, now have a problem with loading controllers OnDemand.
let's code a bit, I have the following HTML:
index.html
<body>
<div ng-app="MyApp">
CLICK ME
<div ng-view></div>
</div>
<script>
angular.module("MyApp",['ngRoute'])
.config(function($routeProvider) {
$routeProvider.when('/child', {
templateUrl: 'somwhere/child.html',
controller: 'childCtrl' <!-- will remove -->
});
}) <!-- will remove contoller defination below -->
.controller('childCtrl', function($scope) {
$scope.data = DoHeavyCalc();
});
</script>
</body>
what is obvious to me is that I should define my controller exactly where I config my module (MyApp), which doing this depends on DoHeavyCalc() which is not needed right now! (think this method does a big calculation, but it should be run only when the user clicks on the link, not at the beginning of the app).
Now I want to load the and define the controller inside child.html instead of my index.html. OK, so I removed the sections marked in above code and tried to write the child.html like this:
child.html
<div ng-controller="childCtrl">
{{data}}
</div>
<script>
angular.module("MyApp")
.controller('childCtrl', function($scope) {
$scope.data = DoHeavyCalc();
});
</script>
but is causes an error:
[ng:areg] `childCtrl` is not a function. got undefined.
also i tried to put script tag in child.html before the div tag, but it didn't affect anything.
Now my question is, how can i define and load the controller OnDemand and do my heavy work just when the user routes to a certain location not at the beginning of app?
You are asking about lazy loading ! By default angular doesn't support lazy loading, what you can do ? you can use any third party library like requirejs or others.
Currently angularjs doesn't execute any javascript inside templateUrl or template, more details
Here is a working example of lazy loading using requirejs.
Also there is some discussion regarding onDemand script loading
Lazy loading angularjs
In the child.html page do not include ng-controller attribute. Already child.html is associated with the childCtrl controller. Just remove the attribute ng-controller from the child.html page
I'm building an AngularJS application with routed views and to be clear the routing is working, as in the template pages in my /views/ folder are asynchronously loading into the view div on my index.html page.
The problem: These used to be seperate pages linked together the old fashioned way. Index.html, deal.html, and merchant.html. They all rendered perfectly in all browsers at that point but when I added this AngularJS routes feature, the same code is being dropped into the index.html file but the pages are not rendering properly. A lot of the styles are there... just misaligned. I can't wrap my head around it. I've looked everywhere and nobody seems to have documented this issue.
Here's what I've got so far:
head:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
<script src="app.js"></script>
The file system:
index.html
app.js
/views/main.html
/views/merchant.html
/views/deal.html
assets/css/
img/
index.html:
<div id="main">
<div class="ng-view" autoscroll="true"></div>
</div>
My app.js:
var appName = angular.module('adamApp', ['ngRoute']);
// configure routes
appName.config(function($routeProvider) {
$routeProvider.when('/', {
templateUrl : 'partials/home.html',
controller : 'mainController'
});
$routeProvider.when('/merchant', {
templateUrl : 'partials/merchant.html',
controller : 'merchantController'
});
$routeProvider.when('/deal', {
templateUrl : 'partials/deal.html',
controller : 'dealController'
});
$routeProvider.otherwise({ redirectTo: "/" });
});
Screenshot of old version of site without Angular routes:
Screenshot of Angular routes version with broken stylesheet:
Any ideas on what's going on?
If your html is under "views" in the file system, why are you declaring the templateUrl property to be "partials/..." ?
Change
$routeProvider.when('/deal', {
templateUrl : 'partials/deal.html',
controller : 'dealController'
});
to
$routeProvider.when('/deal', {
templateUrl : 'views/deal.html',
controller : 'dealController'
});
and a piece of advice: drop ngRoute and use ui-router which is become the defacto standard for routing. https://github.com/angular-ui/ui-router
Perhaps this can help:
How to include view/partial specific styling in AngularJS
and pay attention to the point
2. Specify which stylesheets belong to which routes using the $routeProvider
My question comes from the need to lazy load different/separate ngApps on one page(bootstrapping them with angular.bootstrap), each of them using its own ngRoute definitions(defined in such a way that they do not overlap each other).
Now I have a working plunkr example and all seems to function well, but somehow I have the feeling that this is not the correct approach so that's why Im asking here for advice.
<!DOCTYPE html>
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div class="container">
<div class="col-xs-6 well" id="app1">
<h3>App1</h3>
Route 1
Route 2
Route 3
<div class="well" ng-view></div>
</div>
<div class="col-xs-6 well" id="app2">
<h3>App2</h3>
Route 1
Route 2
Route 3
<div class="well" ng-view></div>
</div>
</div>
<script type="text/javascript" src="https://code.angularjs.org/1.2.21/angular.min.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.2.21/angular-route.min.js"></script>
<script>
var app1 = angular.module('app1', ['ngRoute']);
app1
.config(function($routeProvider) {
$routeProvider
.when('/:app/:param', {
template: 'app = {{app}}, param = {{param}}',
controller: 'Ctrl1'
})
})
.controller("Ctrl1",['$scope','$routeParams', function($scope,$routeParams) {
$scope.app = $routeParams.app;
$scope.param = $routeParams.param;
}]);
var app2 = angular.module('app2', ['ngRoute']);
app2
.config(function($routeProvider) {
$routeProvider
.when('/:app/:param', {
template: 'app = {{app}}, param = {{param}}',
controller: 'Ctrl2'
})
})
.controller("Ctrl2", ['$scope','$routeParams', function($scope,$routeParams) {
$scope.app = $routeParams.app;
$scope.param = $routeParams.param;
}]);
angular.bootstrap(document.getElementById("app1"), ['app1']);
angular.bootstrap(document.getElementById("app2"), ['app2']);
</script>
</body>
</html>
http://plnkr.co/edit/Y7A9bc3bDUyAXIS13JMZ?p=preview
FYI: I have already checked out ui-router to load separate states on one page, but it all requires for the source code to be loaded upfront(and have a single app.config with all routes in it) which is something I do not want as the Project is quite huge. So for the sake of keeping it simple and modular Im looking for something like the above example.
UPDATE to answer HockeyJ question:
OK I will try to keep this as short as possible. Very basically put - as I said the project could potentially become quite huge, from source code and module dependency perspective. This is why I want to make it in such a way, that each module can be totally separate App, able to be injected at any place and tested separately(thats not really an issue with AnJS). In the same time however the whole project, should be a single page app. As such there should not be screen reloads(to load an App jscript files) etc. Hence comes the lazy loading of scripts on demand and bootstrapping apps to DOM elems. The only possible intersection point of all apps is the URL routing which has strict naming convention for routes i.e. /reports/whatever ; /transactions/whatever etc. and should be managed by ngRoute.
UPDATED:
Checkout overmind project for angular and the demo here
Shows a project broken up into several apps: nav, home, profile, and admin. The nav app is always on the page and is the only app bootstrapped on pageload.
Just take routing out of angular and use something like sammy.js , history.js etc for client side routing.
Use
angular.bootstrap(module, node)
in the routes of your sammy handlers
Also you might want to take a look at react.js (with or without flux) and tide it up with sammy the same way. Consider this example from todombc : https://github.com/tastejs/todomvc/tree/gh-pages/architecture-examples/react
I just started this HTML5 project where we decided to make it a single page architecture by leveraging jQuery $.load() method. Unfortunately, as soon as the JS started to grow, we quickly started running into issues where the modules loaded into the master dashboard have no knowledge of their parent.
The architecture looks like this:
dashboard.html (master file)
moduleA.html
moduleA.js
moduleB.html
moduleB.js
moduleC.html
moduleC.js
Since we decided to also keep the JS as separate files, we are having to load all JS files through dashboard.html in order to invoke them individually when modulex is loaded.
So when loading moduleA.html into the dashboard we have to call its corresponding JS. To do this we simply wrote the JS using a Module Pattern so we can easily invoke it by doing a function call, like:
<script>
moduleA
</script>
or this if we want to access a specific property of this member.
<script>
moduleA.someMethod();
</script>
Now, I know there are is gotta be a nicer way of doing this, right? I hate having to have script tags in the HTML modules in order to load its corresponding JS file.
Another limitation of this is the fact that we no longer can work on modules individually, since the scripts and CSS invocation happens on the parent (dashboard.html) so certainly when moduleA.html is loaded directly, it is pure HTML with no script or CSS.
I looked through the other questions but I didn't see anyone with the same problem.
I looked at AngularJS, EmberJS, KO.JS and BoilerPlateJS but none of them addresses what we are trying to accomplish. The only one that has a similar single page concept is jQuery Mobile but I don't know if you can switch from jQuery to jQuery Mobile and everything remains working.
Has anyone face this issue yet? Is there a solution or would I have to go with a custom solution.
Thanks!
I could argue about AngularJS with you. It is exactly what you need
dashboard.html is layout with some directives attached, but power lies in AngularJs if you use ng-view directive
here is example:
dashboard.js
var app = angular.module("modularApp",[]);
app.config(['$routeProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
controller:'HomeCtrl',
templateUrl:'templates/home.html'
});
$routeProvider.when('/moduleA', {
controller:'ModuleACtrl',
templateUrl:'templates/moduleA.html'
});
$routeProvider.when('/moduleB', {
controller:'ModuleBCtrl',
templateUrl:'templates/moduleB.html'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
templates/dashboard.html
<html ng-app="modularApp">
<head>
<!--.... include angular minified js file and what else you need...-->
<script type="text/javascript" src="dashboard.js"></script>
<script type="text/javascript" src="moduleACtrl.js"></script>
<script type="text/javascript" src="moduleBCtrl.js"></script>
</head>
<body>
<a ng-href="#/moduleA">Open Module A View</a>
<a ng-href="#/moduleB">Open Module B View</a>
<!-- Add widgets header menus .... -->
<ng-view></ng-view>
</body>
</html>
moduleACtrl.js
var app=angular.module("modularApp");
app.controller("ModuleACtrl",function($scope){
$scope.scopeValue="Hellow from view";
});
moduleBCtrl.js
var app=angular.module("modularApp");
app.controller("ModuleBCtrl",function($scope){
$scope.scopeValue="Hellow from another view";
});
templates/moduleA.html
<div>{{scopeValue}} in module A</div>
templates/moduleB.html
<div>{{scopeValue}} in module B</div>
You can do more complex things with angular then just this. All depends on your needs. Do you have any special requirements :)
Also, you could create your own directive, like ng-view and use your own $route service and $routeProvider so you can add css and javascript you want to dynamically load when some rute match url.
so instead of above routing table, you could have
app.config(['$myRouteProvider', "$locationProvider", function routes($routeProvider, $locationProvider) {
$routeProvider.when('/dashboard', {
javascript:'javascript/dashboard.js',
templateUrl:'templates/dashboard.html',
cssfile: 'css/dashboard.css'
});
$routeProvider.when('/moduleA', {
javascript:'javascript/moduleA.js',
templateUrl:'templates/moduleA.html',
cssfile: 'css/moduleA.css'
});
$routeProvider.when('/moduleB', {
javascript:'javascript/moduleB.js',
templateUrl:'templates/moduleB.html',
cssfile: 'css/moduleB.css'
});
$routeProvider.otherwise({redirectTo: "/dashboard"});
}]);
But that is, pardon on my French, stup. There are couple libs I tried in ruby on rails to acheive similar, but backend is rendering content, or just part of content. But I'm not sure which backend you are using and are you interested to switch to rails anyway.
DomController in BoilerplateJS does what you need, without using any custom HTML attributes. Your dashboard.html can just have place holders where you want to inject your components. I'm just pulling out some html below from BoilerplateJS index.html to show how it works:
<body>
<section id="page-content">
<header>
<section class="theme"></section>
<section class="language"></section>
</header>
<aside>
<section class="main-menu"></section>
</aside>
</section>
</body>
theme, language and main-menu sections above are just place holders in to which relavant components would be injected by the DomController. The DomController can be now used to register the components with appropriate selectors as below:
//scoped DomController that will be effective only on $('#page-content')
var controller = new Boiler.DomController($('#page-content'));
//add routes with DOM node selector queries and relavant components
controller.addRoutes({
".main-menu" : new MainMenuRouteHandler(context),
".language" : new LanguageRouteHandler(context),
".theme" : new ThemeRouteHandler(context)
});
controller.start();
Above code is extracted from "/boilerplatejs/src/modules/baseModule/module.js"