AngularJS : cannot connect factory to controller - javascript

I'm sorry if this is an easy question. I'm new and probably don't understand the right things to search for to find the answer.
I've basically followed this angularJS tutorial http://www.youtube.com/watch?v=i9MHigUZKEM
I've gotten through all of it except setting up a factory that connects to my controller.
Here is the code for the factory:
demoApp.factory('simpleFactory', function(){
var people = [
{ name: 'Will', age: '30' },
{ name:'Jack', age:'26' },
{ name: 'Nadine', age: '21' },
{ name:'Zach', age:'28' }
];
var factory = {};
factory.getPeople = function() {
return people;
};
});
Here is the controller:
demoApp.controller('FirstCtrl', ['$scope', 'simpleFactory', function ($scope, simpleFactory) {
$scope.people = simpleFactory.getPeople();
}]);
And just a simple repeat in the HTML:
Name:
<input type="text" ng-model="filter.name"> {{ nameText }}
<ul>
<li ng-repeat="person in people | filter:filter.name | orderBy: 'name'">{{ person.name }}- {{ person.age }}</li>
</ul>
The error I get is "TypeError: Cannot call method 'getPeople' of undefined" in the javascript console.
Note: This all works correctly when within the controller I have the data object hardcoded in like so:
demoApp.controller('FirstCtrl', ['$scope', 'simpleFactory', function ($scope, simpleFactory) {
$scope.people = [
{ name: 'Will', age: '30' },
{ name:'Jack', age:'26' },
{ name: 'Nadine', age: '21' },
{ name:'Zach', age:'28' }
];
}]);

A small change in your service;
demoApp.factory('simpleFactory', function(){
var people = [
{ name: 'Will', age: '30' },
{ name:'Jack', age:'26' },
{ name: 'Nadine', age: '21' },
{ name:'Zach', age:'28' }
];
return {
getPeople: function() {
return people;
};
}
});
And in your controller
demoApp.controller('FirstCtrl', ['$scope', 'simpleFactory', function ($scope, simpleFactory) {
$scope.people = simpleFactory.getPeople();
}]);

Essentially you're just missing the return statement from your 'factory' object, but it might be clearer if you do a little renaming as well to avoid confusion.
Example, adapted from the AngularJS book:
demoApp.factory('People', function(){ // Renamed factory to be more descriptive
var people = {}; // Renamed 'factory' to 'people', as an object we can prototype more functions later
people.query = function() { // called query, but could be getPeople
return [ // just return a static array for now
{ name: 'Will', age: '30' },
{ name:'Jack', age:'26' },
{ name: 'Nadine', age: '21' },
{ name:'Zach', age:'28' }
];
}
return people;
});
So now your controller can pull this information in:
demoApp.controller('FirstCtrl', ['$scope', 'People', function ($scope, People) { // dependency injection
$scope.people = People.query();
});
So now we have a descriptive factory for People that returns an object, one of which is called query. We can later update this query function to read in parameters, fire off AJAX calls and do some complicated stuff. But one step at a time :)

Related

Uncaught ReferenceError: $scope is not defined

Receiving the error when the page is loaded. I'm trying to append a new object to an array of entries.
What is wrong with the code?
index.html
Raffler
<div ng-controller="RaffleCtrl">
<form ng-sumbit="addEntry">
<input type="text" ng-model="newEntry.name">
<input type="submit" value="Add">
</form>
<ul>
<li ng-repeat="entry in entries">{{entry.name}}</li>
</ul>
</div>
raffle.js
angular.module('myApp', []).controller("RaffleCtrl", function ($scope) {
$scope.entries = [
{
name: "Larry"
}, {
name: "Curly"
}, {
name: "Moe"
}
]
});
$scope.addEntry = function () {
$scope.entries($scope.newEntry)
$scope.newEntry = {}
};
You wrongly used $scope outside the controller. Use the $scope inside the controller
angular.module('myApp', []).controller("RaffleCtrl", function ($scope) {
$scope.entries = [
{
name: "Larry"
}, {
name: "Curly"
}, {
name: "Moe"
}
];
$scope.addEntry = function () {
$scope.entries($scope.newEntry)
$scope.newEntry = {}
};
});
if you really want to keep it outside
angular.module('myApp', []).controller("RaffleCtrl", function ($scope) {
$scope.entries = [
{
name: "Larry"
}, {
name: "Curly"
}, {
name: "Moe"
}
];
$scope.addEntry = addEntry;
});
function addEntry() {
$scope.entries($scope.newEntry)
$scope.newEntry = {}
};

