Scope problems in basic Yeoman Angular Todo App - javascript

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.

Related

How would I create something similar like this in AngularJS

Having a hard time grasping AngularJs.. How would you build something like this simple textfields and animations in AngularJS? I had been looking into using directives but it just isn't adding up to me much. I had been trying to base this from what I learned online but was not having much luck
http://codepen.io/yusufbkr/pen/RPBQqg
HTML:
<div class="materialContainer">
<div class="title">LOGIN</div>
<div class="input">
<label for="name">Username</label>
<input type="text" name="name" id="name">
<span class="spin"></span>
</div>
<div class="input">
<label for="pass">Password</label>
<input type="password" name="pass" id="pass">
<span class="spin"></span>
</div>
<div class="button login">
<button><span>GO</span> <i class="fa fa-check"></i></button>
</div>
Forgot your password?
<div class="title">REGISTER</div>
<div class="input">
<label for="regname">Username</label>
<input type="text" name="regname" id="regname">
<span class="spin"></span>
</div>
<div class="input">
<label for="regpass">Password</label>
<input type="password" name="regpass" id="regpass">
<span class="spin"></span>
</div>
<div class="input">
<label for="reregpass">Repeat Password</label>
<input type="password" name="reregpass" id="reregpass">
<span class="spin"></span>
</div>
<div class="button">
<button><span>NEXT</span></button>
</div>
just use the codepen link ( http://codepen.io/yusufbkr/pen/RPBQqg ), stackoverflow won't let me input the rest of the code...
Thanks! Any help would be incredible
I'm just learning Angular myself. From what I know, you're on the right track to be thinking of directives for swapping out interactive parts of the DOM. I suspect you'll also be needing ui-router for dealing with the widget outcomes (I hear it's the industry standard, vs the built-in ngRouter).
I'm partly writing this out to make sure I understand it, so I hope other, more knowledgeable people will come by and answer any questions you have about it (or point out where I'm misundertanding something.)
So you bring the webpage into the Angular world by, for instance, making the <html></html> tags into <html ng-app="yourApp"></html>. The unchanging html that will be the same on every view is in the index.html file. Put the html for the boxes is in another html file, say box.html. On the index.html file in the place where you want to have your boxes appear, put <div ui-view></div>. The ui-view connects to ui-router. Down at the bottom of the index.html right before closing the body tag, put
<script src="lib/angular/angular.js"></script>
<script src="lib/angular-ui-router/release/angular-ui-router.js"></script>
<script src="app/app.js"></script>.
In app.js, you put something like,
angular.module('yourApp', [
'yourApp.box',
'ui.router'
])
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/signin/child');
$stateProvider
.state('box', {
url: '/signin',
templateUrl: 'app/box/box.html',
controller: 'BoxController'
})
.state('box.child', {
url: '/child',
template: '<span>{{ definedProperty }}</span>'
});
})
});
Starting at the top, you name angular, call its module function, and give it the parameters ('theNameYouChose', ['dependencies','required'])
In the config bit there, you're telling it about dependencies config needs. $stateProvider and $urlRouterProvider are ui-router things, and $httpProvider is, I believe, an angular thing that is still necessary.
In the urlRouterProvider, you're providing the default address the website goes to: in this case it happens to be a template within a template, /signin from the signin state, and /child from the specifics pasted into signin
In the states, you are giving names and properties to the URLs that will be part of your app: like, if someone goes to index.html/signin, the 'signin' state is summoned.
The template within a template might be how you'd get the box variability that you're looking for. In box.html you will have another <div ui-view></div> and that's where the varying thing described in 'signin.child' gets put in.
box.html is also the place you put your specially created html tags, the ones you will make with the directive. I'll talk through those below.
The parent template, 'signin', talks about BoxController. Let's say you built that in box.js. It would look like this:
angular.module('yourApp.box', [])
.controller('BoxController', function ($scope) {
$scope.definedProperty = 'reRegPass',
$scope.arrayOfObjects = [{prop: 'red'},{prop: 'blue'}]
})
.directive('specificBox', function(){
return {
restrict: 'EA',
templateUrl: 'app/box/box-guts.html',
replace: true,
scope: {
source: '='
},
link: function(scope, element, attribute) {
element.on('click', function() {
alert('Functionality in specificBox is working');
}
}
}
});
The first line is again summoning angular module, and then naming it: note we already listed this, 'yourApp.box', as a requirement in app.js. Then we have the controller-naming function, and the appearance of specific properties in their $scope.
The directive is taking the more useful of the two forms a directive can take. They can either return functions (with signatures like the one in .link), or they can return objects that describe new HTML entities. This one is named specificBox, which Angular will translate into <specific-box></specific-box>. This object (technically called a 'directive definition object') can be translated into HTML tags because of the restrict property... 'E' is for element. 'A' is for attribute. I don't know how something that can be an element could also be an attribute, but having both options works, so I'm going with it. (Maybe for your various signin boxes you want a directive that has just 'A' to make new types of attributes.)
You put these specifically-crafted tags into box.html, and everything in box-guts.html will be between those tags... In fact, replacing those tags because of the replace: true.
The .link property is where you would put a function that would do something interesting to that element; turn it red if clicked, whatever. I have an alert example. Uses jQuery Lite for events.
The .scope property is odd. I believe if left off, the specific-box stuff would have the same controller as box (ie, BoxController), with box's scope and dependencies. But here, instead, we're making an isolate scope. box-guts will not depend on anything or have access to anything built before. How will it get interesting changeable data, then..? Every property in the scope object becomes an attribute on specific-box.
The '=' means that you will pass it objects from somewhere, and they will have 2-way data binding (changes made in box-guts will be reflected in the object collection in your app). Other options are '#', one-way data binding, which means you're passing box-guts a string or something that, if it changes in the DOM, you don't care to reflect in your app; or '&' to give it a function from a controller somewhere else in your app.
So ultimately, in box.html, you will have something like
<div class="box-holder">
<div ui-view></div>
<specific-box source="thing" ng-repeat="thing in arrayOfObjects"></specific-box>
</div>
Like I said above, ask questions and maybe we can sort this out.

How to display a repeated list with angularjs and html formatting?

I want to show a repeated list, and render each element with ngSanitize as follows:
<div ng-repeat="todo in todos" ng-bind-html="todo.text"/>
I get no error in the console, but I also don't get any output. Why?
By the way: the following does actually work, but unformatted markup:
<div ng-repeat="todo in todos">{{todo.text}}</div>
I think there were a few mistakes in your code. The example below should help you further. Remember, Angular doesn't support self closing <div>-tags.
HTML
<div ng-app="myApp" ng-controller="myAppCtrl">
<div ng-repeat="todo in totos" ng-bind="todo.text"></div>
</div>
JS
angular.module("myApp", [])
.controller("myAppCtrl", function($scope) {
$scope.todos = [
{name: "todo1", text: "This is test todo 1"},
{name: "todo2", text: "This is test todo 2"},
{name: "todo3", text: "This is test todo 3"}
]
});
JSFiddle: https://jsfiddle.net/ABr/38foxe31/
I have created a plunker and tried to replicate your problem, but everything is working fine.
A couple of things to consider:
1) Have you loaded angular-sanitize in your index file? (before app.js)
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular-sanitize.js"></script>
2) Have you correctly injected ngSanitize in your module?
angular.module('app', ['ngSanitize']);

