ng-repeat updates all the form - javascript

I'm trying to do kind of a cart in IONIC ( I'm new using this framework as well ).
I have a ng-repeat to fill my screen with products from the database (firebase), inside this ng-repeat I have a form.
My form contais 3 elements, an an and a button. When I fill my form and press add. it works fine. but if I add a second product, my first one gets updated with the value of the new one. Exemple:
1) Add a product A. Qtd: 3 unity: mg.
2) Add a product B. Qtd: 8 utity: g.
Then my first product turn into: product A. Qtd: 8 unity: m8.
Some one can please help me?
Following my HTML and my JS
$scope.category = Category.name;
$scope.amount = {count: '', unit: 'mg'};
function AddToCart(product, amount)
{
if(amount.count == null)
return;
if(!$rootScope.cart)
$rootScope.cart = [];
$rootScope.cart.push({
item: product,
qtd: amount
});
showToast();
}
<div class="item item-product" id="{{$index}}-item" ng-repeat="item in vm.Products | filter:filter.product" >
<p style="width: 95%; margin: 0;">{{item.name}}</p>
<button class="arrow-button" ng-click="showDetails($index)" sytle = "border: none; width: 100%;"><i class="icon ion-ios-arrow-right"></i></button>
<div class="details" id ="{{$index}}-details" style="z-index: 999;">
<hr>
<p> {{item.about}}</p>
<form class="cart-area" id ="{{$index}}-form" ng-submit="vm.AddToCart(item, amount,$index)">
<input type="number" id = "{{$index}}-input" ng-model="amount.count" placeholder="Qtd">
<div class="list">
<select ng-model="amount.unit">
<option>mg</option>
<option selected>g</option>
<option>kg</option>
</select>
</div>
<button><i class="fa fa-plus" aria-hidden="true"></i>Adicionar cotação</button>
</form>
</div>
</div>

