AngularJS - Multiple ng-view in single template - angularjs

I am building a dynamic web app by using AngularJS. Is it possible to have multiple ng-view on a single template?

You can have just one ng-view.
You can change its content in several ways: ng-include, ng-switch or mapping different controllers and templates through the routeProvider.

UI-Router is a project that can help: https://github.com/angular-ui/ui-router
One of it's features is Multiple Named Views
UI-Router has many features and i recommend you using it if you're working on an advanced app.
Check documentation of Multiple Named Views here.

I believe you can accomplish it by just having single ng-view. In the main template you can have ng-include sections for sub views, then in the main controller define model properties for each sub template. So that they will bind automatically to ng-include sections. This is same as having multiple ng-view
You can check the example given in ng-include documentation
in the example when you change the template from dropdown list it changes the content. Here assume you have one main ng-view and instead of manually selecting sub content by selecting drop down, you do it as when main view is loaded.

Using regular ng-view module you cannot have more than one dynamic template.
However, this project enables you to do so (look for ui-router).

It is possible to have multiple or nested views. But not by ng-view.
The primary routing module in angular does not support multiple views.
But you can use ui-router. This is a third party module which you can get via Github, angular-ui/ui-router, https://github.com/angular-ui/ui-router . Also a new version of ngRouter (ngNewRouter) currently, is being developed. It is not stable at the moment.
So I provide you a simple start up example with ui-router.
Using it you can name views and specify which templates and controllers should be used for rendering them. Using $stateProvider you should specify how view placeholders should be rendered for specific state.
<body ng-app="main">
<script type="text/javascript">
angular.module('main', ['ui.router'])
.config(['$locationProvider', '$stateProvider', function ($locationProvider, $stateProvider) {
$stateProvider
.state('home', {
url: '/',
views: {
'header': {
templateUrl: '/app/header.html'
},
'content': {
templateUrl: '/app/content.html'
}
}
});
}]);
</script>
<a ui-sref="home">home</a>
<div ui-view="header">header</div>
<div ui-view="content">content</div>
<div ui-view="bottom">footer</div>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-ui-router/release/angular-ui-router.js">
</body>
You need referencing angularjs, and angular-ui.router for this sample.
$ bower install angular-ui-router