A simple application of todo not adding input value to view

Every time my controller adds new input on view but blank, I want it should add new input with entered value.
<div class="container">
<h2>My todos</h2>
<form ng-submit="addTodo()" role="form">
<input type="text" ng-model="todo" value="Something" placeholder="Add">
<input type="submit" value="Add">
</form>
<p class="form-group" ng-repeat="todo in todos">
<input type="text" ng-model="todo" class="form-control">
</p>
</div>
The above code has separate temple main.html and calling that code inside index.html
<div ng-include="'views/main.html'" ng-controller="MainCtrl"></div>
then whenever I add new todo, its add input blank, and add null value inside todos array list.
but when loading template without using ng-include its work fine.
angular.module('mymailApp')
.controller('MainCtrl', ['$scope', function ($scope) {
$scope.todos = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
$scope.addTodo = function(){
$scope.todos.push($scope.todo);
$scope.todo = "";
};
}]);
Change "onDomReady" to "no wrap - in head" in the left panel "Frameworks & Extensions" panel
Also, in your module call, you want to pass a second parameter, an array containing all of your module's dependencies. In your case, an empty array []
angular.module('mymailApp',[]).controller(...)
Without the second parameter, it assumes you are trying to extend an existing module, not define a new one.
Updated your fiddle: http://jsfiddle.net/z2bvx2oh/11/
so i solved the issue moving controller call to "main.html" template, as ng-include template doesnt call the ng-controller directly, it should use either $routeProvider or controller should be call inside the template
modified code looks something like that:
ng-controller="MainCtrl" moves inside main.html

