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;
}
});
Related
In AngularJS ui-select multiple, I can add a limit to it, or create an alert. But I'm not able to do both. If I show the alert, the previous selected options are not cleared from the UI. Codepen: https://codepen.io/pragatij55/pen/mdpzmqp
I know I can use limit="2", but I also want an alert.
<div ng-app="demo" class="ng-cloak" ng-controller="DemoCtrl">
<ui-select multiple ng-model="myModel" theme="bootstrap" ng-disabled="disabled" close-on-select="false" style="width: 800px;" on-select="changed(myModel)" title="Choose a person">
<ui-select-match placeholder="Select person...">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="person.name as person in people | propsFilter: {name: $select.search, type: $select.search}">
<div ng-bind-html="person.name | highlight: $select.search"></div>
<small>
type: <span ng-bind-html="''+person.type | highlight: $select.search"></span>
</small>
</ui-select-choices>
</ui-select>
</div>
JS:
app.controller('DemoCtrl', function ($scope, $http, $timeout, $interval) {
$scope.people = [
{ name: 'var1', type: 'header' },
{ name: 'var2', type: 'site' },
{ name: 'var3', type:'header' },
{ name: 'var4', type:'header' }
];
$scope.changed = function(val) {
if(val && val.length > 2) {
$scope.myModel = $scope.prevModel;
alert("Upto 2 variables can be selected")
} else {
$scope.prevModel = val;
}
}
});
Not sure if this is what you want, but you can remove that 3rd item right before the alert with Array.pop() right here:
$scope.changed = function(val) {
if(val && val.length > 2) {
val.pop();
In context:
app.controller('DemoCtrl', function ($scope, $http, $timeout, $interval) {
$scope.people = [
{ name: 'var1', type: 'header' },
{ name: 'var2', type: 'site' },
{ name: 'var3', type:'header' },
{ name: 'var4', type:'header' }
];
$scope.changed = function(val) {
if(val && val.length > 2) {
val.pop(); // <- add this line
$scope.myModel = $scope.prevModel;
alert("Upto 2 variables can be selected")
} else {
$scope.prevModel = val;
}
}
});
What is the best way of sharing data between controllers using services in AngularJS?
For example, when the user selects an item from the <ion-list> in services.html, I'd like the title of the selected item to be displayed in service.html. {{service.name}} is some pseudocode I've written down to kinda spell out what I am trying to achieve.
services.html
<ion-list>
<ion-item ng-repeat="service in services" href="#/app/services/{{service.id}}" class="item-icon-right">
{{service.title}}
<i class="icon ion-chevron-right icon-accessory">
<span class="badge badge-positive">{{service.total}}</span>
</i>
</ion-item>
</ion-list>
service.html
<ion-view view-title="Playlist">
<ion-content>
<h1>{{service.name}}</h1>
</ion-content>
</ion-view>
controller.js
.controller('ServicesCtrl', ["$scope", "ServicesData", function($scope, ServicesData) {
$scope.services = ServicesData.GetServices()
}])
.controller('ServiceCtrl', function($scope, $stateParams) {
});
services.js
angular.module('starter.services', [])
.service("ServicesData", [function () {
var servicesData = [
{
title: 'Car Repair and Maintenance',
total: 7,
id: 1
},
{
title: 'Cleaning',
total: 1,
id: 2
},
{
title: 'Delivery, Storage and Moving',
total: 6,
id: 3
}
];
return {
GetServices: function () {
return servicesData;
},
GetServiceById: function () {
// do stuff here to get the service by id
}
}
}])
angular.module('starter.services', [])
.service("ServicesData", [function () {
var servicesData = [
{
title: 'Car Repair and Maintenance',
total: 7,
id: 1
},
{
title: 'Cleaning',
total: 1,
id: 2
},
{
title: 'Delivery, Storage and Moving',
total: 6,
id: 3
}
];
return {
getSelected: function(serviceId) {
var selectedService;
servicesData.forEach(function(service) {
if(service.id === serviceId) {
selectedService = service;
}
});
return return selectedService;
},
getServices: function () {
return servicesData;
}
}
}])
.controller('ServicesCtrl', ["$scope", "ServicesData", function($scope, ServicesData) {
$scope.services = ServicesData.setServices();
}])
.controller('ServiceCtrl', function($scope, $stateParams) {
$scope.service = ServicesData.getSelected($stateParams.service);//the param name should be defined in the route config
});
<!--services.html-->
<ion-list>
<ion-item ng-repeat="service in services" href="#/app/services/{{service.id}}" class="item-icon-right">
{{service.title}}
<i class="icon ion-chevron-right icon-accessory">
<span class="badge badge-positive">{{service.total}}</span>
</i>
</ion-item>
</ion-list>
<!--service.html-->
<ion-view view-title="Playlist">
<ion-content>
<h1>{{service.name}}</h1>
</ion-content>
</ion-view>
Hi I have a directive like,
mainApp.directive('myMenu',function(){
return {
restrict : 'E',
scope :{menuItems : "=menuItems"},
compile: function(element, attributes) {
var linkFunction = function($scope, element, attributes){
for (i = 0;i<$scope.menuItems.length;i++){
element.append('<li>'+$scope.menuItems[i].name+'</li>');
}
}
return linkFunction;
}
}
});
I am using it like below in my HTML
<my-menu menuItems="menuItems"></my-menu>
But in the console I am getting an error like
TypeError: Cannot read property 'length' of undefined
The problem could be that, then the linking phase is executed the menuitems may not be loaded so $scope.menuItems may be undefined.
A better solution could be
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "="
},
template: '<ul><li ng-repeat="item in menuItems">{{item.name}}</li></ul>'
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
If you can't use template then
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "=menuItems"
},
link: function($scope, element, attributes) {
$scope.$watch('menuItems', function(value) {
element.empty();
angular.forEach(value, function(item) {
element.append('<li>' + item.name + '</li>');
});
});
}
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
The Issue was with the name I used, like menuItems in directive should be equal to menu-items, solved the issue by repalcing the menuItems to menu.
use $scope.$eval(attributes.menuItems) inside compile function to get menuItems
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);
}
])
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
}
};
});