Selected ng-options item doesn't stick to 'combo box' - javascript

I'm new to AngularJS and am struggling with something that should be easy.
I have this ugly looking page:
Users add new beers on this screen and a beer may have more than one style - so the 'Add' button creates a new 'Style' combo box and the 'Remove' on the left side, well, removes it.
Problem is when the user selects one style from the list it doesn't appear as selected in the combo box - ie, the style field will be empty after the dropdown list has been collapsed/closed again.
So here is my code:
Beer View
<div class="form-group" ng-repeat="style in beer.styles">
<label for="style" class="col-sm-1 control-label" ng-if="$index == 0" >Style</label>
<div class="col-sm-1" ng-if="$index > 0">
<button type="button" class="btn btn-danger" ng-click="removeStyle($index)">Remove</button>
</div>
<div class="col-sm-10">
<select class="form-control"
ng-controller="styleCtrl"
ng-model="selectedStyleId"
ng-options="style.id as style.name for style in styles"
ng-change="updateStyle($index, selectedStyleId)">
</select>
</div>
<div class="col-sm-1">
<button type="button" class="btn btn-primary" ng-click="addNewStyle()">Add</button>
</div>
</div>
Style Controller (Dummy Impl)
.controller('styleCtrl', ["$scope", "$location", function ($scope, $location) {
$scope.styles = [
{ 'id': '1',
'name': 'Lager' },
{ 'id': '2',
'name': 'American Blonde Ale' },
{ 'id': '3',
'name': 'American Stout' },
{ 'id': '4',
'name': 'Cream Ale' },
{ 'id': '5',
'name': 'Bock' },
{ 'id': '6',
'name': 'German Pilsener' }
];
}]);
Beer Controller
.controller('beerCtrl', ["$scope", "$location", function ($scope, $location) {
$scope.beer = {
'styles': [
{ 'id' : '-1' }
]
}
$scope.addNewStyle = function() {
$scope.beer.styles.push({ 'id': '-1' });
}
$scope.removeStyle = function(index) {
$scope.beer.styles.splice(index, 1);
}
$scope.updateStyle = function(index, styleId) {
$scope.beer.styles[index] = { 'id': styleId };
}
The styleId is correctly set in the array, the only problem really is in the screen.
Have you spotted anything wrong in my code?
P.S.: I suspect the fact I have two controllers - the BeerController and a 'nested' StyleController in the same view may be related to the root cause.

As I mentioned I'm brand new to AngularJS so my mistake was rather simple. Instead of using on-change, I've binded the selected value to the backing object directly:
<select class="form-control"
ng-controller="styleCtrl"
ng-model="beer.styles[$index].id"
ng-options="style.id as style.name for style in styles">
That got the job done. Silly oversight.

Related

Problems with select component

I'm having a problem submitting my list of items from my select, I'm starting with angular and still a little lost. This is my first post here!
In my HTML I use ng-repeat to list all items, and when my screen loads my first and only item is: {{list.name}}, when I click on this item {{list.name}}, ai loads the List correctly.
<select data-placeholder="Escolha uma Empresa/Filial" multiple chosen
style="width: 100%;"
ng-model="filtroRequisicao.codigoSistemaUsuariosFiliais"
required>
<option ng-repeat="list in lista" ng-value="list.id">
{{list.name}}
</option>
</select>
Angularjs:
$scope.lista = [{}];
//Carrega as Filiais dos Cooperados
$scope.loadFiliais = function () {
var usuario = localStorage.getItem("usuarioAutenticado");
var objetoUsuario = {};
objetoUsuario = JSON.parse(usuario);
$http({
method: 'PUT',
url: '/getFiliais',
data: objetoUsuario
}).then(function (response) {
$scope.lista = response.data;
console.log($scope.lista);
},
function (response) {
console.log(response.data);
$scope.showNoty('Nenhum dado encontrado.', 'information');
});
};
$scope.loadFiliais();
One thing I realized was starting my list off of a method PUT call from angular.
$scope.lista =
[
{
'id': 1,
'name': 'American Black Bear',
},
{
'id': 2,
'name': 'Asiatic Black Bear',
},
{
'id': 3,
'name': 'Brown Bear',
},
{
'id': 4,
'name': 'Giant Panda',
},
{
'id': 5,
'name': 'Sloth Bear',
},
{
'id': 6,
'name': 'Sun Bear',
},
{
'id': 7,
'name': 'Polar Bear',
},
{
'id': 8,
'name': 'Spectacled Bear',
}
];
When I fill values out of a function, as an example, my select shows all items. Now if I populate the values inside the return from my angular PUT (response.data), my select gets no items.
It seems to me that the html is being loaded before I even finish loading all my controller in the angle.
I do not know if I'm looking the wrong way, but it seems to me a problem and I do not know how to solve
UPDATE
CSS
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
index.html:
<link rel="stylesheet" href="./vendor/angular/angular-csp.css"/>
form:
<div class="panel-body">
<p class="lt-label-required">Situação</p>
<select data-placeholder="Escolha uma Empresa/Filial" multiple class="chosen ng-cloak" chosen style="width: 100%;"
ng-model="filtroRequisicao.codigoSistemaUsuariosFiliais"
required>
<option ng-repeat="list in lista" value="{{list.id}}">
{{list.name}}
</option>
</select>
</div>
angular:
$scope.loadFiliais= function () {
$http({
method: 'PUT',
url: '/getFiliais'
}).then(function (response) {
$scope.lista = response.data;
console.log($scope.lista);
},
function (response) {
console.log(response.data);
$scope.showNoty('Nenhum dado encontrado.', 'information');
});
};
$scope.loadFiliais();
UPDATE SOLUTION
I was able to solve my problem, but I would like your help to know if this is the correct way:
I used ng-if so that when my $scope.lista is null-dir, ng-if will remove and re-create the elemento.
https://docs.angularjs.org/api/ng/directive/ngIf
Angular:
$scope.lista = [];
Html:
<select data-placeholder="Escolha uma Empresa/Filial" multiple chosen
style="width: 100%;"
ng-if="lista.length > 0"
ng-model="filtroRequisicao.codigoSistemaUsuariosFiliais"
required>
<option ng-repeat="list in lista" ng-value="list.id">
{{list.name}}
</option>
</select>
Edit:
Putting ng-cloak to the body or to the tag takes care of the {{list.name}} issue.
Making $scope.lista = [] and using ng-if inside the select with
ng-if="lista.length > 0"
solves the issue of rendering the select options.
Put an ng-cloak to the body or to the tag.
Read about it here : https://docs.angularjs.org/api/ng/directive/ngCloak.
This will take care of the {{list.name}} issue.
Make $scope.lista = [] not [{}];
An ngIf function can change a preview of your HTML and have ruins consequences in cases of a doubtful or complex upload.
For quick searches like your case, a function has a very valid job.

AngularJS Getting the value of a selected dropdown option in a variable

I have dropdown selection menu & want to send the dropdown selected value in request params of api. My code is...
<select class="form-control" id = "SelectionInput_reason">
<option name="careerType" value="type_1">Career</option>
<option name="examType" value="type_2">Exams</option>
</select>
getValue = function() {
var selectedValue = this.value;
var selectedText = this.options[this.selectedIndex].getAttribute('name');
alert(selectedValue);
alert(selectedText);
}
document.getElementById('SelectionInput_reason').addEventListener('change', getValue );
Please give answer in angularJS if possible...
also how can I get the text input of tinymceeditor in a variable ?
$scope.tinymceModel = 'Initial content';
$scope.getContent = function() {
console.log('Editor content:', $scope.tinymceModel);
};
$scope.setContent = function() {
$scope.tinymceModel = 'Time: ' + (new Date());
};
$scope.tinymceOptions = {
selector: 'textarea',
//plugins: 'link image code',
toolbar: ' bold italic | undo redo | alignleft aligncenter alignright | code'
};
HTML is..
<div class="form-group">
<textarea ui-tinymce="tinymceOptions" id="jander" ng-model="tinymceModel" placeholder="Ask your question" class="form-control"></textarea>
</div>
Bind a model in your select dropdown. Like below
<select class="form-control" id = "SelectionInput_reason" data-ng-model="inputReason">
In your controller you will get selected option
console.log($scope.inputReason)
Here is a sample code with a fiddle attached
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope) {
$scope.shelf = [{
'name': 'Banana',
'value': '$2'
}, {
'name': 'Apple',
'value': '$8'
}, {
'name': 'Pineapple',
'value': '$5'
}, {
'name': 'Blueberry',
'value': '$3'
}];
$scope.cart = {
'fruit': $scope.shelf[0]
};
});
<div ng-controller="MyCtrl">
<div>
Fruit List:
<select ng-model="cart.fruit" ng-options="state as state.name for state in shelf"></select>
<br/>
<tt>Cost & Fruit selected: {{cart.fruit}}</tt>
</div>
</div>
Fiddle link : http://jsfiddle.net/Td2NZ/1867/
My advise in this scenario is try to keep all input choices as a Js Object (Like $scope.shelf in this code) cause thats what Angular is built for rigid and robust handling of the data, eventually you could just load those options from a server or json file and not having to touch your HTML at all!
Hope this helps!

Nested ng-repeat with model binding

HTML:
<div class="panel panel-default" ng-repeat="(section, sectionData) in report">
<div class="panel-heading">{{sectionData.text}}</div>
<div class="panel-body">
<div class="row" ng-repeat="(part, partData) in sectionData.attr">
<div class="col-md-2">
<label>{{partData.text}}</label>
</div>
<div class="col-md-10">
<div class="form-group">
<div class="radio-inline" ng-repeat="condition in radioValues">
<label class="radio-inline">
<input type="radio" name="{{section}}-{{part}}" ng-value="{{condition.value}}" ng-model="partData[model]">
{{condition.text}}
</label>
</div>
</div>
</div>
</div>
</div>
</div>
JS model:
$scope.radioValues = [{
text: 'Good',
value: '1'
}, {
text: 'Average',
value: '2'
}, {
text: 'Needs Improvement',
value: '3'
}];
$scope.report = {
card: {
text: 'Card',
attr: {
front: {
text: 'Front',
model: 'detail.report.card.front',
},
rear: {
text: 'Rear',
model: 'detail.report.card.front.rear'
},
assembly: {
text: 'Assembly',
model: 'detail.report.card.front.assembly'
}
}
} //, and a lot of others like card
};
// Instantiate the model so that values are preselected
for (var section in $scope.report) {
for (var part in $scope.report[section].attr) {
initModel($scope.report[section].attr[part]); // basically sets the model values defined in $scope.report to 1
}
}
The $scope.report object is used to create the html and I'm trying to set the value of ng-model in the html to strings defined in the $scope.report. Along with that, I'm also trying to set the default values of each set of radios.
Is the ng-model="partData[model]" part correct? After setting the model values in the controller, the radios aren't preselected when the page loads. The model defined in the $scope.report should bind to the $scope directly. E.g. detail.report.card.front.assembly should actually become $scope.detail.report...
How do I make this work? Is it the right use of angular? Better alternatives?
I was able to get this done using a directive with isolated scope.
Basically, I shifted the html to a template called report. I changed the template html a little bit. Here's the changed code:
<div class="radio-inline" ng-repeat="condition in radioValues">
<label class="radio-inline">
<input type="radio" name="{{section}}-{{part}}" ng-value="{{condition.value}}" ng-model="detail.report[section][part]">
{{condition.text}}
</label>
</div>
Then created a directive like so:
app.module('').directive('rating', function(){
return {
scope : {
report: "=",
detail: "=",
radios: "="
},
restrict : 'E',
templateUrl : '../view/rating.html',
link : function($scope, iElm, iAttrs, controller) {}
};
});
And in the html I simply call:
<rating report="report" radios="radios" detail="detail"></rating>
So I was able to access the detail object in the parent scope by passing it to the template. This allowed me to modify the parent scope's model directly.

Active button states in Angular + Bootstrap

Seems like a simple problem but I'm actually having trouble with it.
Plunk here
Basically I have a ng-repeat of buttons and then a block of text after that clicking the button will show. However, when I click a button I want to hide the blocks of text from all the other buttons and remove the active states from the other buttons. Basically only 1 button block of text should be shown at a time.
Seems easy, but the way ng-hide handles scope (scope: true) means I can't really look into the other scopes and turn each of them off. The other thing is that I don't want to alter the actual array from ng-repeat if at all possible. This is data from an API that I have to send back and I'm attempting to not alter the actual data structure if I can.
<div class="row" ng-repeat="button in buttons">
<div class="col-sm-2">
<button ng-click="showInfo = !showInfo" class="btn btn-primary">{{button.name}}</button>
</div>
<div ng-show="showInfo" class="col-sm-3">
<div class="alert alert-success">{{button.extraInfo}}</div>
</div>
</div>
And JS
app.controller('MainCtrl', function($scope) {
$scope.buttons = [
{ name: 'One', extraInfo: 'Extra info for button 1' },
{ name: 'Two', extraInfo: 'Extra info for button 2' },
{ name: 'Three', extraInfo: 'Extra info for button 3' }
];
});
I suggest to create new array which has the same length as buttons array, and this array will hold boolean values to indicate where the item active or not.
I didn't log in to plunk so here the modified version of yours.
index.html
<body ng-controller="MainCtrl as vm">
<div class="row" ng-repeat="button in buttons track by $index">
<div class="col-sm-2">
<button ng-click="vm.setActive($index)" ng-class="vm.isActive[$index] ? 'btn btn-primary' : 'btn'">{{button.name}}</button>
</div>
<div ng-show="vm.isActive[$index]" class="col-sm-3">
<div class="alert alert-success">{{button.extraInfo}}</div>
</div>
</div>
</body>
app.js
app.controller('MainCtrl', function($scope) {
$scope.buttons = [
{ name: 'One', extraInfo: 'Extra info for button 1' },
{ name: 'Two', extraInfo: 'Extra info for button 2' },
{ name: 'Three', extraInfo: 'Extra info for button 3' }
];
var vm = this;
vm.isActive =[];
for(var i=0, len=$scope.buttons.length; i < len; i++){
vm.isActive[i] = false;
}
vm.setActive = function(ind) {
for(var i=0, len=vm.isActive.length; i < len; i++){
vm.isActive[i] = i===ind;
}
}
});
If you don't want to change the actual array, then maintain another object or array which will hold the key to each button's show/hide state.
$scope.showInfo = {};
$scope.buttons = [
{ name: 'One', extraInfo: 'Extra info for button 1' },
{ name: 'Two', extraInfo: 'Extra info for button 2' },
{ name: 'Three', extraInfo: 'Extra info for button 3' }
];
$scope.changeShowInfo = function(index) {
for(var prop in $scope.showInfo) {
$scope.showInfo[prop] = false;
}
$scope.showInfo[index] = true;
};
Solved Plunker
You want 1 button active each time, so you better use radio buttons with a currentItem kept in the scope by using ng-bind.
HTML:
<body ng-controller="MainCtrl">
<div name="myForm">
<div ng-repeat="button in buttons">
<label>
<input type="radio" ng-model="$parent.selectedItem" ng-value="button"> {{button.name}}
</label>
</div>
</div>
<div class="alert alert-success">Extra info: {{selectedItem.extraInfo}}</div>
</body>
Didn't need to change your JS.
See Plunker here

Name array with ng-model

I have an add button that uses a directive to add-to a table's (.estimates) tbody:
function EstimateCtrl( $scope, $compile ) {
$scope.services = [
{ 'value': 'c', 'name': 'Standard Courier' },
{ 'value': 'xc', 'name': 'Express Courier' },
{ 'value': 'cc', 'name': 'Country Courier' }
]
$scope.add = function() {
angular.element('.estimates tbody').append( $compile('<tr estimate></tr>')($scope) );
}
}
angular.module('dashboard', [])
.directive('estimate', function() {
return {
restrict: 'A',
template: '<td><input type="text" placeholder="Suburb"/></td><td><select ng-model="estimate.service" ng-options="service.value as service.name for service in services" class="form-control"></select></td><td>$0.00</td><td><button type="button" class="remove">x</button></td>',
link: function( scope, element, attrs ) {
element.find('.remove').bind('click', function() {
element.closest('tr').remove();
});
}
}
});
How can I have an element array using ng-model in angularjs? For example:
<select name="foo[]"></select>
to
<select ng-model="foo[]"></select>
I've been digging around for a day and half but I can't seem to catch a break. I was hoping that maybe someone can point me in the right direction. Thank you very much for any help.
Edit: Here is the link to the plunker I'm sure after seeing this everyone is going know what I'm on about:
http://plnkr.co/edit/JlYB9P0vyAqghOmeNYh4
Edit2: Let's see if I can give you all another example to show you what I'm after
<form method="POST" action="">
<!-- I was attempting to do ng-model="estimate.service[]" but of course this doesn't work -->
<select name="estimate[service][]">
<option value="foor">Foo</option>
<option value="bar">Bar</option>
</select>
<select name="estimate[service][]">
<option value="foor">Foo</option>
<option value="bar">Bar</option>
</select>
<select name="estimate[service][]">
<option value="foor">Foo</option>
<option value="bar">Bar</option>
</select>
<input type="submit" value="Submit"/>
</form>
<?php
if ( $_POST )
{
print_r( $_POST['estimate']['service'] );
}
?>
Output
Ohrighty! I managed to find a work around.
I have abandoned directives and did it another way, here is my working code:
HTML:
<div ng-controller="Ctrl">
<table>
<tbody ng-repeat="service in estimate.services">
<tr>
<td><input type="text" placeholder="Suburb"/></td>
<td>
<select ng-model="estimate.services[service.name]" ng-options="option.value as option.name for option in options" class="form-control"></select>
</td>
<td>$0.00</td>
<td><button type="button" class="remove">x</button></td>
</tr>
</tbody>
</table>
</div>
JavaScript:
function Ctrl( $scope, $compile ) {
$scope.estimate.services = [
{ name: 'service1', value: '' }
];
$scope.options = [
{ name: 'Option 1', value: 'opt1' },
{ name: 'Option 2', value: 'opt2' },
{ name: 'Option 3', value: 'opt3' }
];
$scope.add = function() {
$scope.estimate.services.push({
name: 'service' + ($scope.estimate.services.length + 1),
value: ''
});
};
}
EDITED:
Ok lets say you have two arrays of configurable options:
options1=[...]
options2=[...]
Now if I understand correctly you want a select box that enables yout to select one set of them right? So first you need to enclose both of them in another array or as mentioned before another object.
So lets use an object (I can provide an array example as well)
var $scope.myOptions ={'LabelForOptions1' : options1, 'LabelForOptions2' : options2}
Next we need a place to store the options that were choosen
$scope.selectedOptions = {};
and lastly the select box itself
<select ng-model="selectedOptions" ng-options="value as key for (key,value) in myOptions"></select>
Note that the options1 and options2 variable could be also a single value and the example would still work
COMPLETE SOLUTION TO PROBLEM:
I assume this is your model:
function MyController($scope) {
$scope.options1 = ['Foo1','Bar1'];
$scope.options2 = ['Foo2','Bar2'];
$scope.options3 = ['Foo3','Bar3'];
$scope.allOptions = [$scope.options1, $scope.options2, $scope.options3];
$scope.selectedOptions = ["none","none","none"];
};
So this would be the solution:
<!DOCTYPE html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<script src="./js/myAngular.js"></script>
</head>
<body ng-controller="MyController">
<div><select ng-repeat="option_set in allOptions"ng-model="selectedOptions[$index]" ng-options="value for value in option_set">
</select></div>
</body>
</html>
Working example:
http://jsfiddle.net/qGRQF/11/

Categories

Resources