Multiple ng-app directives on a page - javascript

I want to be able to use multiple ng-app="{angular.module}" directives on one page. I want to do this to make the bits of my app more modular. I figure, if I can create angular modules and plug several of them into one document, I could take those modules and plug them into other projects easily.
I have seen people say that you can only use one ng-app directive on your page... is this true? Is it most accurate to say, "one ng-app directive per view"?
I hope this is not the case, or if it is the case that there is still a best way to achieve a high degree of abstract modularity.
Here are my modules/apps and their controllers...
var searchModj = angular.module('searchModule', []);
var controllers = {};
controllers.SearchList = function ($scope){
$scope.coworkers = [
{name: 'Joe Bob', city: 'Ukrainia'},
{name: 'Adam Blobnovski', city: 'Logan' },
{name: 'Carlos Sanchez', city: 'Deerbushle'},
{name: 'Martin Kellerweller', city: 'Uptown'},
{name: 'John Doe', city: 'New York City'}
];
};
searchModj.controller(controllers);
var fruitModj = angular.module('fruiter', []);
controllers.SomeFruit = function ($scope) {
$scope.fruits = ['apple', 'banana', 'pear'];
};
fruitModj.controller(controllers);
Ok, now here is the relevant part of my markup...
<div ng-app="searchModule">
<div ng-controller="SearchList">
Name:
<br/>
<input type="text" ng-model="name" />
<br/>
<ul>
<li ng-repeat="coworker in coworkers | filter:name">{{ coworker.name }} - {{ coworker.city }}</li>
</ul>
<p>You are searching for <em>{{ name }}</em></p>
</div>
</div>
<div ng-app="fruiter">
<div ng-controller="SomeFruit">
<ul>
<li ng-repeat="fruit in fruits">{{ fruits }}</li>
</ul>
</div>
</div>
I think because it comes first in the document, my "searchModule" app works and the second app does not. When I comment out the first app, the second works. So it looks like I'm confirming my most unfortunate suspicions. Regardless... if this is the case, then what is the best approach I can bear in mind to make the functionality on my projects as modular as possible?

you only want one ng-app on a page, but you can insert your other modules as dependencies of the main ng-app module.
var app=angular.module('myNgAppName', ['searchModule']);
This will expose any directives , controllers etc you have in your 'searchModule'

The limitations of the ngApp directive is just that, limitations of the directive, not AngularJS itself. Angular allow you to associate modules with multiple elements in a page, it even allows you to associate more than one module with each element.
Referencing other modules from you module will work. Another approach that will work is using the angular.bootstrap() method. See: https://stackoverflow.com/a/18583329/984780
Finally you can create a directive that works like ngApp without it's limitations. It would work exactly the way it does in your markup code. That's what I did you can get the code here:
http://www.simplygoodcode.com/2014/04/angularjs-getting-around-ngapp-limitations-with-ngmodule/
The directive is called ngModule. Here's a code sample:
<!DOCTYPE html>
<html>
<head>
<script src="angular.js"></script>
<script src="angular.ng-modules.js"></script>
<script>
var moduleA = angular.module("MyModuleA", []);
moduleA.controller("MyControllerA", function($scope) {
$scope.name = "Bob A";
});
var moduleB = angular.module("MyModuleB", []);
moduleB.controller("MyControllerB", function($scope) {
$scope.name = "Steve B";
});
</script>
</head>
<body>
<div ng-modules="MyModuleA, MyModuleB">
<h1>Module A, B</h1>
<div ng-controller="MyControllerA">
{{name}}
</div>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
<div ng-module="MyModuleB">
<h1>Just Module B</h1>
<div ng-controller="MyControllerB">
{{name}}
</div>
</div>
</body>
</html>

Yes you only want one ng-app per page, but you can create other modules and declare them as dependencies of your main app.
var app=angular.module('appModule'); //resuable module
var app=angular.module('myApp', ['appModule']); //in the HTML ng-app="myApp"
So you can put re-usable stuff in appModule and use it in other projects. For example, I like to put my routing logic (i.e. re-routing users depending on their access levels) in a module that I reuse in other projects.
Note: You might want to look into $provider http://docs.angularjs.org/api/AUTO.$provide if you want to use data from your re-usable ("appModule") module inside the config method of your ng-app ("myApp") module.

Related

accessing global variables in angular scope

