Knockout.js: Combining javascript objects and select options - javascript

In knockoutjs, I want to implement the following: The user can select one of the predefined courses and enter the amount of sessions. The system then displays the total price (amount of sessions multiplied by the price per session for the selected course). I'm still new to knockoutjs so I'm still trying to figure out a lot of things.
So in my program I tried binding an array of javascript objects to select options, but I couldn't find any way to receive the selected object. How can I make the code below work as described? Thanks.
<script type="text/javascript">
// <![CDATA[
$(function() {
var myViewModel = function() {
this.aCourses = ko.observableArray([
{value: 'course_1', text: 'Course 1', price: 35},
{value: 'course_2', text: 'Course 2', price: 39},
{value: 'course_3', text: 'Course 3', price: 30},
{value: 'course_4', text: 'Course 4', price: 43},
]);
this.sSelectedCourse = ko.observable();
this.iSessionCount = ko.observable(0);
this.fTotalPrice = ko.computed(function() { return this.sSelectedCourse().price * this.iSessionCount; }, this);
};
ko.applyBindings(myViewModel);
});
// ]]>
</script>
<div class="main-column-container">
<form class="form-horizontal" role="form" method="post">
<div class="form-group">
<label for="cursus" class="control-label col-sm-3">Rijopleiding</label>
<div class="col-sm-9">
<select class="form-control" id="cursus" name="cursus" data-bind="
options: aCourses,
optionsText: 'text',
value: sSelectedCourse,
optionsCaption: 'Selecteer...'">
</select>
</div>
</div>
<div class="form-group">
<label for="session_count" class="control-label col-sm-3">Amount</label>
<div class="col-sm-9">
<input class="form-control" id="session_count" name="session_count"
data-bind="value: iSessionCount" />
</div>
</div>
<div class="form-group">
<label for="price_total" class="control-label col-sm-3">Total</label>
<div class="col-sm-9">
<p class="form-control-static" data-bind="text: fTotalPrice"></p>
</div>
</div>
</form>
</div>

Conceptually your code is fine, but you have some bugs in your fTotalPrice function: It does not take in account when sSelectedCourse is uninitialized (as it is when the page loads),
and iSessionCount is a function, so it needs to be executed for your calculation to work. An example that should work (not tested):
this.fTotalPrice = ko.computed(function() {
var selectedCourse = this.sSelectedCourse();
return (selectedCourse ? selectedCourse.price : 0) * this.iSessionCount();
},
this);

Related

Angular ng-repeat with mixed elements

