KnockoutJS dynamically set input's value - javascript

I'm trying to dynamically set select input's value using knockoutjs data-bind. If I just put value: $parents[0].selectedSubcategory in the <select> element's attributes, it works fine. But when I try to pass the value in, it doesn't work.
My view looks like this -
<div data-bind="with: QuestionFilter">
<form>
<div data-bind="foreach: details">
<select data-bind="options: subcategories, optionsText: 'name', optionsValue: 'categoryID', value: subcategoriesValue">
</select>
</div>
</form>
</div>
<script type="text/javascript">
ko.applyBindings({
categories = <?php echo $categories; ?>,
details = ko.observableArray([])
});
</script>
And my JS looks like this -
function QuestionFilter(categories, details) {
var self = this;
self.subcategories = ko.observableArray([]);
self.selectedSubcategory = ko.observable();
function search(nameKey, myArray){
for (var i=0; i < myArray.length; i++) {
if (myArray[i].parentCategory_id === nameKey) {
self.subcategories.push(myArray[i]);
}
}
}
search(2, categories);
details.push({ firstName: self.subcategories(), lastName: self.selectedSubcategory()});
});
self.selectedSubcategory.subscribe(function(subcategory) {
function subsearch(nameKey, myArray){
for (var i=0; i < myArray.length; i++) {
if (myArray[i].parentCategory_id === nameKey) {
self.superSubcategories.push(myArray[i]);
}
}
}
subsearch(subcategory, categories);
details.push({ firstName: self.superSubcategories()});
});
In addition to the above, I have tried using lastName: $parents[0].self.selectedSubcategory(), with and without quotes. I have also tried setting the <select> element's value like this: value: $parents[0].lastName also with and without quotes and +, but no joy.
Any thoughts or clarification needed?

Related

Knockout giving previously selected value

