I have a login page, which is just a simple form in the center of the page with no header, footer or sidebar. My regular pages will have a sidebar, header and footer (these 3 are directives). I call all my templates in ng-view, depending on the routes.
I want to place my directives outside ng-view since they'll be common across all the pages except login and I don't want them to be fetched on every URL change.
How do I have a login page without the 3 directives?
I created a small plunk to offer you a solution to your issue.
this is the base of the solution:
<ng-include src="appVm.templatetoShow">
</ng-include>
in the Appcontroller :
function AppController() {
this.templatetoShow = 'login.html';
}
I included a couple of templates that gets loaded when needed.
After some rereading, I made a new plunk to show you how this can be done.
The hiding part is simple, just add some CSS into the mix, and you won't see
anything from your main page.
Added some cloaking to prevent FOUC, and wrote a couple of lines to show
an authentication system.
I introduced an User service that you can inject wherever you need the users
data.
Is this more in what you wanted?
I dont know if I get what You really want to do, but i would suggest to use ng-if
such ng-if may look look like this:
<div ng-if="notLogin"> here is Your navbar contents. Directies, anything You need </div>
and then in controller -
$scope.notLogin = $location.path() !== "/login"
If You have directives for footers, navbars etc, i think You can do that in directive controller and hide all content of directive there, so directive will be empty.
You may also use global controller to do that job and wrap those directives in divs like in my example. Doing it in directives looks cleaner though.
Hope its what You need.
you can use ng-if to conditionally display/hide them on UI. Please note that ng-if is different than ng-switch. The ngIf directive removes or recreates a portion of the DOM tree based on an {expression}. If the expression assigned to ngIf evaluates to a false value then the element is removed from the DOM.
More details - https://docs.angularjs.org/api/ng/directive/ngIf
Qucik Steps -
create a shell page and set the header, footer, sidebar and a view
use ng-if={{User.isLoggedin}} for header, footer, sidebar and login
set User.isLoggedin to false when user is not logged in
Set it to true once user loged in
Directives are shared across all views if you use the same controller however using different controllers is not adviced either. what makes you uncomfortable with that ?
have a look at this post:
https://stackoverflow.com/a/16213055/1218551
best wishes:)
Related
I want to avoid the latency in display of initial JavaScript-rendered views. I want the user to see content immediately and have Angular take it from there. I do not want to just replace this ng-view when Angular ngRoute kicks in as a blink will likely happen. I only want it to replace it once the user hits another route.
Let's imagine this is the base route '/'. This would already exist in my HTML, rendered from the server.
<div ng-view>
<h1>Welcome. I am the first view.</h1>
<p>Please do not replace me until a user has triggered another route.</p>
</div>
I know that a common approach is to have some server-side code in an ng-view and when Angular loads it just replaces it. This is not what I'm looking to do. I want Angular to load and understand that this is actually already my first view.
Any creative ideas as to how to do this? I've looked at the source code- no luck. Maybe even a way to have Angular only replace the HTML if it is different.
Edit:
I am not looking to render templates on the server-side for use as Angular templates. I am looking to render my entire index.html on the server-side, and that would already contain everything the user needs to see for this initial base route.
6-10 seconds on any mobile is quite bad. I wouldn't blame angular here, angular is only 30kb, if that is still too slow, you've chosen wrong framework for task.
Use profiling tools to understand what is going on.
How big is the application you're dealing with?
Can you split the application into sub-applications?
Are you doing minification already for CSS & JS?
Are you lazy loading all your views & controllers?
Are you compressing everything? (gzip)
Anyways, it is possible to do pre-processing on server-side for your index.html
You can do pre-processing using nodejs, for example, and cache the pre-processed index.html.
Your nodejs pre-processor could do (pseudo-code):
function preprocessIndexHtml(queryString) {
if(cached[queryString])) return cached[queryString];
// assume angular.js is loaded
// routeConfiguration is an object that holds controller & url.
var routeConfiguration = $routeProvider.
figureOutRouteConfigurationFor(queryString);
var domTree = $(file('index.html'));
var $rootScope = $injector.get('$rootScope');
// find ng-view and clone it
var yourNgView = $($("attribute[ng-view='']").outerHTML);
// le's get rid of existing ng-view attribute
// and configure new ng-view with templateUrl & controller.
yourNgView.RemoveNgViewAttribute();
yourNgView.AddAttribute("ng-controller", routeConfiguration.controller);
yourNgView.AddAttribute("ng-view", routeConfiguration.templateUrl);
// compile the view & pass the rootScope.
$compile(yourNgView)($rootScope);
// replace the existing dom element with our compiled variant
$("attribute[ng-view='']").replaceHtmlWith(yourNgView);
// we can now cache the resulted html.
return cached[queryString] = domTree.html;
}
ngCloak
The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.
https://docs.angularjs.org/api/ng/directive/ngCloak
<body ng-cloak>
You should read this article http://sc5.io/posts/how-to-implement-loaders-for-an-angularjs-app
The problem I am having is I need the root element as an anchor tag, then need a div under the anchor tag. They will both be using the same angular controller which belongs to the same app. The databind on vm.Open works find inside the anchor tag, but it is not working inside the div tag. How can I have the div tag also bootstrap as 'app' with the controller 'ordercontrol'?
Right now I have :
My HTML
<a data-ng-app="app" data-ng-controller="ordercontrol as vm" href="#" data-ng-click="vm.Open = !vm.Open">{{vm.Open}}</a>
<div data-ng-app="app" data-ng-controller="ordercontrol as vm" id="QuickOrderDiv">
<div class="row">
{{vm.Open}} //showing as '{{vm.Open}}' inside page
</div>
</div>
Per AngularJS documentation, only one application can be bootstrapped per HTML document. There is probably not a good reason for you to try and declare and use multiple apps in one document. Depending on your intention, there are a few ways to proceed.
First, remember: assigning a controller to an element (using the ng-controller directive) creates a new scope that inherits from the parent. All elements, directives, and additional controllers used within that scope can use that controller to share functionality. So in essence, a controller is used to centralize models and application logic that is specific to an application and usually to a view (think of any functional task in your application, such as an order form or log in page; those are serviced by controllers).
If you want to reuse a behavior multiple times throughout your application, and that behavior is not specific to a particular view in your application (think of a component, like a date picker, or perhaps a shopping cart status icon/link) you may wish to encapsulate your logic in a directive.
Now, there is some overlap (for example, directives can have a scope or controller of their own), and it may be confusing when to use one or the other. As I mentioned above, controllers are primarily intended to contain business logic for a view in your application. Directives are more orthogonal, encapsulated templates and logic that a) you can easily reuse across the views in your application, and b) extend existing HTML elements with richer mark-up and programmed behaviors. You can use a service (which is a single-instance object) to coordinate data between controllers and directives.
Another common issue new developers struggle with is maintaining or inheriting different states independently, considering that you can only have one ng-view element per document. In that case, consider ui-router.
My wild guess for your case, you may want some sort of QuickOrder directive that binds to a value on OrderController to determine whether or not it should display, and contains additional template mark-up for displaying the order or whatever and the logic to manage it.
I am trying to write a directive that will format content for modal display (using Bootstrap classes) if given a certain parameter, and as standard view if not. I have this working for a view loaded directly, toggling on a URL param (?modal) available to $routeParams and/or $location.
I want to use this toggle-able template as a "pipe" for other templates. However, the intended content URL will never be the visible URL when used as a modal. I can't get it working when loading the view with $modal.open or ngInclude, because $routeParams/$location has data for the including page, not the included one.
I put this in a Plunker, but because Plunker also doesn't provide the URL param, the modal view isn't available.
Does Angular provide a means to change the template or templateUrl much later in the process? For example, could I use $scope, either from a controller or on the directive, itself?
Clarification: The goal here is to have one template/partial for each component, with that template used either as a standalone or a modal, based on some switch. The _modal and _alone partials in the Plunker convert the component template into the desired state.
$modal.open takes a single object parameter one of the properties of this config parameter is templateUrl
http://angular-ui.github.io/bootstrap/
So you can create the config object and open the modal with any template you need.
Dan Wahlin uses this technique for a dialog service and then in this article goes on to demonstrate a full modal service
http://weblogs.asp.net/dwahlin/building-an-angularjs-modal-service
There were a couple of issues with your code:
First of all the use $routeParams if you don't use ngRoute's $routeProvider.
In your case, it might be easier to use $window.location.search (and compare it against ?modal).
That said, in order to properly display a Bootstrap modal, you need Bootstrap's JS file (which in turn requires jQuery) and you also need to create the modal (e.g. by calling $('.modal').modal()).
Take a look at this modified demo.
For better intergration with Angular's components, you might want to look into UI Bootstrap.
I'm using UI-Router to mange my routing on my application.
One item I've hit a snag on it with my menu which is the main view that the controllers will use.
for example I have a home link that should be
index.htm#/home/3
index.htm#/settings/3
3 is the companyId
but it seems that I can't access it that way. Does anyone have a suggestion as to how I could make that work?
Parent states should not be accessing the parameters of child states, but there is a little bit of magic that ui-router provides that might help. This is ui-sref-active. This directive will add css classes to html elements that have a ui-sref directive and that state is currently active. I think this would be sufficient to solve the problem that you have.
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.