I'm trying to convert a form that is using .Net MVC to using Angular. I'm trying to use ng-repeat to produce the input fields within the form. Which this is working as expected. But, I need select fields mixed in with the text input fields.
How do I go about mixing text fields and select fields within one form using ng-repeat? Is it even possible? I'm new to Angular as well. This code is just a very quick example and will be cleaned up and properly structured.
<div ng-app>
<div ng-init="profiles = [
{id: 'first-name', label: 'First Name', type: 'text'},
{id: 'middle-name', label: 'Middle Name', type: 'text'},
{id: 'last-name', label: 'Last Address', type: 'text'},
{id: 'suffix', label: 'Suffix', type: 'text'},
{id: 'social-security-number', label: 'Social Security Number', type: 'text'},
{id: 'DateOfBirth1', label: 'Date of Birth', class: 'datePicker hasDatepicker valid', type: 'text'},
{id: 'years-in-school', label: 'Years in School', type: 'text'},
{id: 'marital-status', label: 'Marital Status', type: 'select'}
]">
<h2>Borrowers Information</h2>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<div ng-repeat="profile in profiles" class="control-group col-md-3">
<label class="control-label" for="{{profile.id}}">{{profile.label}}</label>
<div class="controls">
<input data-val="true" data-val-required="Please enter a valid first name." id="{{profile.id}}" name="{{profile.id}}" value="" type="{{profile.type}}" class="{{profile.class}}">
</div>
</div>
</div>
</div>
<script>
You can use the angular ng-switch or ng-if for the type and add your select options as an array in your form object like this:
https://jsfiddle.net/82u6gj26/
Angular
vm.person = {};
vm.form = [{
type:'text',
label:'First Name',
id:'first_name'
},
{
type:'select',
label:'Marital Status',
id:'marital_status',
options:[
{id:'Single',name:'Single'},
{id:'Married',name:'Married'}
]}
];
HTML
<div ng-repeat="form in ctrl.form">
<div class="form-group">
<label>{{form.label}}</label>
<div ng-switch="form.type">
<input
type="text"
ng-switch-when="text"
ng-model="ctrl.person[form.id]">
<select
ng-switch-when="select"
ng-model="ctrl.person[form.id]"
ng-options="s.id as s.name for s in form.options">
<option>Select One</option>
</select>
</div>
</div>
</div>
You can use ng-if directive (https://docs.angularjs.org/api/ng/directive/ngIf).
For example:
...
<div class="controls">
<input ng-if="profile.type != 'select'">
<select ng-if="profile.type == 'select'">
</select>
</div>
...
yes this possible,
<div ng-repeat="profile in profiles" class="control-group col-md-3">
<label class="control-label" for="{{profile.id}}">{{profile.label}}</label>
<div class="controls">
<input data-val="true" data-val-required="Please enter a valid first name." id="{{profile.id}}" name="{{profile.id}}" value="" type="{{profile.type}}" class="{{profile.class}}" ng-if="{{profile.type!='select'}}">
<select id="{{profile.id}}" name="{{profile.id}}" class="{{profile.class}}" ng-if="{{profile.type=='select'}}">
<option value="{{option.value}}" ng-repeat="option in profile.options">{{option.caption}}</option>
</select>
</div>
</div>
</div>
use ng-if or ng-show to which element to display

Angularjs Populate a select box by selecting two fields in different select boxes

I have 3 select boxes in total. I will select values in 2 select boxes and will click on Submit button. What will be the process to populate the 3rd select box from this. I have data in tables and I am using hibernate in backend. What will be my js file, will the call be post or get. Please help me and give whole procedure.I am using angularjs on frontend side. Please tell me the content of js file as well as the modifications in HTML file. Thank you.
<form name="deviceForm" ng-submit="submitForm()">
<div class="container" style="margin-top:10px">
<div class="row" style="margin-top:20px">
<div class="col-sm-3" align="right">
<h4>Device Family<super style="color:red">*</super></h4>
</div>
<div class="col-sm-2" align="left">
<select class="form-control" ng-model="device.family">
<option>Device 1</option>
<option>Device 2</option>
<option>Device 3</option>
<option>Device 4</option>
<option>Device 5</option>
</select>
</div>
<div class="col-sm-3" align="right">
<h4>Device Type<super style="color:red">*</super></h4>
</div>
<div class="col-sm-2" align="right">
<select class="form-control" ng-model="device.type">
<option>Type 1</option>
<option>Type 2</option>
</select>
</div>
</div>
</div>
<div align="center" style="margin-top:30px">
<button type="submit" value="submit" class="btn btn-info ">Show Devices</button>
</div>
</form>
This is my HTML.
Now after submitting , how would I write in js file to send these Selected options.
With the stinginess of informations you have proved, this is the most I can do:
(() =>
{
'use strict';
angular.module('app', [/**'ng-routing'**/])
.controller('DeviceController', [function ()
{
var self = this;
// self.forms.new
// OR ... angular will handle this for me
// self.forms.edit
self.storage = {}; // to store raw data from service or anywhere, needed for the form initialization
self.storage.types = [{id: 1, label: 'Type 1'}, {id: 2, label: 'Type 2'}, {id: 3, label: 'Type 3'}, {id: 4, label: 'Type 4'}, {id: 5, label: 'Type 5'}];
self.storage.families = [{id: 1, label: 'Device 1'}, {id: 2, label: 'Device 2'}, {id: 3, label: 'Device 3'}, {id: 4, label: 'Device 4'}, {id: 5, label: 'Device 5'}];
self.events = {};
self.events.typeChanged = self.events.familyChanged = function ()
{
self.storage.theThird = undefined; // this will be used to disable submit button while the value is not set, or any service result is waited to set the value
var type = self.data.type || 0;
var family = self.data.family || 0;
if (0 === (type * family) % 2) // you may implement any logic as needed. IF SYNTAX is just an example
{// this code executes only if the `id` at least one of `selected type` or `selected family` is even
self.storage.theThird = [{id: 2, label: '2016-02'}, {id: 4, label: '2016-04'}, {id: 6, label: '2016-06'}, {id: 8, label: '2016-08'}, {id: 10, label: '2016-10'}, {id: 12, label: '2016-12'}];
}
else
{
self.storage.theThird = [{id: 1, label: '2016-01'}, {id: 3, label: '2016-03'}, {id: 5, label: '2016-05'}, {id: 7, label: '2016-07'}, {id: 9, label: '2016-09'}, {id: 11, label: '2016-11'}];
}
}
}]);
})();
...
<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" ng-app="app" ng-cloak ng-strict-di>
<head>
<meta charset="utf-8">
<title>{{app.title}}</title>
<script src="web/libs/jquery/dist/jquery.js"></script>
<script src="web/libs/angular/angular.js"></script>
<script src="web/js/javascript.js"></script>
<style>
label.optional:after
{
content: '*';
color: red;
}
/** Folowing style are just to ease the viewing of changes **/
main {display: flex; flex-direction: row-reverse}
main > aside {flex-grow: 1}
main > form {flex-shrink: 1}
</style>
</head>
<body>
<!-- You may declare your controller this way, or leave it to the responsibility of your router -->
<main ng-controller="DeviceController as ctrl">
<aside>
<pre>{{ctrl|json}}</pre>
</aside>
<form name="ctrl.forms.new"> <!-- This way, we can access our form inside the controller -->
<div>
<label for="new-device-family">
Family
</label>
<select
id="new-device-family"
ng-model="ctrl.data.family"
ng-change="ctrl.events.familyChanged()"
ng-options="family.id as family.label for family in ctrl.storage.families">
<option></option> <!-- this empty option will allow an unselected state -->
</select>
</div>
<div>
<label for="new-device-type" class="optional">
Type
</label>
<select
id="new-device-type"
ng-model="ctrl.data.type"
ng-change="ctrl.events.typeChanged()"
ng-options="type.id as type.label for type in ctrl.storage.types">
<option></option> <!-- this empty option will allow an unselected state -->
</select>
</div>
<div>
<label for="new-device-the-third">
The Thrid
</label>
<select
id="new-device-the-third"
ng-model="ctrl.data.theThird"
ng-options="theThird.id as theThird.label for theThird in ctrl.storage.theThird">
<option></option> <!-- this empty option will allow an unselected state -->
</select>
</div>
<hr>
<button type="reset" ng-click="ctrl.forms.new.$setPristine(); ctrl.forms.new.$setUntouched()">
Reset
</button>
<button type="sumbit" ng-disabled="!ctrl.data.theThird"> <!-- Disabled if the third is any falsy value -->
Reset
</button>
</form>
</main>
</body>
</html>

using ng-show on option select angularjs

I want to bind my input field to my select option. so if the select option is Yes, the input field should be visible and if it is No, the input field should be hidden.
(function(){
var app = angular.module('spa',[
$rootScope.options = [
{
id: 0,
name: 'No'
},
{
id: 1,
name: 'Yes'
}
]
]);
}());
<form name="newData" class="ng-scope ng-pristine ng-invalid ng-invalid-required" error-popup="newData" novalidate>
<div class="form-group item item-input item-select">
<div class="input-label">
Booking Fee Paid
</div>
<select name="booking" ng-model="user.booking" class="form-control ng-pristine ng-invalid ng-invalid-required" ng-options="option.name for option in options track by option.id" ng-init ="user.booking = options[0]" required>
</select>
</div>
<div class="row" ng-show="user.booking.name == 'Yes'">
<div class="col">
<div class="form-group item item-input">
<input type="text" name="amount" ng-model="user.amount" class="form-control" placeholder="Amount">
</div>
</div>
</div>
</form>
http://plnkr.co/edit/v0NrbTeigo3lm1njRu9A?p=preview
Any help is appreciated
I suggest you to go through the beginner tutorials # angularjs.org.
Here is a working sample that does just what you're asking for:
angular.module('app', [])
.controller('Sample', Sample);
function Sample() {
this.options = [{
id: 0,
name: 'No'
}, {
id: 1,
name: 'Yes'
}];
this.booking = this.options[0];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="Sample as vm">
<select name="booking" ng-model="vm.booking" ng-options="option.name for option in vm.options"></select>
<pre>{{ vm.booking | json }}</pre>
<input type="text" ng-show="vm.booking.name === 'Yes'"/>
</div>
Second parameter specifies required modules not the implementation:
angular.module(name, [requires], [configFn]);
So you had inject error. Here is the fixed code:
http://plnkr.co/edit/L02U4Cq0HIqeLL1AOcbl
var app = angular.module('spa', []);
app.controller('MyController', function($scope) {
$scope.options = [{
id: 0,
name: 'No'
}, {
id: 1,
name: 'Yes'
}];
});

Get form textbox values as array using angularjs

I have the following form
<div ng-controller="Formctrl">
<form ng-submit="getNames(pd)">
<fieldset>
<div class="row">
<section class="col col-4" ng-repeat="n in [] | range:5">
<label class="input">
<input type="text" placeholder="Names" ng-model="pd.names[$index]" >
</label>
</section>
</div>
</fieldset>
</form>
</div>
Basically i want to bind the names in an array form submission. But i am getting the following error
TypeError: Cannot set property '0' of undefined
Here is my Controller
angular.module('myApp')
.controller('FormCtrl', function($rootScope,$scope){
$scope.pd = {};
$scope.getNames = function() { console.log($scope.pd); };
});
I need the output something like below. How do i achieve it??
{
names: [
'Name 1',
'Name 2',
'Name 3',
'Name 4',
]
}
Hey You just try to reach an undefined element just define names in controller...
$scope.pd = {names : []};
here is a PLUNKER...

comparing values from two different context using mustache and canjs

Lets say I have this mustache template below. contacts and categories are basically an array of objects:
<script type="text/mustache" id="contactsList">
<ul class="clearfix">
{{#contacts}}
<li class="contact span8">
<i class="icon-remove"></i>
<form>
<div class="row">
<div class="span2">
<img src="img/canjs.jpg" width="100" height="100">
</div>
<div class="span3">
<input type="text" name="name" placeholder="Add Name" value="{{name}}">
<select name="category">
{{#categories}}
<option value="{{data}}" {{sameCategory category data}}>
{{name}}
</option>
{{/categories}}
</select>
</div>
<div class="span3">
<label>Address</label>
<input type="text" name="address" value="{{address}}">
<label>Phone</label>
<input type="text" name="phone" value="{{phone}}">
<label>Email</label>
<input type="text" name="email" value="{{email}}">
</div>
</div>
</form>
</li>
{{/contacts}}
</ul>
</script>
What I want to do is generate "selected" within the option tag by comparing the contacts|category and categories|data.
so what I did was to implement the sameCategory like this:
can.Mustache.registerHelper('sameCategory', function(contactCategoryId, categoryId) {
console.log(contactCategoryId);
console.log(categoryId);
var result = contactCategoryId == categoryId ? "selected" : "";
console.log(result);
return result;
});
Unfortunately, im getting an object for both param instead of strings so my equality condition fails. What am I doing wrong? is there a better way to do this besides registerhelper?
supporting data:
var CONTACTS = [
{
id: 1,
name: 'William',
address: '1 CanJS Way',
email: 'william#husker.com',
phone: '0123456789',
category: 'co-workers'
},
{
id: 2,
name: 'Laura',
address: '1 CanJS Way',
email: 'laura#starbuck.com',
phone: '0123456789',
category: 'friends'
},
{
id: 3,
name: 'Lee',
address: '1 CanJS Way',
email: 'lee#apollo.com',
phone: '0123456789',
category: 'family'
}
];
var CATEGORIES = [
{
id: 1,
name: 'Family',
data: 'family'
},
{
id: 2,
name: 'Friends',
data: 'friends'
},
{
id: 3,
name: 'Co-workers',
data: 'co-workers'
}
];
I took these code from the examples Diving into CanJS article.
I was checking out your code on a fiddle, and I couldn't replicate it, though perhaps you were getting can.computes that you would need to run as function before you got the values.
However, that said, the helper is entirely unnecessary with the can/view/bindings plugin. Just set can-value="category" on your select and it will auto-magically select the correct option (and update the value when changed).
Demonstrated in a fiddle: http://jsfiddle.net/air_hadoken/g725X/1/
<select name="category" can-value="category">
{{#categories}}
<option value="{{data}}" >
{{name}}
</option>
{{/categories}}
</select>

Categories

Resources