Okay, so you were on the right track, but didn't quite understand how to bind the controls to a specific product - you were binding them to a shared scope, which is why when you updated one, you updated all of them. That is the nature of the two-way data binding built in to AngularJS.
What you want to do is bind the ng-model value to a property on the item itself, like so:
var app = angular.module("myApp", [])
.controller("myCtrl", ["$scope",
function($scope) {
var $this = this;
$this.Products = [{
name: "Product 1",
about: "Info About Product 1",
count: 3,
unit: "mg"
}, {
name: "Product 2",
about: "Info About Product 2",
count: 8,
unit: "g"
}, {
name: "Product 3",
about: "Info About Product 3",
count: 2,
unit: "kg"
}, ];
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl as vm">
<h3>Solution using properties on Products</h3>
<div class="item item-product" id="{{$index}}-item" ng-repeat="item in vm.Products | filter:filter.product">
<p style="width: 95%; margin: 0;">{{item.name}}</p>
<div class="details" id="{{$index}}-details" style="z-index: 999;">
<hr>
<p>{{item.about}}</p>
<div ng-form="{{$index}}-form" class="cart-area" ng-submit="vm.AddToCart(item, amount,$index)">
<input type="number" ng-model="item.count" placeholder="Qtd">
<div class="list">
<select ng-model="item.unit">
<option value="mg">mg</option>
<option value="g">g</option>
<option value="kg">kg</option>
</select>
</div>
<button><i class="fa fa-plus" aria-hidden="true"></i>Adicionar cotação</button>
</div>
</div>
</div>
</div>

Related

AngularJS adding a new item and removing an existing item from a list

(1) I am trying to add a new user to a list of items (userList). The functionality works, but there is an issue. I made the list 'selectable' aka.. when a user clicks on an item on the list the textboxes in my html5 code gets populated w/ values from the selected item in the list. This allows the user to edit the individual properties from the item. Right underneath the group of textboxes is my 'add new user' button..... When the app first runs, the textboxes are empty and I fill them w/ appropriate text and click the add button and the new user is appended to the list. However the issues is, when I have already selected an item, edited it... then the textboxes are still populated w/ the item values... now if I click add new user... a new user is added... but now I have duplicate users in my list.. which is fine because I can always edit one of them... However.... it looks like both the new and the old user are now somehow linked... if I edit one of them, the values in the other also change... (I hope this makes sense). I feel that because the new user was created via the selected record of the old user, somehow their indexes are related....can't seem to figure out how to create a new user without having the old user connected to it.
(2) Deleting a user works fine, but except, the user deleted is always from the bottom of the list. I want to be able to select any item in the list and delete that specific item. I tried using something like:-
$scope.userList.splice($scope.userList.indexOf(currentUser), 1);
but to no avail.
My Javascript:-
<script type="text/javascript">
function UserController($scope) {
$scope.userList = [
{ Name: "John Doe1", Title: "xxxx", Company: "yyyy", Place: "zzzz" },
{ Name: "John Doe2", Title: "xxxx", Company: "yyyy", Place: "zzzz" },
{ Name: "John Doe3", Title: "xxxx", Company: "yyyy", Place: "zzzz" },
{ Name: "John Doe4", Title: "xxxx", Company: "yyyy", Place: "zzzz" }
];
$scope.selectUser = function (user) {
$scope.currentUser = user;
}
$scope.addNew = function (currentUser) {
$scope.userList.push(currentUser);
$scope.currentUser = {}; //clear out Employee object
}
$scope.removeItem = function (currentUser) {
// $scope.userList.pop(currentUser);
$scope.userList.splice($scope.userList.indexOf(currentUser), 1);
$scope.currentUser = {}; //clear out Employee object
}
}
</script>
My HTML:-
<div class="row">
<div style="margin-top: 40px"></div>
<div data-ng-app="" data-ng-controller="UserController">
<b>Employee List</b><br />
<br />
<ul>
<li data-ng-repeat="user in userList">
<a data-ng-click="selectUser(user)">{{user.Name}} | {{user.Title}} | {{user.Company}} | {{user.Place}}. </a>
</li>
</ul>
<hr>
<div style="margin-top: 40px"></div>
<b>Selected Employee</b><br />
<br />
<div style="border:dotted 1px grey; padding:20px 0 20px 0; width:40%;">
<div class="row" style="margin-left: 30px">
<div style="display: inline-block;">
Name:
</div>
<div style="display: inline-block; margin-left: 35px;">
<input type="text" data-ng-model="currentUser.Name">
</div>
</div>
<div style="margin-top: 20px"></div>
<div class="row" style="margin-left: 30px">
<div style="display: inline-block;">
Title:
</div>
<div style="display: inline-block; margin-left: 45px;">
<input type="text" data-ng-model="currentUser.Title">
</div>
</div>
<div style="margin-top: 20px"></div>
<div class="row" style="margin-left: 30px">
<div style="display: inline-block;">
Company:
</div>
<div style="display: inline-block; margin-left: 10px;">
<input type="text" data-ng-model="currentUser.Company">
</div>
</div>
<div style="margin-top: 20px"></div>
<div class="row" style="margin-left: 30px">
<div style="display: inline-block;">
Place:
</div>
<div style="display: inline-block; margin-left: 35px;">
<input type="text" data-ng-model="currentUser.Place">
</div>
</div>
</div>
<div>
<div style="margin: 2% 0 0 8%; display:inline-block">
<button data-ng-click="addNew(currentUser)" class="btn btn-primary" type="button">Add New Employee</button>
</div>
<div style="margin: 2% 0 0 1%; display:inline-block">
<button data-ng-click="removeItem(currentUser)" class="btn btn-primary" type="button">Delete Employee</button>
</div>
</div>
<hr>
<div style="margin-top: 40px"></div>
<b>Employee Details:</b><br />
<br />
{{currentUser.Name}} is a {{currentUser.Title}} at {{currentUser.Company}}. He currently lives in {{currentUser.Place}}.
</div>
</div>
* EDIT * I solved the delete user issue as:-
$scope.removeItem = function (currentUser) {
if ($scope.userList.indexOf(currentUser) >= 0) {
$scope.userList.splice($scope.userList.indexOf(currentUser), 1);
$scope.currentUser = {}; //clear out Employee object
}
}
and thanks to the suggestion, the add new user issue has also been resolved.
You have two issues. In $scope.addNew():
$scope.userList.push(currentUser);
This line pushes a reference to the same object you are currently editing. This is why the users appear linked, because you have the same object in the list twice. You instead need to copy the properties of the object onto a new object, which you can do in this case with angular.extend():
$scope.userList.push(angular.extend({}, currentUser));
You might instead consider having the "add new" button just add a blank user to the list and select it for editing:
$scope.addNew = function () {
$scope.userList.push($scope.currentUser = {});
};
In $scope.removeItem(), you use the pop() method of the array to try to remove a specific item, but pop() removes the last item and doesn't actually accept any arguments:
$scope.userList.pop(currentUser);
You could iterate the list to remove a specific object:
var i;
for (i = 0; i < $scope.userList.length; ++i) {
if ($scope.userList[i] === currentUser) {
$scope.userList.splice(i, 1);
break;
}
}
Or you could use indexOf() but test that the return value is not equal to -1, or the splice() call will remove the last element from the list.

Using the same controller for two identical divs with different data and using xeditable for second div

So I'm trying to duplicate the section div (so I can have multiple sections with multiple articles). I tried using the same controller for both divs as shown below. So I'm able to add the section by appending it to main but I can't edit the second div. Is there any way around this?
I am not using bootstrap and i'm using xeditable.
HTML:
<div id="main" ng-app="main">
<div class = "section" ng-controller="newsController">
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class = "article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
<div class = "section" ng-controller="newsController">
</div>
</div>
JS:
newsletter.controller('newsController',function($scope){
$scope.sections = {
section: "Faculty",
articles: [
{
title: "In __ We Trust",
link:'http://wee.com',
publisher: "Me",
orig_title:"",
description: "Description Here"
}
]
};
$scope.addItem = function(){
$scope.sections.articles.push(this.sections.articles.temp);
$scope.sections.articles.temp={};
};
)};
var newSection = '//Pretend all the tags and the attributes as above are placed here'
$("#add-section").click(function(){
var $section = $('#main').append(newSection);
});
Apologies for formatting. I'm still new to this. Thanks!
Edit: I'm also trying to make this dynamic so the user could edit the texts like the title and the publisher, etc. How would I make the added section also editable?
Try it this way, instead of appending it uses angulars natural way of repeating divs aka ng-repeat:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.sections = {
section: "Faculty",
articles: [{
title: "In __ We Trust",
link: 'http://wee.com',
publisher: "Me",
orig_title: "",
description: "Description Here"
}]
};
$scope.addItem = function() {
$scope.sections.articles.push(this.sections.articles.temp);
$scope.sections.articles.temp = {};
};
var newSection = '//Pretend all the tags and the attributes as above are placed here'
$("#add-section").click(function() {
var $section = $('#main').append(newSection);
});
});
</script>
<div id="main" ng-app="myApp" ng-controller="myCtrl">
<div class="section" ng-repeat="i in ['0','1']">
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class="article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
<div class="section" ng-controller="myCtrl"></div>
</div>
I Got the answer! So I applied the app to the html document and the controller to the body as opposed to the main div and made an array of sections as opposed to a singular section. I did a ng-repeat for the section div. By doing so, I added a "addsection" function where I create a section to be added to the array and the section has to have the same properties as the other ones including an empty array of articles.
HTML:
<body ng-controller="newsController">
<ul id="convenient-buttons">
<li id="add-section">Add Section</li>
<li>Select All</li>
<li><a href=# id="export" onclick="selectText('json')" ng-mouseenter="showJson=true" ng-mouseleave="showJson=false" >Send to Server</a></li>
</ul>
<div id="main">
<div class = "section" ng-repeat="section in news.sections" >
<h2 id="section" editable-text="sections.section">{{sections.section}}</h2>
<div class = "article" ng-repeat="article in sections.articles">
<h3 id="title" editable-text="article.title"><a editable-text="article.link" href="{{article.link}}">{{article.title}}</a></h3>
<p id="publisher" editable-text="article.publisher">{{article.publisher}}</p>
<p id="orig_title" editable-text="article.orig_title">{{article.orig_title}}</p>
<p id="descr" ng-bind-html="article.description" editable-text="article.description"></p>
</div>
</div>
</body>
JS:
$scope.news = {
sections: [
{
title: "Faculty",
articles: [
{
title: "In Memoriam: Eli Pearce",
link:'http://engineering.nyu.edu/news/2015/05/29/memoriam-eli-pearce',
publisher: "NYU Newsroom",
orig_title:" ",
description: "When <strong>Professor Eli Pearce</strong> passed away on May 19, 2015, a slice of Poly history passed along with him. Pearce had been affiliated with the school, then known as the Polytechnic Institute of Brooklyn, since the mid-1950s, when he conducted his doctoral studies in chemistry here. As a student, he learned with such luminaries as Herman Frances Mark, who is often called the Father of Polymer Science, and Charles Overberger, another influential chemist who helped establish the study of polymers as a major sub-discipline."
}
]
}
]
};
$scope.addSection = function(){
$scope.news.sections.temp={
title: "Section Title",
articles:[
// {
// title:"Article Title",
// link:"Link",
// publisher: "Publisher",
// orig_title: "Original Title",
// description: "Description"
// }
]
};
$scope.news.sections.push(this.news.sections.temp);
};

Angular ng-switch inside select option

I want the [Data Presentation Format] applied to [Dropdown Box]. I need the "parent" to be presented as is inside the dropdown box, but I need the "child" to be presented with with a [tab], to show the parent-child relationship.
$scope.itemlist will be prepared in order (i.e. parenta, childa1, childa2, parentb, childb1, childb2)
The HTML:
<div ng-controller="MyCtrl">
<h1>-- Data Presentation Format --</h1>
<ul>
<li ng-repeat="item in itemlist">
<div ng-switch on="item[1]">
<div ng-switch-when="Parent">{{item[0]}}</div>
<div ng-switch-default> {{item[0]}}</div>
</div>
</li>
</ul>
<br/>
<h1>-- Dropdown Box --</h1>
<select ng-model="loc1" ng-options="item[0] for item in itemlist">
<option value="">Item</option>
</select>
<br/><br/>
<h1>-- What I Got --</h1>
<select ng-model="loc2">
<option ng-repeat="item in itemlist">
<div ng-switch on="item[1]">
<div ng-switch-when="Parent">{{item[0]}}</div>
<div ng-switch-default> {{item[0]}}</div>
</div>
</option>
</select>
The javascript:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.itemlist =
[
["Unit A", "Parent"],
["Room A", "Child"],
["Room B", "Child"],
["Room C", "Child"],
["Room D", "Child"],
["Room E", "Child"],
["Unit 1", "Parent"],
["Room 1", "Child"],
["Room 2", "Child"],
["Room 3", "Child"]
];
}
JsFiddle here: http://jsfiddle.net/HB7LU/11174/
I don't think you can use   inside the option element, as its not rendered as html. If you insist on using the element instead of something like a jQuery dropdown (which basically is a ), then i'd suggest using dashes or similar to illustrate parent/child relationship.
http://jsfiddle.net/HB7LU/11177/
I solved it using a formatting function i placed on the scope
$scope.formattedItem = function(item) {
return item[1] === 'Parent' ? item[0] : '--' + item[0];
<select ng-model="loc2">
<option ng-repeat="item in itemlist" >
{{formattedItem(item)}}
</option>
</select>

multidimensional array in angular

I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.

emberjs arraycontroller issue

I am new to Ember and am having an issue. I would like the user to be able to select a number of workstations, and when they hit the next button, I would like the controller to create a number of objects equal to the number the user selected. Once they are taken to the next screen I want to view to append a number of divs with the questions equal to the number the user selected.
I have this for the app.js:
//Initialize the application
App = Ember.Application.create({
rootElement: '#main'
});
//Initialize the data model
App.CustomerController = Ember.Object.extend({
first: null,
last: null,
email: null,
phone: null,
clinic: null,
number: null
});
App.Workstation = Ember.Object.extend({
id: null,
title: null,
newOrExisting: null,
cabling: null
});
App.workstationController = Ember.ArrayController.create({
content: [],
num: null,
init: function() {
this.set('content',[]);
var num = this.get('num');
var tempId = Date.now();
var ws = App.Workstation.create({
id: tempId
});
this.pushObject(ws);
}
});
App.selectNoComputers = ["1", "2", "3", "4", "5"];
App.workstationSelect = ["Counter 1", "Counter 2", "Counter 3", "Counter 4", "Office 1", "Office 2", "Office 3"];
App.yesNo = ["New", "Existing"];
App.Router.map(function(match) {
match("/").to("captcha");
match("/customer").to("customer");
match("/wsnum").to("computers");
match("/overview").to("overview");
});
App.CaptchaRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('captcha');
}
});
App.CustomerRoute = Ember.Route.extend();
App.ComputersRoute = Ember.Route.extend();
App.OverviewRoute = Ember.Route.extend({
});
App.initialize();
And this for my html:
<script type="text/x-handlebars" data-template-name="overview">
<div class="navbar">
<div class="navbar-inner">
<div class="progress-bar-label-div">
Progress:
</div>
<div class="progress-bar-div">
<div class="progress progress-striped">
<div class="bar" style="width:60%;"></div>
</div>
</div>
<div class="btn-group pull-right">
{{#linkTo "computers" class="btn"}}
Prev
{{/linkTo}}
</div>
</div>
</div>
<div class="row-a top">
<div class="pull-left" >
<h3>Workstation Overview</h3>
</div>
<div class="pull-right">
</div>
</div>
{{#each App.workstationController}}
<div class="workstation-b">
<div class="row-b">
<div class="pull-left workstation-title" >
<h4>{{id}}</h4>
</div>
<div class="pull-right form-inputs input-text">
<a class="btn btn-primary" >
Start
</a>
</div>
</div>
<div class="row-b">
<div class="pull-left questions">
What station will this be?
</div>
<div class="pull-right form-inputs input-text">
{{view Ember.Select prompt="Please Select" contentBinding="App.workstationSelect"}}
</div>
</div>
<div class="row-b">
<div class="pull-left questions">
Is this computer a new purchase or replacing and existing workstation?
</div>
<div class="pull-right form-inputs input-text">
{{view Ember.Select prompt="Please Select" contentBinding="App.yesNo"}}
</div>
</div>
</div>
{{/each}}
</script>
I'm sure I'm missing something pretty easy, but any help is appreciated.
I put together a fiddle to illustrate my problem.
Working fiddle: http://jsfiddle.net/mgrassotti/xtNXw/3/
Relevant changes: Added an observer to listen for changes to the num property. When it changes, the content array is reset and then an appropriate number of blank workstation objects are created.
App.workstationController = Ember.ArrayController.create({
content: [],
num: null,
init: function() {
this.set('content',[]);
},
addBlankWorkstation: function() {
var tempId = Date.now();
var ws = App.Workstation.create({
id: tempId
});
this.pushObject(ws);
}
});
App.workstationController.addObserver( 'num', function() {
var num = this.get('num');
this.set('content',[]);
for (var i=0;i<num;i++) {
this.addBlankWorkstation();
}
});
I hesitated to say working fiddle above since there are many things that might be worth refactoring. You'll find most of the complexity could be reduced by following ember naming conventions. Suggest looking at latest guides for more detail.

Categories

Resources