I am building up a simple task management system with AngularJS and am currently playing with it and mocking the data up. I have a problem with the injection of a service into my ProjectCtrl controller and I cannot wrap my head around it.
At the bottom of this code: Why is the projects variable in the ProjectsCtrl controller just an empty array? It should contain the mockup data, no? What am I doing wrong?
Please excuse this maybe very stupid question. I just don't find my mistake.
angular.module("TaskManager", ['ui.router'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
})
.state('projects', {
url: '/projects/{id}',
templateUrl: '/projects.html',
controller: 'ProjectsCtrl'
});
$urlRouterProvider.otherwise('home');
}
])
.factory('projects', [function(){
var o = {
projects: []
};
return o;
}])
.controller('MainCtrl', [
'$scope',
'projects',
function($scope, projects){
$scope.projects = projects.projects;
$scope.projects = [
{title: "project 1", done: 0},
{title: "Another project 2", done: 0},
{title: "project 3", done: 1},
{title: "project 4", done: 0},
{title: "project 5", done: 0},
];
$scope.addproject = function() {
if(!$scope.title || $scope.title === '') { return };
$scope.projects.push({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
}
$scope.markAsDone = function(project) {
project.done = 1;
}
}
])
.controller('ProjectsCtrl', [
'$scope',
'$stateParams',
'projects',
function($scope, $stateParams, projects){
$scope.project = projects.projects[$stateParams.id];
// Why is this an empty array?
console.log(projects);
}
])
For completion: This is the HTML part:
<html>
<head>
<title>TaskManager</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="TaskManager">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<ui-view></ui-view>
</div>
</div>
<script type="text/ng-template" id="/home.html">
<div class="page-header">
<h1>TaskManager</h1>
TEST
</div>
<div ng-repeat="project in projects | orderBy: ['done','title']">
<span ng-click="markAsDone(project)">Done</span>
{{project.title}} - done: {{project.done}}
<span>
Tasks
</span>
<p ng-show="project.comment">Comment: {{project.comment}}</p>
</div>
</hr>
<form ng-submit="addproject()" style="margin-top: 30px;">
<h3>Add new project</h3>
<input type="text" ng-model="title" placeholder="project"></input>
<br><br>
<textarea name="comment" ng-model="comment"></textarea>
<br>
<button type="submit" class="btn">Post</button>
</form>
</script>
<script type="text/ng-template" id="/projects.html">
<div class="page-header">
<h3>Project: {{project.title}}</h3>
</div>
<div ng-repeat="task in project.tasks | orderBy: ['done','title']">
{{task.title}} - done: {{task.done}}
<p ng-show="task.comment">Comment: {{task.comment}}</p>
</div>
</script>
</body>
</html>
You never assign the values to projects.projects. In MainCtrl, you assign $scope.projects to the value of projects.projects (which is an empty array at that point). Then after that you overwrite $scope.projects with a totally new and different array, so you never end up modifying projects.projects.
I would move functions that let you add, remove, update items to projects service, but in the interim you can assign projects.projects first and then assign that to $scope.projects.
Better projects service:
.factory('projects', function() {
var projects = [];
return {
add: function(item) {
// your additional code
projects.push(item);
},
remove: function(item) {
// your additional code
var i = projects.indexOf(item);
if (i >=0) projects.splice(i,1);
},
get: function() {
return projects;
},
initialize: function(items) {
projects = items;
}
};
});
Then you can use this in your controller:
.controller('MainCtrl', function($scope, projects) {
projects.initialize([ ... ]);
$scope.projects = projects.get();
$scope.addproject = function() {
// NOTE: move whatever code you feel is or could be the responsibility of the service to the add method. I left this function as-is though, so you have a frame of reference.
if(!$scope.title || $scope.title === '') { return };
projects.add({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
};
// etc.
});
I recommend making the service the single point of authority so you can test logic pertaining to interacting with the service and its data, and avoid repeating yourself when different controllers and directives need to interact with the service or its data. It'll also help you avoid these sorts of issues where the data becomes out of sync between different controllers, directives, etc.
When you're setting up the mock data, you're only setting them on $scope.projects...the factory's projects is never updated. You could flip it around and it should work:
projects.projects = [<mock data>];
$scope.projects = projects.projects;
Put your mocked data in factory and remove the initialization in your controller.
angular.module("TaskManager", ['ui.router'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
})
.state('projects', {
url: '/projects/{id}',
templateUrl: '/projects.html',
controller: 'ProjectsCtrl'
});
$urlRouterProvider.otherwise('home');
}
])
.factory('projects', [function(){
var o = {
projects: [
{title: "project 1", done: 0},
{title: "Another project 2", done: 0},
{title: "project 3", done: 1},
{title: "project 4", done: 0},
{title: "project 5", done: 0},
]
};
return o;
}])
.controller('MainCtrl', [
'$scope',
'projects',
function($scope, projects){
$scope.projects = projects.projects;
$scope.addproject = function() {
if(!$scope.title || $scope.title === '') { return };
$scope.projects.push({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
}
$scope.markAsDone = function(project) {
project.done = 1;
}
}
])
.controller('ProjectsCtrl', [
'$scope',
'$stateParams',
'projects',
function($scope, $stateParams, projects){
$scope.project = projects.projects[$stateParams.id];
// Why is this an empty array?
console.log(projects);
}
])
Related
I found a code that outputs a multi-level list into Angular here
http://jsfiddle.net/c4Kp8/
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);
}
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="test">
<ul>
<li nested-item ng-repeat="item in items">{{item.title}}</li>
</ul>
</div>
How to implement here hiding and displaying items in the list? Only Angular not jquery....
Hope you know that, you can make nested ng-repeat without special directive for it :-) ?
You can use for example angular-ui.bootstrap.collapse, or some custom solutions.
Solutions:
Angular UI Bootrap
Googled custom solution
Use ng-show / ng-hide / ng-if
https://docs.angularjs.org/api/ng/directive/ngShow
https://docs.angularjs.org/api/ng/directive/ngHide
https://docs.angularjs.org/api/ng/directive/ngIf
Maybe a treeview is more adequate than multiples imbricated ng-repeat
http://ngmodules.org/modules/angular.treeview
Factory:
(function(){
angular
.module('projectApp')
.factory('weatherfactory', weatherfactory);
weatherfactory.$inject=['$http'];
function weatherfactory($http){
var cities=[
{name: "Łódź", link: "http://api.openweathermap.org/data/2.5/forecast/daily? q=Lodz&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Warszawa", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Wrocław", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Kraków", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Gdańsk", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Londyn", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"}
];
var weat={};
var service={
getCities: getCities,
GetWeather: _GetWeather
};
return service;
function _GetWeather(link){
return $http.get(link);
}
function getCities(){
return cities;
}
}
})();
In controller i get data from factory:
sa.cities=weatherfactory.getCities();
And in view i am trying to show data in but it does'n work:
<select class="form-control" ng-options="item.name as item.link for item in sa.cities"></select>
I am using controllerAs approach.
ng-options requires ng-model to be specified:
<select class="form-control" ng-model="something" ng-options="item.name as item.link for item in sa.cities"></select>
var app = angular.module('app', []);
app.controller('myController', function($scope) {
var sa = this;
sa.cities = [{
name: "Łódź",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Lodz&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Warszawa",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Wrocław",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Kraków",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Gdańsk",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Londyn",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app' ng-controller='myController as sa'>
<select ng-model="selected" ng-options="item.name as item.link for item in sa.cities"></select>
</div>
Hey Guys so i have a page that displays a list inside ionic.
TLDR - Basically i want a buy button that increases {{recipe.quantity}}??
That list just lists uses ng-repeat "recipe in recipes" to list them all.
When i click on one it loads the details page which pulls data from a dictionary
how do i make a button appear on the details page that increase the quantity of that item and make that item callable by a span?
does this all make sense? is there a better way to have an amount of items that i dont have to manually code each button, just have the button check the price of the item inside the dictionary and increase it if possible updating a span?
if you need further info just ask, I hope it's clear what i am asking.
http://jsfiddle.net/d8dc2opm/
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
});
})
.config(function($stateProvider, $urlRouterProvider) {
// Ionic uses AngularUI Router which uses the concept of states
// Learn more here: https://github.com/angular-ui/ui-router
// Set up the various states which the app can be in.
// Each state's controller can be found in controllers.js
$stateProvider
// setup an abstract state for the tabs directive
.state('tab', {
url: "/tab",
abstract: true,
templateUrl: "templates/tabs.html"
})
// Each tab has its own nav history stack:
.state('tab.main', {
url: '/main',
views: {
'tab-main': {
templateUrl: 'templates/tab-main.html',
controller: 'mainCtrl'
}
}
})
.state('tab.dash', {
url: '/dash',
views: {
'tab-dash': {
templateUrl: 'templates/tab-dash.html',
controller: 'DashCtrl'
}
}
})
.state('tab.recipes', {
url: '/recipes',
views: {
'tab-recipes': {
templateUrl: 'templates/tab-recipes.html',
controller: 'RecipesCtrl'
}
}
})
.state('tab.recipe-detail', {
url: '/recipes/:recipeId',
views: {
'tab-recipes': {
templateUrl: 'templates/recipe-detail.html',
controller: 'RecipeDetailCtrl'
}
}
})
.state('tab.buildings', {
url: '/buildings',
views: {
'tab-buildings': {
templateUrl: 'templates/tab-buildings.html',
controller: 'BuildingsCtrl'
}
}
})
.state('tab.building-detail', {
url: '/building/:buildingId',
views: {
'tab-buildings': {
templateUrl: 'templates/building-detail.html',
controller: 'BuildingDetailCtrl'
}
}
})
.state('tab.upgrades', {
url: '/upgrades',
views: {
'tab-upgrades': {
templateUrl: 'templates/tab-upgrades.html',
controller: 'UpgradesCtrl'
}
}
})
.state('tab.upgrade-detail', {
url: '/upgrade/:upgradeId',
views: {
'tab-upgrades': {
templateUrl: 'templates/upgrade-detail.html',
controller: 'UpgradeDetailCtrl'
}
}
})
.state('tab.account', {
url: '/account',
views: {
'tab-account': {
templateUrl: 'templates/tab-account.html',
controller: 'AccountCtrl'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/tab/main');
});
var milk = localStorage.getItem("milk") ? localStorage.getItem("milk") : 0.0;
var totalMilk = localStorage.getItem("totalMilk") ? localStorage.getItem("totalMilk") : 0.0;
var milkRate = localStorage.getItem("milkRate") ? localStorage.getItem("milkRate") : 1.0;
var cash = localStorage.getItem("cash") ? localStorage.getItem("cash") : 0.0;
var totalCash = localStorage.getItem("totalCash") ? localStorage.getItem("totalCash") : 0.0;
var butter = localStorage.getItem("butter") ? localStorage.getItem("butter") : 0.0;
function prettify(input){
var output = Math.round(input * 1000000)/1000000;
return output;
}
$("#milkButton").click(function(e) {
milk += milkRate;
totalMilk += milkRate;
document.getElementById("milk").innerHTML = prettify(milk);
document.getElementById("totalMilk").innerHTML = prettify(totalMilk);
});
angular.module('starter.services', [])
.factory('Recipes', function() {
// Might use a resource here that returns a JSON array
// Some fake testing data
var recipes = [{
name: 'MilkShake',
quantity: 0,
sellPrice: 1.5,
milkCost: 2,
id: 0,
face:''
}, {
name: 'Butter',
quantity: 0,
sellPrice: 1.5,
milkCost: 2,
id: 1,
face:''
}, {
name: 'Cream',
quantity: 0,
sellPrice: 2,
milkCost: 2,
id: 2,
face:''
}, {
name: 'Ice Cream',
quantity: 0,
sellPrice: 4.5,
milkCost: 2,
id: 3,
face:''
}, {
name: 'Cake',
quantity: 0,
sellPrice: 5,
milkCost: 2.5,
id: 4,
face:''
}];
return {
all: function() {
return recipes;
},
remove: function(recipe) {
recipes.splice(recipes.indexOf(recipe), 1);
},
get: function(recipeId) {
for (var i = 0; i < recipes.length; i++) {
if (recipes[i].id === parseInt(recipeId)) {
return recipes[i];
}
}
return null;
}
}
})
<ion-view view-title="{{recipe.name}}">
<ion-content class="padding">
<img ng-src="{{recipe.face}}" style="width: 64px; height: 64px">
<h2>{{recipe.name}}</h2>
<p>You Have {{recipe.quantity}}</p>
<p>Takes {{recipe.milkCost}} Milk</p>
<p>Sells for ${{recipe.sellPrice}} Each</p>
<button id = "recipeButton">Buy</button>
</ion-content>
</ion-view>
so what you're trying to do is call a function inside an ng-repeat to modify the data of one element. I have a demo that shows, in principle, what you're trying to do. I hope this helps you solve your problem.
Check out this plunkr:
http://plnkr.co/edit/cfNGnx5eNXTuirCduWaG?p=preview
Template:
<body ng-app="plunker" ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div ng-repeat="elem in data">
{{elem.id}} {{elem.count}}
<button ng-click="increment($index)">increment {{elem.id}}</button>
</div>
</body>
Here's the controller
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.data = [
{id: 'a', count: 0},
{id: 'b', count: 0},
{id: 'c', count: 0}
]
$scope.increment = function(at) {
$scope.data[at].count += 1;
}
});
In a controller I have defined two different arrays and I need to pass different arrays to directives, but the same item array is getting passed only.
Actually this is just snippets. In my actual code this directive I am using inside another.
Is that causing problem or any other way?
<div my-sticky tags="items" template_name="test2.html"></div>
<div my-sticky tags="kititems" template_name="test2.html"></div>
JS:
app.controller('MyCtrl', function($scope, $http) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
templateUrl : function(el, attrs) {
return attrs.templateName;
},
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});
Try this one
Working Demo
Inside your templateURL(test2.html) should like this i.e,
<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>
You might try to access items or kititems both are not in current scope because you have used isolated scope here.For clarity template used here instead of templateURL.
HTML Markup:
<div ng-controller="MyCtrl">
MyCtrl
<div my-sticky tags="items"></div>
<hr/>
<div my-sticky tags="kititems"></div>
</div>
angular modules
var app = angular.module("myApp",[]);
app.controller('MyCtrl', function($scope) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
template :'<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>',
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
// console.log($scope);
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});
Below is the code i am trying to execute but it is not routing to View1.
in View1 i am just looping through each element of Simplecontroller.
Please help.
<!DOCTYPE html>
<html data-ng-app="App">
<head>
<title>a</title>
</head>
<body>
<script src="Scripts/angular.min.js" type="text/javascript"></script>
<script src="Scripts/angular-route.min.js" type="text/javascript"></script>
<script type="text/javascript">
var App = angular.module('App', ['ngRoute']);
App.config(function ($routeProvider) {
$routeProvider
.when('/', { templateUrl: 'Partials/View1.htm', controller:'SimpleController' })
.when('/partial2', { templateUrl: 'Partials/View2.htm', controller: 'SimpleController' })
.otherwise({ redirectTo: '/AJTest' });
});
App.controller('SimpleController', function ($scope) {
$scope.customers =
[
{ name: 'a', city: 'a' },
{ name: 'b', city: 'b' },
{ name: 'c', city: 'c' },
{ name: 'd', city: 'd' }
];
$scope.addCustomer = function () {
$scope.customers.push({ name: $scope.newCustomer.name, city: $scope.newCustomer.city });
}
});
</script>
<div data-ng-controller="SimpleController">
Name :<br />
<input type="text" data-ng-model="name" /> {{ name }}
<ul>
<li data-ng-repeat="cust in customers | filter:name | orderBy:city">{{ cust.name + ' ' + cust.city}} </li>
</ul>
</div>
</body>
</html>
thanks in advance.
All in all the "code" you posted doesn't make sense, first of all if you wish to use ngRoute and populate views with templates, you need a ng-view somewhere, secondly the code executes SimpleController which generates the expected output in the main application, not in a view... Anyways... Here is a Plunker that does what I think your trying to do:
http://plnkr.co/edit/oVSHzzjG3SrvpNsDkvDS?p=preview
Application:
var App = angular.module('App', ['ngRoute']);
App.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'view1.html',
controller: 'View1Controller'
})
.when('/partial2', {
templateUrl: 'view2.html',
controller: 'View2Controller'
})
.otherwise({
redirectTo: '/404'
});
});
App.controller('View1Controller', function($scope) {
$scope.customers = [{
name: 'a',
city: 'a'
}, {
name: 'b',
city: 'b'
}, {
name: 'c',
city: 'c'
}, {
name: 'd',
city: 'd'
}];
$scope.addCustomer = function() {
$scope.customers.push({
name: $scope.newCustomer.name,
city: $scope.newCustomer.city
});
}
});
App.controller('View2Controller', function($scope) {
$scope.hello = "BOOOO!";
});
Main Page:
<!DOCTYPE html>
<html ng-app="App">
<body>
VIEW 1 - VIEW 2
<div ng-view></div>
<script src="https://code.angularjs.org/1.2.16/angular.min.js"></script>
<script src="https://code.angularjs.org/1.2.16/angular-route.min.js"></script>
<script src="script.js" ></script>
</body>
</html>
View1:
HELLO FROM VIEW 1:
<br />
<br />Running through items in the view::
<br />Name :
<br />
<input type="text" data-ng-model="name" />{{ name }}
<ul>
<li data-ng-repeat="cust in customers | filter:name | orderBy:city">{{ cust.name + ' ' + cust.city}}</li>
</ul>