In http://www.w3schools.com/angular/tryit.asp?filename=try_ng_repeat_object
instead of
<div ng-app="" ng-init="names=[
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]">
Is there a syntax to refer to an external variable declared elsewhere in ng-init (and NOT declared in angularjs controller) as this one doesn't work:
<script>
var myNames = [
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]
</script>
<div ng-app="" ng-init="names=myNames">
I know it's bad practice but I use another framework also and don't want to duplicate the content of the variable. And it's just for prototyping not code for real app.
Or else if I use ng-init initial declaration, can I refer it from another script variable ?
It will not work because of the following reasons
Script tag is for to the html document's javascript. It has no relationship with angular's ng-init directive.
The angular documentation about ngInit directive warns as shown below.
Best practise is to go with controllers when these kind of situations arises.
Updated as per the discussion in chat
<body>
<script>
var myNames = [
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}];
var app = angular.module('myApp',[]);
app.controller('myController',function($scope){
$scope.names=window.myNames;
});
</script>
<div ng-app="myApp" ng-controller="myController">
<p>Looping with objects:</p>
<ul>
<li ng-repeat="x in names">{{ x.name + ', ' + x.country }}</li>
</ul>
</div>
</body>

ng-controller variable Vs ng-init vairable

For below code, using angularJS,
<script type="text/javascript">
angular.module('app').controller('add', ['$scope',function($scope) {
$scope.name = "Bonita Ln";
}]);
</script>
corresponding Javascript code to access $scope variable member name is,
<div ng-app="app" ng-controller="add">
<script type="text/javascript">
var dom_el = document.querySelector('[ng-controller="add"]');
var ng_el = angular.element(dom_el);
var ng_el_scope = ng_el.scope();
var name = ng_el_scope.name;
</script>
</div>
Below is the angular code, accessing ng-init variable name using angular expression,
<body ng-app="">
<div ng-init="name='Bonita Ln'">
{{name}}
</div>
</body>
How do I access the ng-init variable name using JavaScript?
you can do this by accessing variable $scope.name in your controller, but it has to be define inside your scope.
<div ng-init="name='Bonita Ln'">
{{name}}
<div ng-controller="MyCtrl">
Hello, {{name2}}!
</div>
</div>
angular.module('myApp',[]).controller('MyCtrl',function($scope) {
$scope.name2 = $scope.name;
})
this works, as you have define name in parent scope to controller, and is being inherited
but if for same controller html template will look like that:
<div ng-controller="MyCtrl">
<div ng-init="name='Bonita Ln'">
{{name}}
Hello, {{name2}}!
</div>
</div>
then it won't work, as variable was undefined when controller function was invoked
You could use it in exactly the same way, but you shouldn't.
I've downvoted your question because it is really really really bad practice, and I mean like every line of code you provided is bad practice.
I'm struggling trying to find out what you'd like to do, so I can't really provide you with better code to do so, but I can provide you with some links you must check out before continuing with whatever you're coding now.
Shaping up with Angular is a free codeschool course provided by the angular team, it is a really good course that will give you more insight on how to use angular than the phone-tutorial on the angular website.
Papa Johns Angular styleguide a great guide on how to write maintainable code.
thinkster.io a step by step guide to learn to master Angular
egghead.io a collection of good video tutorials
I know that my comment sounds quite harsh, but future you will thank you if you write more standardized Angular, and keep to a defined style-guide.
Also remember, in angular, your code should not know about the DOM, you don't need to specify eventListeners to DOM-elements, just use the appropriate directives like ng-click.
(yes I am aware that there can be exceptions to the rule)

Scope problems in basic Yeoman Angular Todo App

I'm following the tutorial here: http://www.youtube.com/watch?v=iUQ1fvdO9GY while learning yeoman and angular.
I've gotten to here http://www.youtube.com/watch?v=iUQ1fvdO9GY#t=266 and where his example works, mine does not due to some scope problems.
main.html
<div class="jumbotron">
<h2>My Todos:</h2>
<p ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span>{{todo.text}}</span>
</p>
<form ng-submit="addTodo()">
<input type="text" ng-model="newTodoText" size="20">
<input type="submit" class="btn btn-primary" value="add">
</form>
</div>
main.js
'use strict';
var myApp = angular.module('todoAppApp');
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.todos = [
{text: 'Item 1', done: false},
{text: 'Item 2', done: true},
{text: 'Item 3', done: false}
];
$scope.addTodo = function() {
$scope.todos.push({text: $scope.newTodoText, done: false});
$scope.newTodoText = '';
};
}]);
For some reason, though, the newTodoText variable is in a scope that is child to the $scope in main.js. This is confirmed using Batarang. I can't post a picture due to lack of rep, but in Batarang, there's:
Scope001 > Scope002 > Scope003(which is the $scope I have access to in the js) > Scope004 > {Scopes for each of the todos}
Scope003 has the original todos array and the addTodo() function. Scope004 has the newTodoText text when you type into the input form. On clicking add, addTodo() is correctly called, but $scope doesn't contain newTodoText because it's in Scope004.
I'm obviously missing something simple here due to my newness to the framework and the practically barebones implementation here. My Google-fu has turned up few results.
EDIT:
Ok, so in index.html, it contains the line
<div class="container" ng-include="'views/main.html'" ng-controller="MainCtrl"></div>
which includes main.html. I've replaced that line with the literal contents of main.html enclosed in the div
<div class="container" ng-controller="MainCtrl">
<!-- contents of main.html from above -->
</div>
And magically my scope problems are solved. Why does including main.html from a separate file mess with the scope?
EDIT #2
Cool, I figured it out.
Turns out that ng-include creates a new scope, which is contrary to what I thought it did (I had assumed that it was equivalent to a literal html injection). So I just moved the ng-controller="MainCtrl" from the .container div in index.html (Scope003) to the .jumbotron div within main.html.
Thanks for the help, and I'm a lot more knowledgeable about scope now!
ng-include creates a new scope. So I moved the ng-controller="MainCtrl" from the .container div in index.html (Scope003) to the .jumbotron div within main.html.

