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>
I have a complex object as shown below:
$scope.document =
{
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
I want to display all the fields present in "GENERAL_FIELDS" and "stn". Fields' value can either be string or array of strings. If it is array, I further want to ng-repeat on it and display the content. Following is my html:
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
But ng-if="Array.isArray(value)" is never true and array fields are being displayed in object form: ["Journal","Article"]. What am I missing ?
Or add this in your controller and leave rest like it is.
$scope.isArray = angular.isArray;
html would be like this :
<div ng-if="isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!isArray(value)">
{{value}}
</div>
Instead of accessing a method on the Array object directly in the template, you should do in your controller. So for example:
<div ng-if="vm.isValueAnArray(value)">
// Some html
</div>
Your controller:
function isValueAnArray(val) {
return Array.isArray(val);
}
I haven't tested it, but logic should be in the controller, not in the template.
This is an issue of Scoping
The scope of the template is relative to $scope in the controller, so when it looks for Array, it will look for that in the controller scope (e.g. $scope.Array).
One option is to use ng-if="window.Array.isArray(value)". See the working example below.
Another option is to set $scope.Array = Array.prototype in the controller. That way there is no need to reference window before calling Array.isArray().
Another option is to create an alias for Array.isArray() in the controller scope:
$scope.isValueAnArray = Array.isArray;
Then call that function to determine if the value is an array.
angular.module('ang', [])
.controller('cont', function($scope) {
//use this to avoid referencing window in the template
//$scope.Array = Array.prototype;
$scope.document = {
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ang" ng-controller="cont">
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="window.Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!window.Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
</div>
I am working with my project that will create quizzes that form. I want it to be submitted into json format, which will be look like this:
[
{
"questions": [
{
"question": "Who is Mark Zuckerberg?",
"options": [
{
"answer": "Facebook CEO",
"correct": 1
},
{
"answer": "Google Programmer",
"correct": 0
}
]
},
{
"question": "Who is the founder of Apple?",
"options": [
{
"answer": "Mark Zuckerberg",
"correct": 0
},
{
"answer": "Bill Gates",
"correct": 0
},
{
"answer": "Steve Jobs",
"correct": 1
}
]
}
]
}
]
I have my form that allows the user to add & delete questions and options. User can also select the correct answer in the list of options.
Here is the JSFiddle link.
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="row">
<button id="btn-add-tab" type="button" class="btn btn-primary pull-right">Add Question</button>
</div>
<div class="row">
<form id="form">
<!-- Nav tabs -->
<ul id="tab-list" class="nav nav-tabs" role="tablist">
<li class="active">Question 1</li>
</ul>
<!-- Tab panes -->
<div id="tab-content" class="tab-content">
<br>
<div class="tab-pane fade in active" id="tab1">
<div class="input-group">
<input type="text" class="form-control" id="question" placeholder="Your question" required>
<span class="input-group-btn">
<button class="btn btn-success" id="add-option" type="button">Add Option</button>
</span>
</div>
<br>
<div id="options">
<!--- OPTIONS -->
<div class="well">
<textarea id="answer" class="form-control" placeholder="Your answer" required></textarea>
<div class="radio"><label><input type="radio" id="correct-answer" name="correct-ans-1" required>Correct Answer</label></div>
</div>
<!--- END OPTIONS -->
</div>
</div>
</div>
</div>
<div class="row">
<button id="btn-get-json" type="submit" class="btn btn-success pull-right btn-block">Get JSON</button>
</div>
</form>
</div>
</div>
</div>
With myjavascript code, I am experiencing an error which only shows json from the first question. Also, it doesn't display the list of options. In my code I used each to get all input fields in the form. Then I used JSON.stringify(); to convert array to JSON.
$("#form").submit(function(e) {
var jsonArr = [];
var obj = {};
var questionsArr = [];
var questionsCont = {};
var tabs = $("#form :input:not(input[type='submit'],button[type='button'])");
$(tabs).each(function(k,v){
var id = $(this).attr("id");
var value = $(this).val();
questionsCont[id] = value;
});
questionsArr.push(questionsCont);
obj["questions"] = questionsArr;
jsonArr.push(obj);
var json = JSON.stringify(jsonArr, null, "\t");
alert(json);
e.preventDefault();
});
I would like to have a json result that will looked-like from the post above. For testing my code, please see this JSFiddle link.
Any help is appreciated. Thank you!
First, IDs are meant to be unique -- so you cannot have two or more elements with the same ID. When you open a new tab or create a new option, you violate that rule.
Therefore, you should change your IDs to classes and/or names (using []). So, what I did is change input elements to use names and other problem elements to use classes.
How I see of going about is to start by looping through each tab pane. On each pane, find the question and its options, and add them to a data structure that will hold all your data. I am using the $.map to translate each tab into a question.
$("#form").submit(function(e) {
e.preventDefault();
var json = {};
// loop through each tab pane
json.questions = $('.tab-pane').map(function() {
return {
question: $('[name^=question]', this).val(),
// loop through each answer
options: $('[name^=answer]', this).map(function() {
return {
answer: $(this).val(),
correct: $(this).siblings('.radio').find('[name^=correct-ans]').prop('checked')
};
}).get()
};
}).get();
alert(JSON.stringify(json, null, "\t"));
});
Demo
<!-- Left Navbar -->
<div class="container-fluid" style="margin-top: 50px">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul ng-repeat="type in types" class="nav nav-sidebar">
<li>{{type}}</li>
<li ng-repeat="sensor in sensors | filter: { type: {{type}} }">{{sensor.name}}</li>
</ul>
</div>
</div>
I am trying to nest a ng-repeat and would like to use the value of the outer repeat to determine the inner repeat. I would like to dynamically build a navigation bar to list all items in a json array of objects and list them under their sensor type (led, fan, motor, ect) Is this possible?
I have a scope variable sensors with all the sensors on the board and a types array that has all the distinct types of sensors.
Assuming the data structure is defined as this
function Ctrl($scope) {
$scope.types = [
"A", "B"
];
$scope.sensors = [{
"type": "A",
"name": "AA"
}, {
"type": "B",
"name": "BB"
}];
}
Use this syntax:
ng-repeat="sensor in sensors | filter: {'type': type}"
Forgive me if I don't explain this well. I'm having a hard time getting my mind around the real problem I'm having. I'm hoping that someone will be able to make sense of my ideas and probably correct my misunderstandings of MVC, the way Angular works, or the structure of my controllers and which components I have tied a controller to. Sorry for the long question, but I've included as many screenshots and as much code as I thought might be helpful in understanding my current predicament. I've been stuck on this for weeks.
I'm building the initial stages of an EHR (Electronic Health Record) application that doctors would use in the office when visiting with patients. Obviously I'm not done styling everything or putting in content to make it look good. I'm also not finished making all of the data dynamic - which is what I'm having a hard time with. The piece I'm working on allows the doctor to select a patient, view information from their past visits, start a visit, and fill in the information for all of their symptoms, diagnoses, and prescription information.
The left menu bar is a controller, the header is a controller, and the bottom part (patient summary) is a controller. I want it to function as you might expect - it loads the header initially and then swaps out the bottom 2/3 of the site (from summary to symptoms, diagnosis, and prescriptions). So, after clicking on Start Visit, it should load the piece below.
As you can see from the first screenshot, the URL is localhost:8080/#/patientSummary/1 where the 1 is the patient ID. Everything must be based off of that ID. So, when a doctor initially selects a patient, it should load that page and base the information in the header and the patient summary off of the ID (using a query to the DB that works just fine). Then in the transition to second screenshot, and all transitions within that page, the header should stay constant.
In each of my views, patientSummary, symptoms, diagnosis, and prescriptions-tests, at the top I have <ng-include src="'templates/header.html'"></ng-include> to get the header. I know this is not good practice. So obviously every time I change a page it re-renders the header. As stated above, I don't want to do it this way, but this is the only way that I could get it to work.
Any ideas on what I could do differently? It needs to work like I've described above, so that the header will stay constant on every page but also will be populated dynamically based off of the patient ID at the same time as the patient summary, but I can't figure out how. I've looked into Services/Cache to share the patient ID between the header and patient summary controllers, but that doesn't seem like the best way to do it, either (and every time I try it comes back as undefined even after I've injected it into the controller).
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Patient Summary</title>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="css/common.css"/>
</head>
<body>
<div id="content" ng-app="osmosisApp" ng-controller="MainCtrl">
<!-- Left Menu -->
<div id="left-menu-wrapper" ng-controller="MenuCtrl">
<div class="left-menu-button" ng-click="showLeftMenu = !showLeftMenu"></div>
<nav id="left-menu" class="left-menu" ng-class="{'open' : showLeftMenu}">
<h3>Patient List</h3>
<block class="patient-button" ng-repeat="patient in patients" ng-click="go('/patientSummary/' + patient.id)">
<img class="patient-button-image" ng-src="{{patient.picture}}"/>
<div id="patient-name-and-status" class="patient-name-and-status">
<h4 class="patient-button-name">{{patient.name}}</h4>
<p class="patient-button-status">{{patient.status}}</p>
</div>
</block>
</nav>
<div id="content-cover" ng-click="showLeftMenu = !showLeftMenu" ng-class="{'content-cover' : showLeftMenu, 'content-uncover' : !showLeftMenu}"></div>
</div>
<!-- /Left Menu -->
<!-- Content -->
<div id="content-frame" ng-view></div>
<!-- /Content -->
</div>
<script src="http://maps.googleapis.com/maps/api/js?libraries=places&sensor=true"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.js"></script>
<script src="lib/angular/angular-route.js"></script>
<script src="js/controllers.js"></script>
</body>
</html>
header.html
<!-- Header -->
<div id="header-wrapper">
<div id="patient-summary-header" class="header-row" ng-controller="HeaderCtrl">
<div id="pic-and-info" class="column-1">
<img id="patient-picture" ng-src="{{patient.picture}}" />
<h2 id="patient-name">{{patient.name}}</h2>
<div id="patient-info">
<p>{{patient.age}}, {{patient.sex}}</p>
</div>
</div>
<div id="patient-vitals-graph" class="column-2">
<canvas id="vitals-graph"></canvas>
</div>
<div id="logo-div" class="column-3">
<img id="logo" ng-src="{{'http://placehold.it/400x150'}}" />
</div>
</div>
</div>
<!-- /Header -->
patientSummary.html (one of the views)
<ng-include src="'templates/header.html'"></ng-include>
<!-- Patient Summary -->
<!-- Nav Buttons -->
<div id="start-visit" class="start-visit-button" ng-click="go('/symptoms')">Start Visit</div>
<!-- /Nav Buttons -->
<div id="patient-summary" class="section group">
<div id="column1" class="column span-2-of-3 height-5-of-5">
<h2>Past Visits</h2>
<div id="past-visits-info" class="info-section height-5-of-5">
<div class="past-visits-display" ng-repeat="pastVisit in patientSummary.pastVisits">
<h5>Diagnosis</h5>
<p>{{pastVisit.diagnosis}}</p>
<h5>Symptoms</h5>
<ul>
<li ng-repeat="symptom in pastVisit.symptoms">{{symptom}}</li>
</ul>
<div class="past-visits-display-date">{{pastVisit.date}}</div>
</div>
</div>
</div>
<div id="column2" class="column span-1-of-3 height-5-of-5">
<h2>Current Conditions</h2>
<div class="info-section height-1-of-5">
<ul>
<li ng-repeat="condition in patientSummary.currentConditions">{{condition}}</li>
</ul>
</div>
<h2>Current Prescriptions</h2>
<div class="info-section height-2-of-5">
<ul>
<li ng-repeat="prescription in prescriptions | currentPrescriptions">{{prescription.name}}</li>
</ul>
</div>
<h2>Expired Prescriptions</h2>
<div class="info-section height-2-of-5">
<ul>
<li ng-repeat="prescription in prescriptions | expiredPrescriptions">{{prescription.name}}</li>
</ul>
</div>
<h2>Patient Questions</h2>
<div class="info-section height-1-of-5">
<ul>
<li ng-repeat="question in patientSummary.questions">{{question}}</li>
</ul>
</div>
</div>
</div>
<!-- /Patient Summary -->
Routes in controllers.js
var osmosisApp = angular.module('osmosisApp', ['ngRoute'], function($routeProvider, $httpProvider) {
$routeProvider
.when('/select-news', {
templateUrl:'templates/select-news.html'
})
.when('/select-news/end-visit', {
templateUrl:'templates/select-news.html',
controller:'EndVisitCtrl'
})
.when('/patientSummary/:id', {
templateUrl:'templates/patientSummary.html',
controller:'SummaryCtrl'
})
.when('/symptoms', {
templateUrl:'templates/symptoms.html',
controller:'SymptomsCtrl'
})
.when('/prescriptions-tests', {
templateUrl:'templates/prescriptions-tests.html',
controller:'PrescriptionsTestsCtrl'
})
.when('/diagnosis', {
templateUrl:'templates/diagnosis.html',
controller:'DiagnosisCtrl'
})
.otherwise({redirectTo:'/select-news'});
// Other magic to make POST stuff work
Controllers in controllers.js
// Main Controller
osmosisApp.controller('MainCtrl', ['$scope', '$location', function ($scope, $location) {
$scope.showHeader = false;
$scope.go = function(path) {
$location.path(path);
};
$scope.$on('$routeChangeSuccess', function() {
$.getScript("lib/chart/Chart.js", function() {
$.getScript("js/chart.js");
});
});
}]);
// Header Controller
osmosisApp.controller('HeaderCtrl', ['$scope', '$http', function ($scope, $http, cacheService) {
//sharedProperties.getId();
//cacheService.get('id');
// Needs to grab the ID from the SummaryCtrl
/*$http.post("/patient/getPatientInfo", {"patient_id" : 1})
.success(function(response) {
console.log("Server response: " + JSON.stringify(response));
});*/
$scope.patient = {
"id" : 1,
"name" : "Mike DeMille",
"age" : "23",
"sex" : "Male",
"picture" : "images/MikeDeMille.png"
};
}]);
// Patient Summary Controller
osmosisApp.controller('SummaryCtrl', ['$scope', '$routeParams', function ($scope, $routeParams, cacheService) {
//sharedProperties.setId($routeParams.id);
//cacheService.put('id', $routeParams.id);
$scope.patientSummary = {
"currentConditions" : ["Lung cancer", "Another awful, life-threatening condition"],
"pastVisits" : [{
"date" : "9/1/2013",
"symptoms" : ["Old age", "Mortality"],
"diagnosis" : "The patient is going to die... Again",
"prescriptions" : [{
"name" : "Prescription name",
"dose" : "Once daily",
"form" : "tablet",
"duration" : "30 days",
"refills" : "3",
"expiration" : "9/1/2014"
},{
"name" : "Prescription name 2",
"dose" : "Twice daily",
"form" : "capsule",
"duration" : "60 days",
"refills" : "3",
"expiration" : "9/1/2014"
}],
"tests" : [{
"name" : "Test name",
"results" : "Blah blah blah, results"
},{
"name" : "Test name 2",
"results" : "Blah blah blah, results 2"
}]
},{
"date" : "7/3/2011",
"symptoms" : ["Promiscuity", "Risk taking"],
"diagnosis" : "The patient is going to die",
"prescriptions" : [{
"name" : "Prescription name 3",
"dose" : "Once daily",
"form" : "tablet",
"duration" : "30 days",
"refills" : "3",
"expiration" : "7/3/2012"
},{
"name" : "Prescription name 4",
"dose" : "Twice daily",
"form" : "capsule",
"duration" : "10 days",
"refills" : "3",
"expiration" : "7/3/2012"
}],
"tests" : [{
"name" : "Test name 3",
"results" : "Blah blah blah, results 3"
},{
"name" : "Test name 4",
"results" : "Blah blah blah, results 4"
}]
}],
"questions" : ["When am I going to die?", "Why am I going to die?"]
}
$scope.prescriptions = [{
"name" : "Prescription name",
"dose" : "Once daily",
"form" : "tablet",
"duration" : "30 days",
"refills" : "3",
"expiration" : "9/1/2014"
},{
"name" : "Prescription name 2",
"dose" : "Twice daily",
"form" : "capsule",
"duration" : "60 days",
"refills" : "3",
"expiration" : "9/1/2014"
},{
"name" : "Prescription name 3",
"dose" : "Once daily",
"form" : "tablet",
"duration" : "30 days",
"refills" : "3",
"expiration" : "7/3/2012"
},{
"name" : "Prescription name 4",
"dose" : "Twice daily",
"form" : "capsule",
"duration" : "10 days",
"refills" : "3",
"expiration" : "7/3/2012"
}
];
}]);
You may want to take a look at ui-router. ui-router supports a more complex templating structure including multiple views in one page.
You could put header template out of ng-view and the data linked with globals $rootScope, and when you change the page, you should remove data from $rootScope to change in the header.
For example, to view only the ID from param....
'index.html'
<ng-include src="'templates/header.html'"></ng-include>
<div id="content-frame" ng-view></div>
'controllers.js'
osmosisApp.controller('SummaryCtrl', ['$scope', '$routeParams', '$rootScope', function ($scope, $routeParams, $rootScope) {
$rootScope.pacientId = $routeParams.id;
.........................
$scope.$on("$destroy", function(){
delete $rootScope.pacientId;
});
'headers.html'
{{pacientId}}
You can see this working in Plunkr