Below is how my angular app looks like.
(function () {
"use strict";
angular
.module("app.core", ['ngRoute']);
}());
Service
(function () {
'use strict';
angular
.module('app.service')
.factory('dataService', dataService);
dataService.$inject = ['$http', '$q'];
function dataService($http, $q) {
var service = {
create: create,
read: read,
update: update,
remove: remove
}
return service;
function read() {
return
$http.get("APIURL")
.then(success)
.catch(exception);
function success(response) {
}
function exception(ex) {
}
}
function create() {}
function update() {}
function detete() {}
}
})`
index.html
<body ng-app="app.core">
<div ng-view> </div>
</div>
On page load, home-template.html is inserted into ng-view.
No clue on below
what would be the right way to call only dataService's read() on page
load?
The normal use case is to call the service in the constructor of your controller.
John Papa's style guide has a lot of detailed guidance on this type of architecture and best practices. I'd highly recommend it.
https://github.com/johnpapa/angular-styleguide/tree/master/a1
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
We have separate file for each controller and lazy loaded when required.. They have corresponding entry in application.js.
Now the problem is :
I need 2-3 child controllers all linked to a parent controller.. and all are there in a single file.. so that they can be loaded..
Tried :
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
function myCtrl1($scope,$http){};
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
but, dosen't seems to be working.
UPDATE ----
Parent --
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
With another controller :
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
return myCtrl;
});
function myCtrl1($scope,$http){
};
This is working .. not sure they have parent child relationship or not... confused !
You can go the other way.
it is possible to extend a controller or make a single controller a mixin of multiple controllers.
module.controller('CtrlChild', ['$scope', '$controller', function ($scope, $controller) {
// Initialize the super class and extend it.
angular.extend(this, $controller('CtrlParent', {$scope: $scope}));
… Additional extensions to create a mixin.
}]);
In a Angular App i am using ui-router to handle navigation etc.
In a separate script file, i have a function like so;
$(function () {
function doSomething(){
if ($('.thisclass').length) {
$('.thisclass').css({ 'height': someHeight });
}
}
});
My problem is, that whenever the state changes, i want to run the above function. But as it is not part af any Angular function, i get an error when i reference it, as i cannot find it.
What should i be doing, instead of the above?
Hello you can also add your jquery code into the onEnter:function() of your state , as onEnter is executed each time you change the state and a controller is loaded.
example(a login state):
.state('login', {
url: '/login',
controller: 'LoginCtrl',
templateUrl: '/assets/modules/login/login.html',
resolve: {
user: ['authService', '$q', function (authService, $q) {
if (authService.user) {
return $q.reject({authorized: true});
}
}]
},
onEnter: function () {
//i hide header tabs, you can add your code here
$('.container-fluid').css('display', 'none');
},
onExit: function () {
//onExit is executed when we leave that state and go to another
}
});
Hope helps, good luck.
Here is how I would do.
app.js
(function(){
angular.module('app',[]);
/* other code like configuration etc */
})();
SomeService.js
(function(){
angular.module('app');
.factory('someService',function(){
return {
doSomething: function(){
$('.container-fluid').css('display', 'none');
}
};
});
})();
app.run.js
(function(){
angular.module('app')
//Inject your service here
.run(function($rootScope,someService){
//Look for successful state change.
//For your ref. on other events.
//https://github.com/angular-ui/ui-router/wiki#state-change-events
$rootScope.$on('$stateChangeSuccess', function() {
//If you don't wanna create the service, you can directly write
// your function here.
someService.doSomething();
});
})
})();
Always wrap your angular code within IIFE it wraps everything in closure and prevents leaks as well as provides a layer of security.
Hope this helps!
If you are the one controlling the state changes via $state.go() for example, you can amend it:
$state.go('somewhere', { 'place': 'somewhere' }).then(() => {
// write your function here
});
I have two AngularJS services:
service1.js
'use strict';
angular.module('myModule', [])
.factory('myService1', function() {
return {
myFunction: function(options) {
if (options) {
// do stuff
}
}
};
});
service2.js
'use strict';
angular.module('myModule', [])
.factory('myService2', function() {
return {
myFunction: function(options) {
if (options) {
// do stuff
}
}
};
});
I learned the hard way from the official docs that this approach will basically remove myService1 because the module gets overwritten. My question is, do I have any options here? I would really like to be able to have my services defined in separate files. Yet, I only want one module.
Thank you for any insights.
You have to define the module just once:
angular.module('myModule', []);
And then use it as many times as you need it without setting the dependencies:
angular.module('myModule');
So either you define the module in a separate file, or if you define it in the first service, the second should look like this:
angular.module('myModule')
.factory('myService2', function() { ... });
I'm beginning a large scale javascript application with Marionette. A Marionette application has a concept of application Modules and RequireJS is also used to break code into modules,
currently I have this for the start of my application:
require([ "jquery", "underscore", "backbone", "marionette" ],
function ($, _, Backbone, Marionette) {
$(function() {
App = new Marionette.Application();
App.addInitializer(function(options) {
App.addRegions({
mainArea: "#mainArea"
});
});
App.on("start", function() {
// done starting up, do stuff here
});
App.start();
});
});
If I wanted to add a view would I do something like the following in a file?
require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {
App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
return Marionette.ItemView.extend({
//define view stuff in here
});
});
});
I'm not sure how I'd get this code to actually run, any help is much appreciated
Marionette's modules are meant to be a simple alternative to RequireJS (and other) module formats. I would not recommend using them together, as noted in the wiki:
https://github.com/marionettejs/backbone.marionette/wiki/AMD-Modules-vs-Marionette's-Modules
IMHO I like to differ from the view point stated above "Marionette's modules are meant to be a simple alternative to RequireJS (and other) module formats."
I like to draw a comparison between Require.js modules and Marionette.js modules with C#'s assembly and namespace concepts. Marionette.js's modules help us group definitions of various building blocks based on functionality, while Require.js could be used to load / inject dependencies.
Again, this is my view / understanding (based on discussions with David Sulc on his book 'Structuring Backbone Code with RequireJS and Marionette Modules'), which has helped in my implementation. In a way we can use Marionette.js and Require.js together as described below.
The example below is a small Library Manager app (sample) which could be found online # https://github.com/srihari-sridharan/LibraryManagement. The code below (omitting insignificant bits and pieces) creates the application object and renders the list of books after initialization. Please find it here - https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/app.js
define([
'marionette',
'modules/config/marionette/regions/dialog'], function (Marionette) {
// Create the application object
var LibraryManager = new Marionette.Application();
// Add regions to the application object
LibraryManager.addRegions({
//Header
headerRegion: "#header-region",
//Main
mainRegion: "#main-region",
//Footer
footerRegion: "footer-region",
//Overlay Dialog
dialogRegion: Marionette.Region.Dialog.extend({
el:"#dialog-region"
})
});
// Subscribe to Initialize After event.
LibraryManager.on('initialize:after', function() {
if(Backbone.history){
require(['modules/books/booksModule', 'modules/about/aboutModule'], function (){
Backbone.history.start();
if(LibraryManager.getCurrentRoute() === ''){
LibraryManager.trigger("books:list");
}
});
}
});
// Return the application object.
return LibraryManager;
});
Next we define the module / sub-modules based on the functionality. This will also have a module specific router and will wire controllers and handle routes. Note the require call to controllers. This code is present in https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/booksModule.js
define(['app'], function (LibraryManager) {
// Define a new module for Books - BooksModule
LibraryManager.module('BooksModule', function (BooksModule, LibraryManager, Backbone, Marionette, $, _) {
BooksModule.startWithParent = false;
BooksModule.onStart = function () {
console.log('Starting BooksModule.');
};
BooksModule.onStop = function () {
console.log('Stopping BooksModule.');
};
});
// Define a new module for a Router specific to BooksModule
LibraryManager.module('Routers.BooksModule', function (BooksModuleRouter, LibraryManager, Backbone, Marionette, $, _) {
BooksModuleRouter.Router = Marionette.AppRouter.extend({
appRoutes: {
'books': 'listBooks',
'books(?filter:=criterion)': 'listBooks',
'books/:id': 'showBook',
'books/:id/edit': 'editBook'
}
});
var executeAction = function (action, arg) {
LibraryManager.startSubModule('BooksModule');
action(arg);
LibraryManager.execute('set:active:header', 'books');
};
var API = {
// This is where we are using / referring to our controller
listBooks: function (criterion) {
require(['modules/books/list/listController'], function (ListController) {
executeAction(ListController.listBooks, criterion);
});
},
showBook: function (id) {
require(['modules/books/show/showController'], function (ShowController){
executeAction(ShowController.showBook, id);
});
},
editBook: function (id) {
require(['modules/books/edit/editController'], function (EditController) {
executeAction(EditController.editBook, id);
});
}
};
// Navigating routes.
LibraryManager.on('books:list', function () {
LibraryManager.navigate('books');
API.listBooks();
});
LibraryManager.on('books:filter', function(criterion) {
if(criterion){
LibraryManager.navigate('books?filter=' + criterion);
}
else{
LibraryManager.navigate('books');
}
});
LibraryManager.on('book:show', function (id) {
LibraryManager.navigate('books/' + id);
API.showBook(id);
});
LibraryManager.on("book:edit", function(id){
LibraryManager.navigate('books/' + id + '/edit');
API.editBook(id);
});
LibraryManager.addInitializer(function () {
new BooksModuleRouter.Router({
controller: API
});
});
});
return LibraryManager.BooksModuleRouter;
});
Finally we have the definitions for our views, models and controllers. These definitions will be tied to module / sub module objects.
The view code is shown below. Look at the .extend() methods. They are assigned to variables attached to the BooksModule.List.View sub module. https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/list/listView.js
define(['app',
'tpl!modules/books/list/templates/layout.html',
'tpl!modules/books/list/templates/panel.html',
'tpl!modules/books/list/templates/none.html',
'tpl!modules/books/list/templates/list.html',
'tpl!modules/books/list/templates/listItem.html'],
function (LibraryManager, layoutTemplate, panelTemplate, noneTemplate, listTemplate, listItemTemplate) {
LibraryManager.module('BooksModule.List.View', function(View, LibraryManager, Backbone, Marionette, $, _) {
View.Layout = Marionette.Layout.extend({
template: layoutTemplate,
regions:{
panelRegion: '#panel-region',
booksRegion: '#books-region'
}
});
View.Panel = Marionette.ItemView.extend({
// More code here!
});
View.Book = Marionette.ItemView.extend({
// More code here!
});
var NoBooksView = Marionette.ItemView.extend({
template: noneTemplate,
tagName: "tr",
className: "alert"
});
View.Books = Marionette.CompositeView.extend({
// More code here!
});
});
return LibraryManager.BooksModule.List.View; // Return the definition.
});
The controller code is shown below. This gets called from the code in booksModule.js. The controller definition is attached to BooksModule.List sub module.
define(['app', 'modules/books/list/listView'], function (LibraryManager, View) {
LibraryManager.module('BooksModule.List', function (List, LibraryManager, Backbone, Marionette, $, _) {
List.Controller = {
listBooks: function (criterion) {
require(['common/views', 'entities/book'], function (CommonViews) {
var loadingView = new CommonViews.Loading();
LibraryManager.mainRegion.show(loadingView);
var fetchingBooks = LibraryManager.request('book:entities');
var booksListLayout = new View.Layout();
var booksListPanel = new View.Panel();
require(['entities/common'], function (FilteredCollection) {
$.when(fetchingBooks).done(function (books) {
// More code here!
});
if(criterion){
filteredBooks.filter(criterion);
booksListPanel.once('show', function () {
booksListPanel.triggerMethod("set:filter:criterion", criterion);
});
}
var booksListView = new View.Books({
collection: filteredBooks
});
booksListPanel.on('books:filter', function (filterCriterion) {
filteredBooks.filter(filterCriterion);
LibraryManager.trigger("books:filter", filterCriterion);
});
booksListLayout.on("show", function(){
booksListLayout.panelRegion.show(booksListPanel);
booksListLayout.booksRegion.show(booksListView);
});
booksListPanel.on('book:new', function () {
require(["modules/books/new/newView"], function (NewView) {
// More code here!
});
LibraryManager.dialogRegion.show(view);
});
});
booksListView.on('itemview:book:show', function (childView, model) {
LibraryManager.trigger("book:show", model.get('id'));
});
booksListView.on('itemview:book:edit', function(childView, model) {
require(['modules/books/edit/editView'], function (EditView) {
// More code here!
LibraryManager.dialogRegion.show(view);
});
});
booksListView.on("itemview:book:delete", function (childView, model) {
model.destroy();
});
LibraryManager.mainRegion.show(booksListLayout);
});
});
});
}
}
});
return LibraryManager.BooksModule.List.Controller; // Return the definition.
});
Thus require.js modules and marionette modules can coexist. The following are the advantages.
Much cleaner organization of source code and clearer separation of concerns.
Module start and stop methods provide provision to initialize and cleanup objects.
When you model functionalities and sub-functionalities as modules and sub modules, we have more granular control over what resides in memory and what should not.
Also, module definition can be split across multiple files.
Please post your thoughts. Thanks for reading.
PS: Based on the above view point, please find the changes to your example below:
require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {
App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
FirstView.View = Marionette.ItemView.extend({
//define view stuff in here
});
return FirstView.View;
});
});