Angular JS $scope query

I'm trying to do the following:
Type something in a search box, perform the related search and show the result defined in a separate view template.
The search thingy is part of a page header and so, my index page is as follows:
index.html
<div class="row search-box">
<div class="col-md-2"></div>
<div class="col-md-6">
<form id="myForm" class="form-inline form-search"
style="padding-left: 10px">
<!-- Search box -->
<input id="in" type="text" ng-model="searchTerm" placeholder="Search for products, categories or brands"
class="search-query input-search">
<button class="btn searchBtn" ng-click="doSearch()" ng-controller="MediaListController">
<i class="icon-search"></i>Search
</button>
</form>
</div>
<div class="col-md-3">
<div class="btn btn-blue cart-btn-cont">
<span class="cart-icon"></span>
</div>
</div>
</div>
...
<div ng-view></div>
Controller (relevant function)
$scope.doSearch = function () {
var type = $scope.mediaType;
$scope.foundItems = MediaService.search().length;
console.log("Found items :"+ $scope.foundItems + "for search term :"+ $scope.searchTerm);
$location.path("/search");
}
app.js
var app= angular.module('sampleApp', ['ngRoute','ngResource','mgcrea.ngStrap']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/login', {templateUrl: 'app/views/partials/login.html', controller: 'LoginController'});
$routeProvider.when('/search', {templateUrl : 'app/views/partials/tpl/search_result.tpl.html'});
$routeProvider.when('/', {templateUrl: 'app/views/landing_page.html', controller: 'MediaListController'});
$routeProvider.otherwise({redirectTo: '/'});
}]);
Search result template:
Found {{ foundItems }} product(s) for search phrase <i>"{{searchTerm}}"</i>
It all works fine, but in getting to this stage, I am confused by the following:
a. Why is $scope.foundItems not visible in the template i.e, {{foundItems}} works only when I use $scope.$parent.foundItems in the controller?
b. If a new child scope is being created, how come $scope.searchTerm is available in the view template i.e., {{searchTerm}}? Is it because ng-model="searchTerm" creates a model binding in the rootScope?
c. I also saw that {{foundItems}} in the template worked fine if I used $rootScope.foundItems, but what if I want to avoid $rootScope - is the use of $scope.$parent valid?
Thanks in advance.
a. Why is $scope.foundItems not visible in the template i.e, {{foundItems}} works only when I use $scope.$parent.foundItems in the controller?
I think it's a little strange to assign the same controller in the ng-view and then another tag like button. Because button will create its own scope right there. And then ng-view also creates one.
b. If a new child scope is being created, how come $scope.searchTerm is available in the view template i.e., {{searchTerm}}? Is it because ng-model="searchTerm" creates a model binding in the rootScope?
that scope has nothing to do with the doSearch scope actually.
c. I also saw that {{foundItems}} in the template worked fine if I used $rootScope.foundItems, but what if I want to avoid $rootScope - is the use of $scope.$parent valid?
You should probably have a main controller for the page and then call everything from there perhaps.

Multiple ng-app directives on a page

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.

Categories