You cant have multiple ng-view. Below is my use case where I solved my requirement.
I wanted to have tabbed behavior in my model dialog. I was facing issue as click on tabs having hyperlink which will invoke router links. I solved this using button and css for tabs. When user clicks on tab, it actually will not call any hyperlink which will always invoke the ng-router. When user click on tab it will call a method, where I dynamcilly load html. Below is the function on click of tab
self.submit = function(form) {
$templateRequest('resources/items/employee/test_template.html').then(function(template){
var compiledeHTML = $compile(template)($scope);
$("#d").replaceWith(compiledeHTML);
});
User $templateRequest. In test_template.html page add your html content. This html content will be bind to your controller.

Related

Changing states with <a href="/#/xyz> instead of <a ui-sref="primary"> using ui-router

I'm using AngularJs V1.6. Ui- Router V 1.0.3
I've been trying to learn how to work with ui-router for the past few eeks and I came upon this code today which has me totally confused :-
Html side -
<ul ng-if="!isAuthenticated()" class="nav navbar-nav pull-right">
<li>Login</li>
<li>Sign up</li>
</ul>
JS -
$stateProvider
.state('home', {
url: '/',
controller: 'HomeCtrl',
templateUrl: 'partials/home.html'
})
.state('login', {
url: '/login',
templateUrl: 'partials/login.html',
controller: 'LoginCtrl',
resolve: {
skipIfLoggedIn: skipIfLoggedIn
}
})
This is code from Satellizer.
I tried replicating it but all it did was end up showing me the folder structure of my working directory in the browser.
However, I was using ui-router visualizer and upon clicking the route in that, it worked properly. I can't find any samples where ui-router is used this way, how exactly is this above snippet working?
I also read here that typing
<a ui-sref="party">Go To Party</a>
will turn it into
Go To Party
in our browser. However, in the example I posted intially, there is no ui-sref with the href. Once again, how exactly or what exactly is happening? Is it only working because it's retrieving a separate html file?
I tried replicating it but all it did was end up showing me the folder
structure of my working directory in the browser.
This happened because the first example in your question uses hash-prefix for generated links and the second example uses html5Mode for links.
When you click on a link with hash prefix mode eg. #/home, the request of that resource is handled at client-side by ui-router and is not sent to the server. But when you click on a link that is without prefix eg. /home, the request goes to the server. Your server needs to understand about this type of request (look at the referenced link below).
The default mode generates # as prefix. If you don't want to have hash prefix # in generated links then you need to enable HTML5Mode like this:
app.config(["$locationProvider", function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
If you enable this mode, then don't forget to inform Angular about the root URL of your app by adding following to the head section of your HTML file:
<base href="/">
Reference:
Read more about how to configure your server to work with html5Mode here.
About ui-sref directive:
The preferred way of route/state resolution using ui-router in HTML templates is to use ui-sref which is a directive that binds a link i.e. anchor tag ( tag) to a state. If a state has an associated URL, the directive will automatically generate, update the href attribute for you.
This way your HTML template just needs to refer a state name and the link resolution will be done for you which is good as if in future you change the underlying link for states, your templates will still work. This directive uses $state.href() method for link value.
You can directly use the associated link in anchor tag without using this directive. But doing this, you will always need to revise your link if you change url in route config. Let ui-router module help you to manage this without writing any extra line of code and to ease state management.
The usage of this directive is:
ui-sref='stateName' - Navigate to state, no params. 'stateName' can be any valid absolute or relative state, following the same syntax rules as $state.go()
ui-sref='stateName({param: value, param: value})' - Navigate to state, with params.
Example: If you html has following link:
<a ui-sref="home">Home</a>
The generated HTML (Html5Mode Off results in prepended '#') will be following provided your route config has a valid state named as home:
Home
Reference:
- UI Router ui-sref directive
When use you are write this routing sample
Home
You must be write this code in the routing
$locationProvider.html5Mode(true)
This link can help you

ui.router not working well in custom directive

I am working on custom directives in AngularJS and getting help from Mark Zamoyta's tutorials from PluralSight.
M using UIRouter in angular which is not working well in custom direcives while ngRoute is working well.Here is position of ui-view
<ng-transclude></ng-transclude>
<div ui-view></div>
In Controller i've this code to use routes/states getting from custom directive.
$scope.$on('ps-menu-item-selected-event', function (evt, data) {
$scope.routeString = data.route;
//$location.path(data.route);
$state.go(data.route);
checkWidth();
broadcastMenuState();
});
Now,I f i use ngroute then it is showing partial/template at right place but if i use ui router it goes out of ui-view ang show partials/templaes but not in between
<div ui-view></div>
i have thse searches who lead me to downgrading ui router's version but no success.
1.ui-view doesn't work when used inside angularjs custom directives
2.https://github.com/angular-ui/ui-router/issues/774
Any Help?????
When you use $location.path(url), you pass in something like /home/item into the url, but when you use $state.go(state), you pass in the state name like home.item. Make sure you pass the right thing.

AngularJS with ASP.NET MVC5 - using routeProvider with templates but also adding angularJs bound data in _Layout.cshtml

Good morning,
I am building an MVC5 application that includes a SPA (single page application) which runs in a small part of my bigger ASP.NET MVC5 application.
So here is my angular app setup:
var storeApp = angular.module('AngularStore', ['ngRoute']).
config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.
when('/shop', {
templateUrl: 'Templates/browse.html',
controller: storeController
}).
otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
}]);
As you can see it loads browse.html which is an html template.
My _Layout.cshtml file has the angular app registered at the top:
<html lang="en" ng-app="AngularStore">
Inside here, as with any ASP.NET MVC application it also has the following:
#RenderBody()
ASP.NET Mvc loads an Index.cshtml into the render body.
Here is my Index.cshtml:
#{
ViewBag.Title = "Shop";
ViewBag.InitModule = "AngularStore";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section MyScripts
{
<script src="~/js/product.js" type="text/javascript"></script>
<script src="~/js/store.js" type="text/javascript"></script>
<script src="~/js/shoppingCart.js" type="text/javascript"></script>
<script src="~/js/app.js" type="text/javascript"></script>
<script src="~/js/controller.js" type="text/javascript"></script>
}
<div ng-view></div>
As you can see the div that has the ng-view angular attribute on it is where my browse.html template will get loaded.
This all works as expected but here is my puzzle.
My browse.html template is a list of products. You can click "add to cart" and angular adds the product to the cart.
The thing is I want the count of the things in the cart and the money value to appear on the _Layout.cshtml. So in essence the count and money will appear no matter what template angular loads.
So, I want to put some angular stuff inside the top part (header) of my _Layout.cshtml that will look at the cart and show the count and money value.
This is probably easy but i'm having trouble figuring out how to get it to work and also structure it the right way.
Maybe I need to use ng-contoller on a on my _Layout.cshtml?
Any pointers would be greatly appreciated.
If you live in Melbourne i'll buy you a beer!
thanks
Russ
I'm also building applications that are mainly MVC but have several pages that are using angularjs and are mini-SPA's. You're going to need a controller used at your _Layout level. You can do this simply by creating a div at that level and using ng-controller directive as mentioned in your question. You do not need to do anything with routes at all, ng-controller defines the controller to use, and actually I don't use angularjs routing in my miniSPA's (though I can see where you might want to if your url changes within a single page).
What I think would be more appropriate however is to have a Partial view (which you can render from your _Layout if you want it always to be there), and within that partial view use ng-controller so you can have one controller for your cart and another controller for your shop. You have a number of options to communicate between the controllers. You could use the $scope.$root, or store your products in your Session data.
Keep a root controller in the Layout which includes header HTML. Define count variable in root controller and bind it to any header HTML element to display the count value. Now you can access the scope of the root controller in your nested controller (in your case storeController). You can set the count variable (from root controller) value inside the storeController which will automatically display the value in header as it's binded to some header HTML element.