display number of items in ember.js

I need a very easy thing and looking on the web the solutions founded tell me that my code is right. But clearly isn't.
I just need to display how many notes(models) I have in my app:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> Notes and bookmarks </title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
<div class="wrap">
<div class="bar">
<input type="text" class="search" />
<div class="bar-buttons">
<button> NEW </button>
<button> HOME </button>
</div>
</div>
<aside>
<h4 class="all-notes">All Notes {{all}}</h4>
{{#each item in model}}
<li>
{{#link-to 'note' item}} {{item.title}} {{/link-to}}
</li>
{{/each}}
</aside>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="main">
<section>
<h2>Hellooo</h2>
</section>
</script>
<script type="text/x-handlebars" data-template-name="note">
<section>
<div class="note">
{{#if isEditing}}
<h2 class="note-title input-title"> {{edit-input-note value=title focus-out="modified" insert-newline="modified"}} </h2>
<p class="input-body"> {{edit-area-note value=body focus-out="modified" insert-newline="modified"}} </p>
{{edit-input-note value=url focus-out="modified" insert-newline="modified"}}
{{else}}
<h2 {{action "editNote" on="doubleClick"}} class="note-title" > {{title}} </h2>
<button {{action "removeNote"}} class="delete"> Delete </button>
<p {{action "editNote" on="doubleClick"}}> {{body}} </p>
{{input type="text" placeholder="URL:" class="input" value=url }}
{{/if}}
</div>
</section>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars-1.0.0.js"></script>
<script src="js/libs/ember-1.1.2.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/app.js"></script>
<script src="js/router.js"></script>
<script src="js/models/note_model.js"></script>
<script src="js/controllers/note_controller.js"></script>
<script src="js/controllers/notes_controller.js"></script>
<script src="js/views/note_view.js"></script>
</body>
</html>
My model:
Notes.Note = DS.Model.extend ({
title: DS.attr('string'),
body: DS.attr('string'),
url: DS.attr('string')
});
Notes.Note.FIXTURES = [
{
id: 1,
title: 'hello world',
body: 'ciao ciao ciao ciao',
url: ''
},
{
id: 2,
title: 'javascript frameworks',
body: 'Backbone.js, Ember.js, Knockout.js',
url: '...'
},
{
id: 3,
title: 'Find a job in Berlin',
body: 'Monster, beralinstartupjobs.com',
url: '...'
}
]
And my notes controller:
Notes.NotesController = Ember.ArrayController.extend ({
all: function () {
var notes = this.get('model');
return notes.get('lenght');
}.property('model')
});
I think that's all the important code for make this work but if you need others part I will add.
Why i can't see the number of my notes in {{all}} ?
In your notes.hbs template you should be able to do {{length}}.
I made a few adjustments to the code that you provided and got it to work the way that i think you expected it to work. I am going to try to explain where I think you may have been sent for a loop as it relates to your App. Ember takes some getting use to for sure.
One thing that is important is that Ember uses naming conventions to help you wire up and associate your Router, Routes, Controllers, Templates correctly with each other and also help you to look at code and know what Ember expects. Ember also gives you free stuff, yaaay for free stuff.
So when Ember boots up you get some default assets for free the ApplicationRoute, ApplicationController, and the application template these are always present in Ember even if you never explicitly define them. However if you need to use them just define them and add whatever code. In addition to those you get an IndexRoute, IndexController and index template which are present at the top of Ember like those other assets.
Remember the application template, which is at the highest level of the app, think of it as the parent of all templates. Other templates will be put into it and rendered, including the index template that you got for free. Now this is where things get a little tricky.
If you define a router like this in Ember for example. You still have that index template which you can use as you were and you also now have a template called note.
App.Router.map(function() {
this.resource('note');
});
Can you guess the names for the Controller and Route associated with the note resource using Ember naming conventions? I'll leave that for homework. Now when Ember boots with this Router if you defined a index template (just like you were) it will automatically be pushed into the application templates {{outlet}}and rendered. A note on the application template if all its doing is basically being and 'outlet' to render stuff Ember will do that just fine by default.
Behind the scenes the default application template may look something like this. I just put the <script> tags to highlight its the application template.
<script type="text/x-handlebars">
{{outlet}}
</script>
The template without a data-template-name is used as the application template. You can put in data-template-name="application" if you like but its not necessary unless you are using build tools. Now back to the Router stuff.
If you define a Router like this something happens that is important to realize.
App.Router.map(function() {
this.resource('note', { path: '/'});
});
By adding {path: '/'} in that resource you are overriding the /, which is the based url of you application. Now '/' is not associated with index template that you got for free its the note template. When Ember boots it will automatically push the note template into the application outlet.
Moving along, in your code you had few other things that were also conflicting. Remember with naming conventions if ask Notes.NotesController for something Ember by default will look for a Notes.NotesRoute and a notes template, attention to the pluralization, but in your code your code you have a note template.
The other conflict is with the index template, again with the naming conventions Ember is going to look for the IndexController for the {{all}} property and IndexRoute to the supply the model. In your code its on the Notes.NotesController.
And finally dont forget if you are using and adapter to define it:
Notes.ApplicationAdapter = DS.FixtureAdapter.extend({});
Sorry for the long answer but i hope it clear up things
Here the jsBin.
http://emberjs.jsbin.com/UPaYIwO/7/edit
Happy Coding!!
PS. You could have put {{model.length}} in the template and it would have accomplished the same thing. But in Ember there is a few ways to do the same thing.
There is also a typo:
return notes.get('lenght');
Lenght should be length;
return notes.get('length');

Reducing code in ng-repeat

I relatively new to AngularJS, and also brushing up on my JS in the process, so this may come across as a very basic question, but here goes nothing. I would like to separate my code out, i.e. the logic from the presentation, since I would like to follow the MVC guidelines.
So, the problem I have is that I end up having to write a lot of code in my HTML, and I really do not like this since, it gets hard to debug later on, and this code is included inside ng-repeat.
So, without much further-ado, here is the code, for reference, I please check this, as it has line numbers to the side.:
HTML
<!doctype html>
<html lang="en-US" ng-app>
<head>
<meta charset="UTF-8">
<title>ng-click</title>
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" href="style.css" media="all"/>
</head>
<body>
<!--Search div-->
<div class="search">
<label for="findName" >
Find your name:
<input type="search" name="userSearch" id="angularUserSearch" ng-model="search"/>
</label>
</div>
<!--List of results-->
<div ng-controller="List">
<ul>
<!--Can also work with objects-->
<li ng-repeat="person in people | filter:search">
{{ person.name }} -> {{ person.age }}
</li>
</ul>
<div>
<label for="">
Name:
<input type="text" name="new_name" id="new_name" ng-model="new_name"/>
</label>
<br>
<label for="">
Age:
<input type="text" name="new_age" id="new_age" ng-model="new_age"/>
</label>
<br>
<button ng-click="add()">Add</button>
</div>
</div>
<!--Scripts-->
<script type="text/javascript" src="angular_1.0.7.js"></script>
<script type="text/javascript" src="myscript.js"></script>
</body>
</html>
The styling is irrelevant. So, I leave it out.
JavaScript
var List = function ($scope) {
// Making the list called names
$scope.people = [
{name: "Harold", age:"20"},
{name: "Jessie", age:"34"},
{name: "Samantha", age:"18"},
{name: "March", age:"40"},
{name: "Scott", age:"44"},
{name: "David", age:"28"},
{name: "Dan", age:"28"}
];
$scope.add = function () {
$scope.people.push({
name: $scope.new_name,
age: $scope.new_age
});
$scope.new_name = "";
$scope.new_age = "";
};
};
So, what I would like to do, is simply give ng-repeat (on line 22) a function, that is declared in my JS, so that I do not have to put in any business logic inside the code. So for example, I would like something like this:
<li ng-repeat="function_that_returns_person_objects()">
How can I achieve this?
It makes sense to put page logic on the template like the comprehension expression in the ng-repeat. This is not the business logic, it is the logic for rendering the page. And it is more elegant than implementing it using an function.
ng-repeat only takes an expression but you should be able to use a function as a part of the expression like this,
<li ng-repeat="data in myFunction()">
so you can simplify a bit if you need to preprocess the data source. But I don't think you can use a pure function in the ng-repeat as what you said. Hope it helps.
I don't quite understand why you need the function_that_returns_person_objects again. Angular's binding will take care of ensuring your view stays up to date.
Here's a demonstration, and here's the source for it.
If you have a complex filter function then just write it 'aside' like this : http://docs.angularjs.org/guide/dev_guide.templates.filters.creating_filters
But when you consume it, it's right from the view, as you want to repeat an view partial following the value returned by it.

Categories