Is this the correct use for a Directive? - angularjs

I have been tasked with setting up a initial application structure for a large angular application, I came across a few blog posts that basically said everything should be a directive (which I mostly agree with) but I have a feeling I have took this idea too far..
what I have got is basically - when you navigate to portal ui-router will load the portal template from the templates folder, all that's inside that actual template is <portal-view></portal-view.. the portalView directive basically the entire view wrapped up in a directive.
Route
angular.module('portal').config([
'$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
$stateProvider.state('portal', {
url: "/",
templateUrl: "templates/portal.tpl.html"
});
}]);
portal.tpl.html
<div class="container">
<portal-view></portal-view>
</div>
portalView directive
angular.module('portal').directive('portalView', function() {
return {
controller: 'portalController',
controllerAs: 'vm',
templateUrl: "/directives/portalView/portalView.tpl.html"
}});
portalView folder
portalView
portalView.controller.js
portalView.js
portalView.less
portalView.tpl.html
In my head this seems a good idea but I can it becoming a chore when we start adding more view to the application but I am hoping some angular pro will tell me this is the best way to do it :)
Any help, advice and links would be much appreciated!

Componentizing the view and putting it into directive looks like a smart idea to me. This will ease the migration to another router also if it takes place.
portal.tpl.html is unnecessary if you plan to follow this practice, template: '<portal-view></portal-view>' would be enough. But don't reject it if you have plans on using the template for css and js asset loading (using the scripts in templates isn't straight-forward, but it is possible), it would fit the scheme quite well.

Related

"Header" controller which will always be present on top

Started learning AngularJS today, using webpack and ES6. I'm kind of stuck on a simple problem that I cannot figure out. I'm building a really simple SPA app, like a shopping cart app, which should have the "header" component with a couple of links to other pages and the sum and total of shopping cart.
One of these other controllers could be just a random dummy page, one could be the shopping cart summary, and the main controller would be where the user can actually add items to the shopping cart.
What I have thus far is just the following routes:
export default function routes($stateProvider) {
$stateProvider
.state('main', {
url: '/',
template: require('./main.html'),
controller: 'MainController',
controllerAs: 'main'
})
.state('other', {
url:'/other',
template: require('./other.html'),
controller: 'OtherController',
controllerAs: 'other',
})
}
index.js:
export default angular.module('app.main', [uirouter, productService])
.config(routing)
.controller('MainController', MainController)
.controller('OtherController', OtherController)
.name;
productService isn't relevant here, so I exclude that code.
Just as a test I tried to set the OtherController as a separate view in index.html:
<body>
<div ui-view="other"></div>
<div class="container" ui-view></div>
</body>
Kind of like suggested here: How to update partial of html using ES6 syntax with angular-webpack-seed
Also tried to use ng-controller="other" instead of ui-view="other", but that throws error:
The controller with the name 'other' is not registered
And I don't know where I would then register it...
Some places I read that you could use a directive for making a "header"/navbar, but that doesn't seem correct to me at all. Also tried that though, in this way: https://stackoverflow.com/a/33714913/6294072 Read about ng-include, but that wouldn't work, since I need the service for the shopping cart.
I am missing something fundamental here, but can't figure it out. My brain is also (unfortunately) wired on Angular (4), since I have some experience with that :)
Sorry for the noob question, I feel ashamed, but I have been struggling for this for hours now and therefore going crazy. Help is very much appreciated!
Use components instead of controllers with controllerAs. You'll find it much more similar to Angular 4. Then depending on how you want to do it, you can use either bindings or requires to create relationships between your components.
Since you have tight control over this, I would go with requires. Create a component with your navbar and stuff in it, then create child components and require the parent in them. We have a generic in-house framework for this stuff. The outer component we call the site, so we get a reference to it in our child components like this:
require: { site: '^f1Site' }
If you're not already doing so, use ui-router 1.0.x and use route to component to handle navigation & state.
Index.html just has <f1-site>Loading</f1-site> in it's body.
The f1-site template has basic layout stuff in it - including navbars and such that we've moved off into their own components - and then the ui-view directive is on the tag where we want to load all of dynamic components.

From 1 to 3 controllers in Angular