Dynamically Loading Controllers and ng-include

At the moment I have an app that has a sidebar, and the sidebar loads different html templates using ng-include based on what operation the user chooses to do. It's a map related app, so for example, if the user selects the 'Add Leg' button it will load the add_leg.html template into the sidebar using ng-include:
// The Javascript code:
$scope.addLeg = function() {
$scope.sidebar = true;
$scope.sidebar_template = '/partials/legs/add_leg.html';
}
// Simplified example of HTML:
<html>
<div ng-app="MyApp" ng-controller="MainCtrl">
<a ng-click="addLeg()">Add Leg</a>
<a ng-click="addRoute()">Add Route</a>
<a ng-click="editLeg()">Edit Leg</a>
<a ng-click="editRoute()">Edit Route</a>
...
<div id="sidebar" ng-class="{'sidebar-hidden': sidebar == false}">
<div ng-include src="sidebar_template"></div>
</div>
<google-map></google-map>
</div>
This is all well and good and works as desired, but at the moment my app is only using one controller (MainCtrl in js/controllers.js) and it's starting to get really cluttered. I've got a lot of methods now because the apps functionality is expanding. I'd like to split my controller up into multiple controllers whilst retaining this sidebar functionality.
I want to have MainCtrl as the main controller that controls the loading of the sidebar template (by changing the sidebar_template variable that points to the file destination), and I want it to control some of the global map related methods (like fetching suburb names from coordinates, etc).
I've tried to split it like so:
controllers/js/main.js - MainCtrl
controllers/js/legs.js - LegCtrl
controllers/js/routes.js - RouteCtrl
I want the LegCtrl and RouteCtrl to inherit the MainCtrl so I can access its scope and methods, that's all fine. But now the problem is how do I dynamically load the controller onto the sidebar div based on what functionality is required. Originally all of my methods were in MainCtrl, and that's on the wrapper div that surrounds the sidebar div (see above again for an example), so it wasn't a problem.
For example, say I press the 'Add Leg' button, it's going to need to call addLeg in LegCtrl, but LegCtrl isn't loaded on the app, so it doesn't have access to the method. I could keep addLeg() inside the MainCtrl, and have it change the sidebar_template variable to load the template, but nothing in the template will work because it is calling methods from inside the LegCtrl now.
I need to somehow dynamically load the controller on the sidebar's ng-include div, something like this perhaps:
// MainCtrl
$scope.addLeg = function() {
$scope.required_controller = LegCtrl;
$scope.sidebar = true;
$scope.sidebar_template = '/partials/legs/add_leg.html';
LegCtrl.addLeg();
}
<div id="sidebar" ng-class="{'sidebar-hidden': sidebar == false}">
<div ng-include src="sidebar_template" ng-controller="{{required_controller}}"></div>
</div>
In the non-working example above you can see a possible solution I've thought of, but I need LegCtrlto be the actual controller function, not an object (for ng-controller to work). I also need some way to call addLeg on the LegCtrl from the MainCtrl.addLeg (perhaps using broadcast?).
If anyone can point me in the right direction that'd be great. Sorry for the huge post, it needed a bit of explaining to make it coherent. Hopefully it makes sense. Thanks.
Update: I think I've found a solution using a service to act as the navigation control (it will load the relevant templates and broadcast an event to the new controller being dynamically loaded to tell it what function to execute):
http://plnkr.co/edit/Tjyn1OiVvToNntPBoH58?p=preview
Just trying to test this idea out now, but the broadcast .on doesn't work. I think it's because the broadcast fires before the template loads. How can I fix this? Is this a good solution for what I'm doing?
If i have understood you correctly what you can try would be to create a template view specifically to create a new leg.
From the main controller implement some logic to show the template
$scope.addLeg=function() {
$scope.showAddLeg=true;
}
The AddLeg template view would load the controller and hence provide a mechanism to actually add new leg. The template would look like
<script type="text/ng-template" class="template" id="addLegTemplate">
<div ng-controller='LegsController'>
<!--html for adding a new leg-->
</div>
</script>
You can include this template inside you main html using ng-if + ng-include.
<div ng-if='showAddLeg'><div ng-include='addLegTemplate'/></div>
Basically you can create multiple view and bind to same controller (but instance would differ). In this case the LegsController can be binded to multiple views to support the complete functionality.
I like this data driven scenario, just manipulate the rows in the templates:
$scope.templates = [
{ name: 'include1.html', url: 'partials/include1.html' }
];
$scope.addTemplate = function(name, url) {
$scope.templates.push({ name: name, url: url });
};
and the markup:
<div ng-repeat="template in templates" ng-include="template.url"></div>
To add or remove views, just modify the templates et voila! If you need more controller code, then you can include a link to the script in the include. I guess there may be complications with binding to data in the partial view itself, but $compile should resolve those.
I've forked your plunkr demo into one that I believe does what you're looking for: http://plnkr.co/edit/whsjBT?p=preview
This demonstrates an event being broadcast from one controller to another AFTER the 2nd controller (LegCtrl in our example here) is loaded via ng-include and passing it data.
I used $includeContentLoaded event from ng-include to delay broadcasting the event until angular reports that add_leg.html is loaded. Also I think there were some issues with how you were using $broadcast()... it's first parameter should be the same as the one used in $on() in LegCtrl
Another idea to consider is simply using your Navigation service itself to share state between the controllers if that's appropriate in your scenario.