I have two connected dropdowns that I'm using with Knockout. Both of them I'm populating with a simple Value,Text pair that I load from an ajax call.
When I go to change the value however I always get the previous value from the selected list. So if the default RegionCode value is "-1" the first RegionCode value that will be passed to the loadLocationList when I change it to another region will be "-1" even if the actual selected value is different that what I get by inspecting the element or through JQuery
Model
define([
'util',
'locationService',
'jquery',
'knockout',
'knockoutmapping',
function(util, svc, $, ko, mapped) {
var LocationViewModel = function(regionCodes, regionCode, locationCodes, locationCode, currentYear) {
var self = this;
self.CurrentYear = currentYear;
self.RegionCode = ko.observable(regionCode)
self.RegionCodes = ko.observableArray(regionCodes)
self.LocationCode = ko.observable(locationCode)
self.LocationCodes = ko.observableArray(locationCodes)
self.loadLocationList = function() {
self.LocationCodes([]);
var locationListCallback = function(data){
for (var i = 0; i < data.length; i++) {
self.LocationCodes.push(new SelectListPair(data[i].Value, data[i].Text));
}
}
svc.getLocationsInRegion(self.CurrentYear, self.RegionCode, true, locationListCallback);
}
}
var SelectListPair = function (value, text) {
this.Value = ko.observable(value);
this.Text = ko.observable(text);
}
return function summaryViewModel() {
var self = this;
self.LocationSummaryViewModel = ko.observable();
var initViewModel = function() {
$.ajax({
url: 'Url here',
success: function(vm) {
var locationVM = vm.LocationSummaryViewModel;
var selectListArray = locationVM.LocationList;
var selectList = [];
for (var i = 0; i < selectListArray.length; i++) {
var SelectListPair = {
Value: ko.observable(selectListArray[i].Value),
Text: ko.observable(selectListArray[i].Text),
};
selectList.push(SelectListPair);
}
var location = [new SelectListPair("-1", "Please select a location")];
self.LocationSummaryViewModel(new LocationViewModel(vm.CurrentYear, selectList, "-1", location, "-1"));
},
});
}
}
self.initViewModel();
});
View
<!-- ko with: LocationSummaryViewModel() -->
<div class="panel panel-default" data-bind="visible: Visible()">
<div class="panel-heading">
<div class="row">
<div class="col-md-4">
Location Summary
</div>
<div class="col-md-4">
<select class="form-control" data-bind="
options: RegionCodes(),
disable: RegionCodes().length === 1,
optionsText: 'Text',
optionsValue: 'Value',
event: {change: loadLocationList },
value: RegionCode">
</select>
</div>
<div class="col-md-4">
<select class="form-control" data-bind="
options: LocationCodes(),
disable: LocationCodes().length === 1,
optionsText: 'Text',
optionsValue: 'Value',
value: LocationCode">
</select>
</div>
</div>
</div>
<div class="panel-body">
Neat content
</div>
<div class="panel-footer">
</div>
</div>
<!-- /ko -->
Don't bind to the change event on a select. Subscribe to the value variable instead. In Knockout, the idea is always to represent the view in the model, and then use the view exclusively.

Adding of users to dropdown list doesn't work

Good evening!
There's a problem with adding a user to dropdown list (ui-grid is used).
I need to push input name by id into dd list after the "addNewPerson" button is clicked, if there's no such name in the list, or to call "alert", if there is.
Here's a code, responsible for the dd list creation in html:
<ui-select ng-model="person.selected" theme="select2" style="min-width:300px;">
<ui-select-match placeholder="Select a person in the list or search by name">{{$select.selected.name}}
</ui-select-match>
<ui-select-choices repeat="person in contacts | filter: {name: $select.search} track by $index">
<div ng-bind-html="person.name | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
Button and input field:
<button type="button" id="addPerson" class="button" ng- click="addNewPerson()">Add New Person</button>
<input id="name" class="form-control" placeholder="Enter your Name">
Array of objects with the "name" field, which needs to be passed into the dd list:
$scope.contacts = [
{name: "Han Solo"},
{name: "ThetaSigma"},
{name: "Ollie Reeder"},
{name: "Amy McDonald"},
{name: "PJ Harvey"},
{name: "Sofie Marceau"},
{name: "Arthur Zimmermann"},
{name: "Michelle Dockery"},
{name: "Xavier Dolan"}
];
And, at last, the notorious function:
$scope.person = {};
$scope.addNewPerson = function () {
var nameInput = document.getElementById("name");
for (var i=0; i <= $scope.contacts.length; i++) {
if ($scope.contacts[i].name == nameInput.value.toLowerCase()) {
alert("Error, the name entered already exists");
}else{
var obj1 = {name: nameInput.value};
$scope.contacts.push(obj1);
}
}
};
I've tried various formations of the function, it either pushes nothing and alerts 10 times, or pushes names correctly, but even already existing ones, or pushes 10 times and alerts 10 times after a single adding.
I'm handicapped. Tried to find similar q/a here, but to no avail.
And sorry for my english, it'snot my native language.
Here's working codepen example.
First Change the html for the input to use scope variable:
<input ng-model="name" class="form-control" placeholder="Enter your Name">
and in the controller:
$scope.name = "";
$scope.addNewPerson = function () {
for (var i=0; i < $scope.contacts.length; i++) {
if ($scope.contacts[i].name.toLowerCase() === $scope.name.toLowerCase()) {
alert("Error, the name entered already exists");
return;
}
}
$scope.contacts.push({name: $scope.name});
};
You ng-model to bind the new user name:
<input id="name" class="form-control" placeholder="Enter your Name" ng-model="new_user_name">
Then in your js, you have to break out of your forloop :
$scope.person = {};
$scope.addNewPerson = function () {
var name = $scope.new_user_name || "";
var match_found = false;
for (var i=0; i <= $scope.contacts.length; i++) {
if ($scope.contacts[i].name == name.toLowerCase()) {
alert("Error, the name entered already exists");
match_found = true;
break;
}
}
if (!match_found) {
var obj1 = {name: name};
$scope.contacts.push(obj1);
}
};
The above answers are fine, I would just like to add the option to use functional programming style as I love to do it and for me it looks cleaner.
View
<input ng-model="name" class="form-control" placeholder="Enter your Name">
Controller
$scope.name = "";
$scope.addNewPerson = function () {
var contactIndex = $scope.contacts.map(function (contact) {
return contact.name.toLowerCase();
}).indexOf($scope.name.toLowerCase());
if (contactIndex === -1)
$scope.contacts.push({name: $scope.name});
else
console.log("Error, the name entered already exists");
}
A few notes:
When working with angular, never access DOM in view controllers, use directives instead
Functional programming style will save you a lot of typing
Using console.log instead of alerting boosts your productivity by approx. 9000%

AngularJS Add More Option For Input Type File

Suppose I want to add 'Add More' button next to input type file field.
So that on click of 'Add More' button one more file field will be created.
I could use '.clone()' for this in JQuery.
But how will I do this in AngularJS?
Thanks!!
In controller:
$scope.addMore = function() {
$scope.inputs += 1;
};
$scope.range = function(count) {
var inputs = [];
for (var i = 0; i < count; i++) {
inputs.push(i)
}
return inputs;
}
$scope.inputs = 0;
In HTML:
<input ng-repeat="n in range(inputs)">
<button ng-click="addMore()">Add more!</button>
MORE USEFUL VERSION:
Typically you want to have other information related to the input field though, so depending on your use case, you might just want to have an array of input objects with some properties, and then ng-repeat over that array like following:
Controller:
$scope.inputs = [];
$scope.addMore = function() {
$scope.inputs.push({
id: $scope.inputs.length,
text: "Initial text here"
})
};
HTML:
<input ng-repeat="input in inputs" ng-model="inputs[$index].text">
<button ng-click="addMore()">Add more!</button>
What you want to do is to create an array of input button.
display your input array :
<div ng-repeat="item in items"><input type="text" ng-model="item.value"><div>
attach a function to the ng-click of your button :
<button ng-click="addMore()">Add More</button>
and then define your array in controller :
$scope.items = [];
$scope.addMore = function () {
$scope.items.push({
value:'default'
});

Angularjs enabling disabled dropdowns in the ng-repeat

Hi in following html code I am using ng-repeat to display all the rows with name and the dropdowns.
<div ng-repeat="a in items">
<div>
<span>{{a.name}}</span>
</div>
<div>
<select ng-model="a.c_id" ng-options="d.c_id as d.description for d in loclist" ng-disabled="display" ng-change="selected(a.c_id)">
</select>
</div>
<button ng-click="submit(items)">Submit</button>
In my controller when value of c_id is 3 it disables the dropdown using following code
$scope.display = false;
$scope.selected = function (value) {
this.te = value;
if (this.te == 3) {
this.display = true;
}
};
Using submit button I want to first enable all the dropdowns that were disabled previously. Using the following code
$scope.submit=function(items)
{
for(i=0; i< items.length; i++)
{
this.display=false;
}
}
It doesnt work. It will not set display=false for the dropdown to be enable.
Please let me know how to correct this issue so I can reset the display to false.
Thanks
A simple solution is to bind ng-disabled with an item instead of the scope:
ng-disabled="a.display"
on ng-change, pass the item to the function:
ng-change="selected(a)"
Change your select function to update the display property on an item:
$scope.selected = function (item) {
var value = item.c_id;
this.te = value;
if (this.te == 3) {
item.display = true;
}
};
In your submit function, just loop through the items and reset the display:
$scope.submit=function(items)
{
for(i=0; i< items.length; i++)
{
items[i].display=false;
}
}
I think you have a typo here
ng-click="submit(items)"
Correct it as follows sbumit to submit
$scope.submit=function(items)
{
for(i=0; i< items.length; i++)
{
this.display=false;
}
}

AngularJS - Save as multiple rows - data coming from ng-repeat

Hello new to AngularJS and I am trying to wrap my head around something...
Basically I have a form that can accept 1 to many "Guests" for an event. Using ng-repeat I display the fields like so:
<div ng-repeat="guest in guests">
<input type="text" ng-model="guests[$index].first_name" />
<input type="text" ng-model="guests[$index].last_name" />
<select ng-model="guests[$index].meal" ng-options="meal.id as meal.name for meal in meals"></select>
<select ng-model="guests[$index].rsvp">
<option value="0">No</option>
<option value="1">Yes</option>
</select>
</div>
<div class="controls"><button ng-click="submit()" class="btn btn-success">Save</button></div>
And in the controller:
//DETERMINE TOTAL IN PARTY
var totalInParty = 2;
$scope.meals = RSVPRes.Meals.query();
//EDIT
if ($scope.rsvpId) {
}
//NEW RSVP SUBMISSION
else {
$scope.rsvp = new RSVPRes.RSVP();
//INITIALIZE EMPTY GUESTS
$scope.guests = [];
for (var i = 0; i < totalInParty; i++) {
$scope.guests[i] = {
first_name: '',
last_name: '',
meal: 1,
rsvp: 0
};
}
}
And my Resource
.factory( 'RSVPRes', function ( $resource ) {
return {
RSVP: $resource("../reservations/:id.json", {id:'#id'}, {'update': {method:'PUT'}, 'remove': {method: 'DELETE', headers: {'Content-Type': 'application/json'}}}),
Meals: $resource('../meals.json')
};
})
Now all this works really well - I am just having trouble saving the data. I would like to save each Guest (First Name, Last Name, Meal & RSVP) as it's own row.
If I try this:
$scope.submit = function() {
for(var i = 0; i < $scope.guests.length; i++){
$scope.rsvp.first_name = $scope.guests[i].first_name;
$scope.rsvp.last_name = $scope.guests[i].last_name;
$scope.rsvp.meal_id = $scope.guests[i].meal;
$scope.rsvp.rsvp = $scope.guests[i].rsvp;
$scope.rsvp.$save();
}
$state.transitionTo('rsvps');
};
It creates two rows (total_in_party set to 2) but it's always the 2nd persons data.
Feel like I am close, I looked a quite a few ng-repeat examples, but couldn't find one that deals with my specific case!
Any help is appreciated.
SOLVED
I totally duffed my thinking about the Resource, creating a new RSVP object now everytime in the loop.
$scope.submit = function() {
for(var i = 0; i < $scope.guests.length; i++){
$scope.rsvp = new RSVPRes.RSVP();
$scope.rsvp.first_name = $scope.guests[i].first_name;
$scope.rsvp.last_name = $scope.guests[i].last_name;
$scope.rsvp.meal_id = $scope.guests[i].meal;
$scope.rsvp.rsvp = $scope.guests[i].rsvp;
$scope.rsvp.$save();
}
$state.transitionTo('rsvps');
};
Move $scope.rsvp.$save(); outside the loop

Categories