I'm new to angularJS so I'm still learning the "angular way" of doing things and therefore seek for advice.
I started building my login/register/forgotten_password views which now work perfectly fine. So the routing looks basically like this:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/login',
{
controller: 'loginCtrl',
templateUrl: 'views/login.html'
})
.when('/register',
{
controller: 'registerCtrl',
templateUrl: 'views/register.html'
})
.when('/dashboard',
{
controller: 'dashboardCtrl',
templateUrl: 'views/dashboard.html'
})
})
Now here's the problem and/or question I have :
After a successful login, you get redirected to the dashboard which gets loaded into ng-view. The dashboard is indeed the view but there should be more controllers and templates once you enter the app (sidebar, topbar, chat...).
How would you approach this ?
I also have a globalCtrl on the html element to handle other things, just in case that information might be helpful in any way.
You shouldn't think in controllers anymore. Rather think in "components". There are lots of articles on the internet on how to learn this (better) approach. Here is one to start: Refactoring Angular Apps to Component Style
tl;dr; Create a component (element directive) for every section/part of your view. In your case a <dashboard>, <sidebar>, <topbar>, ...
You can (and should) route to components! See this issue for more information. Here is a "real life" example on how to achieve this: https://github.com/ui-router/sample-app-ng1
This is the con giving of using $routeProvider in Angular JS. When you use a $routeProvider to define routes in angular JS, a single route can only point to a single view and there is no concept of nested views using $routeProvider.
And when you talk about having more than one template inside the view, you are talking about having nested views inside a single view. Unfortunately Angular's $routeProvider doesn't provide that. But now comes the ui-router, which replace the concept of route with states. You can define multiple sub states in a state and each sub state can point to different template. Look at the following link and follow simple steps to power up views and nested views in your app.
ui-router

when should i use more than one controller in my app?

So recently I started to learn angularJS and I'm working on a SPA project.
it is a game with several steps (the purpose of the game isn't important for the matter of the question)
At the moment i have a controller for each one of the steps, all of them are childs of a mainControlelr so in my html it looks something like:
<div class="container" ng-controller="mainCtrl">
<!--main container-->
<div class="row">
<!--center pane-->
<div id="centerPane" class="col-lg-8 center-block">
.
. some code...
.
<!--steps-->
<div ng-view id="form-views"></div>
.
. some code...
.
</div>
.
. some code...
.
</div>
after speaking with some of my friends, all of them told me that this isn't a good way to do it and i should do one of the following instead:
work with only one controller (for this exmaple, 'mainCtrl')
keep the code like this, but inject a specific service, to all of my controllers, which will hold all of my global vars and make sure the controllers are updating the service vars.
so basically my question is when, as a rule of thumb, one should consider using more than one controller in a SPA?
Thanks,
No, I would advise against putting all your code in one controller. This makes it very hard to understand, as a lot of business logic will be in one file, and it will be hard to unit-test.
First of all, business logic should be put in a service. Keep as much business logic out of the controller and put it in a service or factory.
Assuming you're using ngRoute (I see ng-view in your code) and the steps are one after the other, I would suggest to make full use of the ngRoute component. What this means is that every step in your application should have it's own route and companying controller. As previously stated, business logic that applies to multiple routes should be abstracted to a service or factory.
To make it more clear, the below code could be the configuration of the routes for every step.
.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/step-1', {
templateUrl: 'partials/step1.html',
controller: 'StepOneCtrl'
}).
when('/step-2', {
templateUrl: 'partials/step2.html',
controller: 'StepTwoCtrl'
}).
otherwise({
redirectTo: '/step-1'
});
}]);

AngularJS routing with ng-view relative to HTML page

I am quite new to Angular and now trying to make a simple routing with it. I have my landing page, currently called index2.html, containing some .js and .css includes and a div containing <ng-view></ng-view> where my content should go into.
My app.js looks like this:
var module1 = angular.module('module1', ['ngRoute']);
module1.config(function($routeProvider, $locationProvider){
$routeProvider.when("/",
{
templateUrl: "page1.html",
controller: "uiCtrl"
}
).when("/:param",
{
templateUrl: "page1.html",
controller: "uiCtrl"
}
).when("/transactions",
{
templateUrl: "page2.html",
controller: "uiCtrl"
});
});
But actually this does quite confusing things. Calling http://myurl.com/index2.html, the content of page1.html is properly loaded into the ng-view. So far, so good, but calling index2.html/123 gives my a Not Found instead of interpreting 123 as a parameter. I don't know why, but to make 123 a paremeter i have to call index2.html#123, which works, but then instantly updates the url to index2.html#/123.
Calling index2.html/transactions doesn't work at all. How can i call my /transactions route?
EDIT: If this is useful, i am using JQueryMobile as well in these pages.
I finally got what I want by getting the HTML5 mode work, which makes things so much easier.
After setting $locationProvider.html5Mode(true); I had to re-configure my webserver, what I wasn't able to manage until I found this nice guide: https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode
Now I can access each of my routes at the conventional way using a slash. Parameters can be passed with ? and &, as usual and I don't need to grapple with confusing hashtags and self-changing url's anymore.

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