Router and refresh multiples ng-inludes

I start with code:
when('/admin', {
templateUrl: 'partials/admin/layout.html',
controller: AdminCtrl
})
when('/admin/products', {
templateUrl: '????',
controller: AdminProductsCtrl
})
Template "tree":
index.html ---> <div ng-view/>
---layout.html ---> <div ng-include=menu/> and <div ng-include=body/>
------menu.html
------products.html
Actually I do this:
function AdminCtrl($scope) {
$scope.menu = 'partials/admin/menu.html';
}
function AdminProductsCtrl($scope) {
$scope.menu = 'partials/admin/menu.html';
$scope.body = 'partials/admin/products/index.html';
}
The point is: What I put in '????', if I put layout.html this work fine, but I like just "refresh" ng-include=body. I think that my concepts about Angularjs is wrong.
Other problem is, when AdminProductsCtrl "take the control" of layout.html I miss the AdminCtrl $scope, this implicates repeat all AdminCtrl $scope in AdminProductsCtrl $scope (for example $scope.menu).
Thanks a lot, and sorry for "my english".
UPDATE
After think.. and think... I understanding that routes not apply for my app, then I manage all functionality under one url 'site.com/#/admin'. The menu.html is manage for AdminMenuCtrl, this controller contains a model for each 'ng-include' and contains one method for each menu entry. When the user click a menu entry, the associate method in the $scope replace $scope.includes.body with the 'new' html. The partial cointains your ng-controller.
This works fine by now :D. And the best is that I don't need use $rootScope.
The new problem is a bit more complicated, the ng-include require a tag (i.e DIV) and ng-controller too. Then my design is affected for this. In code language:
DESING:
<div>MENU-HTML</div>
<div>BODY-HTML</div>
TEMPLATE:
<div ng-include="menu"></div>
<div ng-include="body"></div>
AFTER RETRIEVE PARTIALS:
<div ng-include="menu"><div ng-controller="MenuCtrl">MENU-HTML</div></div>
<div ng-include="body"><div ng-controller="ListProductsCtrl">BODY-HTML</div></div>
THE IDEAL THING:
1 - ng-include don't 'include' into the DIV, instead 'replace' the DIV.
2 - ng-controller DIV is replaced for nothing in the DOM.
It's possible now with angular? Is a bad approach this idea? The point 2 with $route is possible, not with ng-controller directive.
I believe you are correct in your example you would set ???? to layout.html but the idea is to have different views based on the route so pointing to the same layout.html is not ideal.
If you are trying to keep a static menu on all pages I would add the menu to your index.html and then choose a different templateUrl for each route (ie /admin goes to partials/admin.html and /admin/products goes to partials/products.html) and not use the ngInclude.
I'm new to AngularJS but I'm getting the impression that you generally want to use ngView with routes to templateUrls OR use ngInclude (possibly with ngSwitch) if you want to roll your own view switching. I'm sure there are times when using both is appropriate but as a newbie it confuses me somewhat. Resident experts please correct me if I'm wrong!
For your second issue there might be some helpful information here and here for tips on sharing the same model across multiple controllers but you probably don't need to for your example.
An alternative is to use a string constant path to your partial in layout.html and remove the references to $scope.menu in your controller code by using:
<div ng-include="'partials/admin/menu.html'"/>

Resources