AngularJS ng-options with complex objects (no unique identifier)

I'm trying to use angular's select/ng-options to work with complex objects that does not have an ID. In particular, the data might look like this (an array of accounts):
this.items = [
{
prefix: 123,
number: 111111111,
bankCode: "0100"
},
{
prefix: 456,
number: 22222222,
bankCode: "0200"
},
{
prefix: 789,
number: 33333333,
bankCode: "0300"
}
];
I want to have a select-box with items from this array, formatted using custom angular filter (format "prefix-number/bankCode"). When an item is selected, it should be stored in controller.selected as complex account item, eg.
this.selected = {
prefix: 123,
number: 111111111,
bankCode: "0100"
};
Now, this works OK using the snipped bellow.
var myApp = angular.module('myApp', [])
.filter('accountNumberFilter', function () {
return function (input) {
return input.prefix + "–" + input.number + "/" + input.bankCode;
};
})
.controller('MyCtrl', function () {
this.selected = null;
this.items = [
{
prefix: 123,
number: 111111111,
bankCode: "0100"
},
{
prefix: 456,
number: 22222222,
bankCode: "0200"
},
{
prefix: 789,
number: 33333333,
bankCode: "0300"
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl as ctrl">
<select
ng-model="ctrl.selected"
ng-options="acct | accountNumberFilter for acct in ctrl.items">
<option value=""></option>
</select>
<pre>Selected:
{{ ctrl.selected | json : spacing }}</pre>
</div>
The problem is when I navigate from one page to another, storing the selected item to a service. After returning to the page with selectbox (eg. having an item in ctrl.selected), no item in selectbox is selected. I understand that this is due to object inequality, so I'm looking for a way to tell angular "two account items are considered equal iif prefix==prefix && number==number && bankCode==bankCode".
This snipped demonstrates this situation (the only difference to the above is that controller have
this.selected = {
prefix: 123,
number: 111111111,
bankCode: "0100"
};
var myApp = angular.module('myApp', [])
.filter('accountNumberFilter', function () {
return function (input) {
return input.prefix + "–" + input.number + "/" + input.bankCode;
};
})
.controller('MyCtrl', function () {
this.selected = {
prefix: 123,
number: 111111111,
bankCode: "0100"
};
this.items = [
{
prefix: 123,
number: 111111111,
bankCode: "0100"
},
{
prefix: 456,
number: 22222222,
bankCode: "0200"
},
{
prefix: 789,
number: 33333333,
bankCode: "0300"
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl as ctrl">
<select
ng-model="ctrl.selected"
ng-options="acct | accountNumberFilter for acct in ctrl.items">
<option value=""></option>
</select>
<pre>Selected:
{{ ctrl.selected | json : spacing }}</pre>
</div>
You need to store this.items somewhere else so that it is not destroyed and recreated every time your controller is instantiated. Because then it is a new array of new objects that will not be equal to what you have saved. Perhaps put it into a service also.

AngularJS edit scope with same name from another method

I would like to update some value in scope with same name in another method of same class.
Problem is that update method is not working if i'm trying to update scope named gridData.
Should i use scope apply or something Simillar in Angular?
Here is my code:
orders.controller('OrdersCtrl', function ($scope, $http, $rootScope, $location, $notification, $routeParams, ApiService) {
$scope.gridData = {};
$scope.initGrid = function () {
$scope.gridData = new kendo.data.ObservableArray([
{ artist: "Pink Floyd", track: "The dark side of the Moon" },
{ artist: "The Beatles", track: "I've just seen a face" },
{ artist: "Queen", track: "Innuendo" }
]);
$scope.test = function() {
console.log('dede');
};
$scope.data.test,$scope.gridColumns = [
{ field: "artist", title: "Artist" },
{ field: "track", title: "Track" }
];
};
$scope.update = function() {
// empty JSON object
console.log($scope.gridData);
// Here i get error
$scope.gridData[0].set("track", "Hey you");
};
Thanks for any example how to do it correctly.

Angularjs: call a controller method with param from a directive dropdown on change event

I have made a dropdown directive. On selecting a value from the dropdown, a method from the controller is called with the selected filter passed. Everything works fine except that the method is always returning the default selected value, no matter, what is selected.
html:
<div ng-controller="Ctrl">
<div>
<dropdown filters="filters" filter="filter" select="selectFilter(filter)"></dropdown>
</div>
</div>
javascript:
var app = angular.module('app', []);
function Ctrl($scope){
$scope.filters = [
{ Id: 1, Name: "All" }, { Id: 2, Name: "filter1" }, { Id: 3, Name: "filter2" }, { Id: 4, Name: "filter3"}
];
$scope.filter = $scope.filters[0];
$scope.selectFilter = function(selectedFilter){
alert(selectedFilter.Name);
};
}
app.directive('dropdown', function(){
return {
restrict: "E",
scope: {
filter: "=",
filters: "=",
select: "&"
},
template: "<select ng-model=\"filter\" ng-change=\"select(filter)\" ng-options=\"f.Name for f in filters\"></select>"
};
});
Working fiddle: http://jsfiddle.net/wXV6Z/98/
You're using wrong syntax to call method binding.
Try:
ng-change=\"select({filter:filter})\
DEMO
I made this, but the other answer seems better :P
http://jsfiddle.net/wXV6Z/101/
var app = angular.module('app', []);
function Ctrl($scope, $element){
$scope.filters = [
{ Id: 1, Name: "All" }, { Id: 2, Name: "filter1" }, { Id: 3, Name: "filter2" }, { Id: 4, Name: "filter3"}
];
$scope.filter = $scope.filters[0];
}
app.directive('dropdown', function(){
return {
restrict: "E",
scope: {
filter: "=",
filters: "=",
},
template: "<select ng-model=\"filter\" ng-change=\"selectFilter(filter)\" ng-options=\"f.Name for f in filters\"></select>",
controller: function($scope, $element) {
$scope.selectFilter = function(selectedFilter) {
alert(selectedFilter.Name);
}
}
}
});

Looping through deep objects in ng-repeat

I'm in angular and i have a object like this.
var items = [{
title: 'Something',
children: [
{ title: 'Hello World' },
{ title: 'Hello Overflow' },
{ title: 'John Doe', children: [
{ title: 'Amazing title' },
{ title: 'Google it' },
{ title: 'I'm a child', children: [
{ title: 'Another ' },
{ title: 'He\'s my brother' },
{ title: 'She\'s my mother.', children: [
{title: 'You never know if I'm going to have children'}
]}
]}
]}
]
}];
I wan't to loop through all of these so i have something like this.
    • Something
       • Hello World
       • Hello Overflow
       • John Doe
          • Amazing Title
          • Google it
          • I'm a child
              • Another
              • He's my brother
              • She's my mother
                  • You never know if I'm going to have children
The problem is I wouldn't know how deep this object will go or what's in it. so I wouldn't be able to do it manually. I have done a basic loop with ng-repeat in the fiddle provided at the bottom, but i can't figure out how I can automatically loop through these and create nested <ul>'s and <li>'s.
What would be the best way to accomplish this?
Demo: http://jsfiddle.net/XtgLM/
You don't need to make a custom directive, what you want is to use an inline template that calls it's self.
I forked your fiddle.
http://jsfiddle.net/MasterMorality/E99Gh/2/
basically it looks like this:
<script type='text/ng-template' id="item.html">
...
<div ng-repeat="x in x.childrens" ng-include="'item.html'"></div>
</script>
...
<div ng-repeat="x in things" ng-include="'item.html'"></div>
I should note that you are not actually overwriting x, since angular creates a new scope for each repeated item.
Here you go:
html
<div ng-app="app" ng-controller="test">
<ul>
<li nested-item ng-repeat="item in items">{{item.title}}</li>
</ul>
</div>
JavaScript
var items = [{
title: 'Something',
children: [
{ title: 'Hello World' },
{ title: 'Hello Overflow' },
{ title: 'John Doe', children: [
{ title: 'Amazing title' },
{ title: 'Google it' },
{ title: 'Im a child', children: [
{ title: 'Another ' },
{ title: 'He\'s my brother' },
{ title: 'She\'s my mother.', children: [
{title: 'You never know if im going to have children'}
]}
]}
]}
]
}];
var app = angular.module('app', []);
app.controller('test', function( $scope ) {
$scope.items = items;
});
app.directive('nestedItem', ['$compile', function($compile){
return {
restrict: 'A',
link: function(scope, element){
console.log(element);
if (scope.item.children){
var html = $compile('<ul><li nested-item ng-repeat="item in item.children">{{item.title}}</li></ul>')(scope);
element.append(html);
}
}
};
}]);
I forked your fiddle:
http://jsfiddle.net/c4Kp8/
Actually I must confess that I like Master Morality's approach but you can also go with a custom directive. The key thing to know if you go that route is that you need to intercept on the item level to manually check if the current item has children, and if so, $compile the directive for the node yourself.
UPDATE
However, there is one thing that should bother us in the above code. The duplication of html code (inlined in the directive) is a code smell. If you like, you can get really funky and fix this by introducing a generic template-code directive which doesn't do anything else but providing the code of the node where it is applied on as a template for other directives.
So then our solution would look like this:
html
<div ng-app="app" ng-controller="test">
<ul template-code>
<li nested-item ng-repeat="item in items">{{item.title}}</li>
</ul>
</div>
JavaScript
var items = [{
title: 'Something',
children: [
{ title: 'Hello World' },
{ title: 'Hello Overflow' },
{ title: 'John Doe', children: [
{ title: 'Amazing title' },
{ title: 'Google it' },
{ title: 'Im a child', children: [
{ title: 'Another ' },
{ title: 'He\'s my brother' },
{ title: 'She\'s my mother.', children: [
{title: 'You never know if im going to have children'}
]}
]}
]}
]
}];
var app = angular.module('app', []);
app.controller('test', function( $scope ) {
$scope.items = items;
});
app.directive('templateCode', function(){
return {
restrict: 'A',
controller: function(){},
compile: function(element){
element.removeAttr('template-code');
//ATTENTION: We need to trim() here. Otherwise AngularJS raises an exception
//later when we want to use the templateCode in a $compile function.
//Be aware that we assume a modern browser
//that already ships with a trim function.
//It's easy to secure that with a polyfill.
var templateCode = element.parent().html().trim();
return function(scope, iElement, iAttrs, controller){
controller.templateCode = templateCode;
}
}
}
});
app.directive('nestedItem', ['$compile', function($compile){
return {
restrict: 'A',
require: '^templateCode',
link: function(scope, element, iAttr, controller){
if (scope.item.children){
scope.items = scope.item.children;
var html = $compile(controller.templateCode)(scope);
element.append(html);
}
}
};
}]);
Plunker: http://jsfiddle.net/2rQWf/
You're probably going to need to create your own directive passing in the object to iterate over. Put a watch on the object passed in and when that fires run some recursive function that appends elements to the element that the directive is on.
You can get at the DOM element from the element parameter on the directive link function. You can obviously append DOM elements using that same element parameter.

Categories

Resources