My goal is to create a SPA using AngularJS.
I know concepts of AngularJS, however I lack experience with planning the whole project, since I have never created a real project with it.
The template is quite simple: there is a nav header at the top (that switch either user is loggedon or not) and the content of the site.
The header has 2 views (depending if user is loggedon or not) and container has many views (photo gallery, video gallery etc...)
There may be also a simple footer, working the same way as header.
1) Should I use 1 global controler for whole site or have a headerController and containerController ?
2) Have would these 2 controllers communicate ? (ex: header controller stores the username and password of a loggedon user) ?
Maybe someone could provide a simple stub of AngularJS arhcitecture for such a website ?
Regards
You can (and should) use a "global" controller, controlling the top level view and scope, and others controllers can control each component that have its own behavior.
<!DOCTYPE html>
<html data-ng-app="myapp">
<head>
<!-- ... -->
</head>
<body data-ng-controller="BodyCtrl">
<section id="navbar" data-ng-controller="NavbarCtrl">
<!-- ... -->
</section>
<section id="content" data-ng-controller="ContentCtrl">
<div data-ng-controller="FirtSubCtrl">
<!-- ... -->
</div>
<div data-ng-controller="SecondSubCtrl">
<!-- ... -->
</div>
</section>
</body>
</html>
Controllers can communicate between themselves using events, but it should be used in restricted situations. Instead, you should write some services (or factory, value, provider) to encapsulate the shared logic in your app (and shared objects). Then you can inject any service in each controller that need it.
angular.module('myapp',[]) // define the myapp module
.factory('myService', ['', function(){ // define a service to share objects and methods
var _myLogic = function(){
// ...
return ret;
};
var _myObject = {
prop1: "my first prop value",
prop2: "my second prop value"
};
return {
myLogic: _myLogic,
myObject: _myObject
};
}])
.controller('BodyCtrl', ['$scope', 'myService', function($scope, myService){
$scope.myScopeMethod = myService.myLogic;
$scope.myObject = myService.myObject;
}])
.controller('FirtSubCtrl', ['$scope', 'myService', function($scope, myService){
$scope.myScopeMethod = myService.myLogic;
$scope.myObject = myService.myObject;
}])
;
Here you can notice that two controllers can share the exact (or not) object or method, injecting the shared services.
Dealing with ng-view means dealing with templates :
Here you index :
<!-- index.html -->
<!DOCTYPE html>
<html data-ng-app="myapp">
<head>
<!-- ... -->
</head>
<body data-ng-controller="BodyCtrl">
<section id="navbar" data-ng-controller="NavbarCtrl">
<!-- ... -->
</section>
<ng-view></ng-view>
</body>
</html>
And your views :
<!-- template/contentIfLogged.html -->
<section id="contentIfLogged" data-ng-controller="ContentCtrl">
<div data-ng-controller="FirtSubCtrl">
<!-- ... -->
</div>
<div data-ng-controller="SecondSubCtrl">
<!-- ... -->
</div>
</section>
and
<!-- template/contentIfNOTLogged.html -->
<section id="contentIfNOTLogged" data-ng-controller="Content2Ctrl">
<div data-ng-controller="ThirdSubCtrl">
<!-- ... -->
</div>
<div data-ng-controller="FourthSubCtrl">
<!-- ... -->
</div>
</section>
Now, you have to configure your routes to enable the correct view, onevent or button click.
angular.module('myapp').config(function($routeProvider){
$routeProvider
.when('/notlogged',
{templateUrl: 'template/contentIfLogged.html'}
)
.when('/logged',
{templateUrl: 'template/contentIfNOTLogged.html'}
)
.otherwise({redirectTo: '/notlogged'})
});
Now, in your <section id="nav"> element, you can add these buttons :
<section id="navbar" data-ng-controller="NavbarCtrl">
<a class="btn" href="#logged">
Logged view
</a>
<a class="btn" href="#notlogged">
Not logged view
</a>
</section>
and then, switch between your view clicking it.
Or, programmaticaly, in your controller (or in a service), you can switch using the $location angular service :
.controller('NavbarCtrl', ['$scope', '$location', 'myService', function($scope, $location, myService){
$scope.myScopeMethod = myService.myLogic;
$scope.myObject = myService.myObject;
var login = function(){
$location.path('/logged');
};
var logout = function(){
$location.path('/notlogged');
};
}])
To you to fill the gaps to fit your application, but the base organization of your simple app is there.
Related
I am trying to create a SPA using angular ui-router with MVC. I am facing three issues.
1.The controllers OfferController.js and OfferCustomizationController.js are not registering.
2.I am getting the errors ' No reference point given for path '.offer', No reference point given for path '.customizations'.
3. Main view is rendering thrice.
I have no clue what am i missing. Please help.
I have included the following files in my Layout.cshtml:
<script src="#Links.Scripts.angular_min_js"></script>
<script src="#Links.Scripts.ProductCatalog.app_js"></script>
<script src="#Links.Scripts.ProductCatalog.service_js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-animate.min.js"></script>
I have made two views. Offers And OfferCustomizations. And a main view. Following is my code for the main view:
<div class="inside-data-div" ng-app="ProductCatalog">
#*<div ui-view></div>*#
<div class="col-lg-12">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div id="form-container">
<div class="page-header text-center">
<h2>Create Offer</h2>
<!-- the links to our nested states using relative paths -->
<!-- add the active class if the state matches our ui-sref -->
<div id="status-buttons" class="text-center">
<a ui-sref-active="active" ui-sref=".offer"><span>1</span> Offer Details</a>
<a ui-sref-active="active" ui-sref=".customizations"><span>2</span> Offer Customizations</a>
</div>
</div>
<!-- use ng-submit to catch the form submission and use our Angular function -->
<form id="signup-form" ng-submit="SaveForm()">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
<!-- show our formData as it is being typed -->
<pre>
{{ formData }}
</pre>
</div>
</div>
</div>
</div>
Following is my code of app js:
var productCatalogApp = angular.module('ProductCatalog', ['ui.router']);
productCatalogApp.value('appName', 'Product Catalog');
productCatalogApp.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
// route to show our basic form (/form)
.state('wizard', {
url: '/wizard',
templateUrl: 'WizardSubForm',
controller: 'WizardMainController'
})
// nested states
// each of these sections will have their own view
// url will be nested (/form/profile)
.state('wizard.offer', {
url: '/offer',
templateUrl: 'OfferForm',
controller: 'OfferCtrlr'
})
// url will be /form/interests
.state('wizard.customizations', {
url: '/customizations',
templateUrl: 'OfferCustomizations',
controller: 'CustomizationCtrlr'
});
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/wizard/offer');
});
productCatalogApp.controller('WizardMainController', function ($scope) {
// we will store all of our form data in this object
$scope.formData = {};
// function to process the form
$scope.SaveForm = function () {
alert('awesome!');
$scope.formData = { "Offer": $scope.offer, QuestionGroups: $scope.QuestionGroups }
};
});
This is how i'm registering my controllers. Below is the one for offercontroller.
var app = angular.module('ProductCatalog.controllers', []);
app.controller('OfferCtrlr', function ($scope, $http, $modal) {
});
Any help will be appreciated.
I have a bit of a complicated MVC project that we are adding some Angular capability to. I am very new to Angular and have watch three Angular courses on Pluralsight now. They have been very helpful but I am missing some piece of understanding.
I understand that I need to have just one ng-app (unless I want to bootstrap and I don't think I want to). So this app has defined my config and a custom directive. The custom directive is working nicely but when I click on a link in that directive it does not go anywhere....well perhaps it does but not where I am expecting. Fiddler shows nothing unusual like 404 or 500 errors.
(function ()
{
"use-strict";
angular.module("appMainNav", ["ngRoute"])
.config(function ($routeProvider) {
$routeProvider.when("/MySearch", {
controller: "routingController"
});
$routeProvider.otherwise({ redirectTo: "/" });
})
})();
My routingController:
(function () {
"use strict";
angular.module("appMainNav")
.controller("routingController", routingController);
function routingController($http) {
var vm = this;
alert("Routed");
}
})();
My HTML link as rendered:
Search
I don't get a page not found...the url just appends the #/MySearch. No console errors etc.
I was expecting to get a javascript alert...?
TIA
Edit
Should have added sooner. Here is markup:
_Layout page:
<body>
<div ng-app="appMainNav" ng-controller="routingController" class="container">
#RenderBody()
</div>
</body>
RenderBody is Index.cshtml:
<div class="row">
<div>
Header Stuff
</div>
</div>
<div class="row">
<div class="col-md-2">
<dashboard-main-nav></dashboard-main-nav>
</div>
<div class="col-md-3">
<div></div>
</div>
<div class="col-md-8">
<div></div>
</div>
</div>
<div class="row">
<div>
Footer Stuff
</div>
</div>
The custom directive angular code was left out of the snips above however that is where the link is generated.
You need to assign an event handler to a click event on the anchor tag:
Search
where foo is a function defined on the controller's scope:
function routingController($http, $scope) {
var vm = this;
$scope.foo = function(){
alert("I am so tired");
}
alert("Routed");
}
Try that and it should work.
I'm trying to add the wysiwyg nicEdit text editor to my text areas. When I goto the actual templateUrl the textbox and tool bar do work but they do not submit correctly (to my firebase db). When I goto the page that is to render the template I am unable to get get nicEdit toolbar features and just get a regular text area box. I'm using angularjs and have the templateurl as addPost.html.
So again the template does render when I goto #/addPost but not with the working nicEdit features, yet going directly to the template url addPost.html does have the nicEdit features working but then won't submit to my firebase db. Anyone have an idea on why this is so? Thanks.
Template file addPost.html:
<head>
<script type="text/javascript" language="javascript" src="../nicEdit.js"></script>
<script type="text/javascript" language="javascript">
bkLib.onDomLoaded(nicEditors.allTextAreas);
</script>
<!-- Bootstrap core CSS -->
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
<nav class="blog-nav">
<a class="blog-nav-item " href="#/welcome">Home</a>
<a class="blog-nav-item active" href="#/addPost">Add Post</a>
<a class="blog-nav-item " style="cursor:pointer;" ng-click="logout();">Logout</a>
</nav>
</head>
<body ng-controller="AddPostCtrl">
<div class="container" >
<form class="form-horizontal" ng-submit="AddPost()">
<fieldset>
<!-- Form Name -->
<legend>Create Post</legend>
<!-- Textarea -->
<div class="form-group">
<label class="col-md-4 control-label" for="txtPost">Post</label>
<div class="col-md-4">
<textarea class="form-control" id="txtPost" ng-model="article.post" name="txtPost" ></textarea>
</div>
</div>
</fieldset>
</form>
</div><!-- /.container -->
</body>
addPost.js
'use strict';
angular.module('myApp.addPost', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/addPost', {
templateUrl: 'addPost/addPost.html',
controller: 'AddPostCtrl'
});
}])
.controller('AddPostCtrl', ['$scope','$firebase','$location','CommonProp',function($scope,$firebase, $location,CommonProp) {
if(!CommonProp.getUser()){
$location.path('/main');
}
$scope.logout = function(){
CommonProp.logoutUser();
}
$scope.AddPost = function(){
var title = $scope.article.title;
var date = $scope.article.date;
var post = $scope.article.post;
var firebaseObj = new Firebase("http://..");
var fb = $firebase(firebaseObj);
fb.$push({ title: title, date: date, post: post,emailId: CommonProp.getUser() }).then(function(ref) {
console.log(ref);
$location.path('/welcome');
}, function(error) {
console.log("Error:", error);
});
}
}]);
Going to #addPost shows the template with out nicEdit working
But going to the actual templateUrl addPost.html it works fine, minus not being able to submit
The problem has to do with trying to run scripts Angular html partials. The simple solution is to move the scripts you need to the head of your main index file, outside of ng-view, though it does seem (according to other stackoverflow posts) technically possibly to try to get these scripts to execute:
"AngularJS: How to make angular load script inside ng-include?"
https://stackoverflow.com/a/19715343/1078450
(Also, you have html in your head file that is not likely to be rendered: <nav class="blog-nav">)
Well, I have had the same problem, with initialisation of NicEdit in templates.
First I used onDomLoaded() else, but it's better to wait for the document. With jQuery I use document.ready.
Take a look here, pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it
The problem is to tell NicEditor the new textarea.
Here a code sample to do this in jQuery.
var idnr = 0;
var NicEditor = new nicEditor();
$('#add').click(function(){
var $clone = $('#dummy').clone();
var id = 't_' + idnr;
idnr = idnr + 1;
$clone.attr('id',id).attr('name',id).removeClass('dummy');
$('#wrapper').append($clone);
NicEditor.panelInstance(id);
$(nicEditors.findEditor(id).elm).focus();
});
And here is a working Example of dynamic NicEdit use.
I am trying to make a simple project where i want to populate the section with ng-view directive and i keep getting the following error:
I also included in index.html the angular files:
1-angular min js
2-angular-route min js
3-angular-resource min js
Error: $injector:modulerr Module Error Failed to instantiate module
booksInventoryApp due to: Error: [$injector:modulerr]
How can i fix this?
My code is:
index.html
<!DOCTYPE html>
<html ng-app="booksInventoryApp">
<body>
<section ng-view></section>
<script src="js/index.js"></script>
</body>
</html>
index.js
var app = angular.module('booksInventoryApp', ['booksInventoryApp.bsm','booksInventoryApp.allBooks']);
//route provider
app.config(['$routeProvider', function($routeProvider){
$routeProvider
// route for the index page
.when('/', {
templateUrl : '../allBooks.html',
controller : 'booksCtrl'
})
// route for the best selling month page
.when('/bsm/:id', {
templateUrl : 'bsm.html',
controller : 'bsmCtrl'
})
// route for the root
.otherwise({
redirectTo : '/'
});
}]);
bsm.js
var app = angular.module('booksInventoryApp.bsm', []);
app.controller('bsmCtrl', function($scope) {
$scope.book = "Bla Bla";
});
bsm.html
<section class="container">
{{book}}
</section>
allBooks.js
var app = angular.module('booksInventoryApp.allBooks', []);
// controllers
app.controller('booksCtrl', function($scope, $http) {
$http.get("https://whispering-woodland-9020.herokuapp.com/getAllBooks")
.success(function(data) {
$scope.data = data;
});
});
allBooks.html
<section class="row">
<section class="col-sm-6 col-md-2" ng-repeat="book in data.books">
<section class="thumbnail">
<img ng-src="{{book.url}}">
<section class="caption">
<h3>{{book.name}}</h3>
<p>Author: {{book.author}}</p>
<p>ID: <span class="badge">{{book.id}}</span></p>
<p>Available: <span class="badge">{{book.amount}}</span></p>
<p>Price: <span class="badge">${{book.price}}</span></p>
<p><a ng-src="#/bsm/{{book.id}}"><button class="btn btn-info">Best selling month</button></a></p>
</section>
</section>
</section>
</section>
You need to add ngRoute module in your app and also the script reference of the angular-route.min.js write after the angular.js, Also you need to add bsm.js and allBooks.js in your html after above two mentioned file has loaded.
Code
var app = angular.module('booksInventoryApp', [
'booksInventoryApp.bsm',
'booksInventoryApp.allBooks',
'ngRoute'
]);
Note
Both the version of angular.js & angular.route.js should be the
same otherwise it will show some wierd issue. Preferred version is 1.3.15
In your index.html page you not included bsm.js and allBooks.js files which contains the required dependencies of your app.
Since you have specified the dependency of 'booksInventoryApp.bsm','booksInventoryApp.allBooks' in your app angular is not able to find those modules and hence you are getting that error.
Also you need to include angular route script reference and ngRoute in your dependencies because you are using angular routing in your app.
var app = angular.module('booksInventoryApp', ['ngRoute', 'booksInventoryApp.bsm', 'booksInventoryApp.allBooks']);
Suppose you are using routes:
// bootstrap
myApp.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when('/about', {
templateUrl: 'partials/about.html',
controller: 'AboutCtrl'
});
...
And in your html, you want to navigate to the about page when a button is clicked. One way would be
<a href="#/about">
... but it seems ng-click would be useful here too.
Is that assumption correct? That ng-click be used instead of anchor?
If so, how would that work? IE:
<div ng-click="/about">
Routes monitor the $location service and respond to changes in URL (typically through the hash). To "activate" a route, you simply change the URL. The easiest way to do that is with anchor tags.
Go Home
Go to About
Nothing more complicated is needed. If, however, you must do this from code, the proper way is by using the $location service:
$scope.go = function ( path ) {
$location.path( path );
};
Which, for example, a button could trigger:
<button ng-click="go('/home')"></button>
Here's a great tip that nobody mentioned. In the controller that the function is within, you need to include the location provider:
app.controller('SlideController', ['$scope', '$location',function($scope, $location){
$scope.goNext = function (hash) {
$location.path(hash);
}
;]);
<!--the code to call it from within the partial:---> <div ng-click='goNext("/page2")'>next page</div>
Using a custom attribute (implemented with a directive) is perhaps the cleanest way. Here's my version, based on #Josh and #sean's suggestions.
angular.module('mymodule', [])
// Click to navigate
// similar to <a href="#/partial"> but hash is not required,
// e.g. <div click-link="/partial">
.directive('clickLink', ['$location', function($location) {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(function() {
$location.path(attrs.clickLink);
});
});
}
}
}]);
It has some useful features, but I'm new to Angular so there's probably room for improvement.
Remember that if you use ng-click for routing you will not be able to right-click the element and choose 'open in new tab' or ctrl clicking the link. I try to use ng-href when in comes to navigation. ng-click is better to use on buttons for operations or visual effects like collapse.
But
About
I would not recommend. If you change the route you might need to change in a lot of placed in the application. Have a method returning the link. ex:
About. This method you place in a utility
I used ng-click directive to call a function, while requesting route templateUrl, to decide which <div> has to be show or hide inside route templateUrl page or for different scenarios.
AngularJS 1.6.9
Lets see an example, when in routing page, I need either the add <div> or the edit <div>, which I control using the parent controller models $scope.addProduct and $scope.editProduct boolean.
RoutingTesting.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Testing</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-route.min.js"></script>
<script>
var app = angular.module("MyApp", ["ngRoute"]);
app.config(function($routeProvider){
$routeProvider
.when("/TestingPage", {
templateUrl: "TestingPage.html"
});
});
app.controller("HomeController", function($scope, $location){
$scope.init = function(){
$scope.addProduct = false;
$scope.editProduct = false;
}
$scope.productOperation = function(operationType, productId){
$scope.addProduct = false;
$scope.editProduct = false;
if(operationType === "add"){
$scope.addProduct = true;
console.log("Add productOperation requested...");
}else if(operationType === "edit"){
$scope.editProduct = true;
console.log("Edit productOperation requested : " + productId);
}
//*************** VERY IMPORTANT NOTE ***************
//comment this $location.path("..."); line, when using <a> anchor tags,
//only useful when <a> below given are commented, and using <input> controls
$location.path("TestingPage");
};
});
</script>
</head>
<body ng-app="MyApp" ng-controller="HomeController">
<div ng-init="init()">
<!-- Either use <a>anchor tag or input type=button -->
<!--Add Product-->
<!--<br><br>-->
<!--Edit Product-->
<input type="button" ng-click="productOperation('add', -1)" value="Add Product"/>
<br><br>
<input type="button" ng-click="productOperation('edit', 10)" value="Edit Product"/>
<pre>addProduct : {{addProduct}}</pre>
<pre>editProduct : {{editProduct}}</pre>
<ng-view></ng-view>
</div>
</body>
</html>
TestingPage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.productOperation{
position:fixed;
top: 50%;
left: 50%;
width:30em;
height:18em;
margin-left: -15em; /*set to a negative number 1/2 of your width*/
margin-top: -9em; /*set to a negative number 1/2 of your height*/
border: 1px solid #ccc;
background: yellow;
}
</style>
</head>
<body>
<div class="productOperation" >
<div ng-show="addProduct">
<h2 >Add Product enabled</h2>
</div>
<div ng-show="editProduct">
<h2>Edit Product enabled</h2>
</div>
</div>
</body>
</html>
both pages -
RoutingTesting.html(parent), TestingPage.html(routing page) are in the same directory,
Hope this will help someone.
Another solution but without using ng-click which still works even for other tags than <a>:
<tr [routerLink]="['/about']">
This way you can also pass parameters to your route: https://stackoverflow.com/a/40045556/838494
(This is my first day with angular. Gentle feedback is welcome)
You can use:
<a ng-href="#/about">About</a>
If you want some dynamic variable inside href you can do like this way:
<a ng-href="{{link + 123}}">Link to 123</a>
Where link is Angular scope variable.
just do it as follows
in your html write:
<button ng-click="going()">goto</button>
And in your controller, add $state as follows:
.controller('homeCTRL', function($scope, **$state**) {
$scope.going = function(){
$state.go('your route');
}
})