Show option only when there are no results - javascript

I am trying to hardcode in an option to an ng-options select list when the scope it is pulling from has no answers. I was able to achieve this in another scenario by doing something like that:
ng-show="!$scope.length"
However, it does not seem to be working inside the select list itself. Perhaps it's something with how Angular does ng-options? Here is my attempt:
<select ng-model="myModel" ng-options="option.title for option in optionsList">
<option ng-show="!optionsList.length">There are no prompts for this lesson.</option>
</select>
I have hardcoded an option inside of the select with the same logic, but it does not seem to want to work. Thanks in advance!

One way to do this would be to create a filter that inserts the default option if empty...
.filter('defaultOptionIfEmpty', function () {
return function (items, defaultItem) {
return items.length ? items : [defaultItem];
}
});
Use it like this...
<select ng-model="foo" ng-options="item.value for item in items | defaultOptionIfEmpty : {id: 0, value: 'There are no prompts for this lesson.'}">
</select>
Fiddle

Assuming that your data for options is "dataBlocksHere.currentData.selections", you need to pre-process this block to check if there is anything in it, and insert the appropriate content into it.
So before you assign the item to scope, process the results in your javascript.
if(!dataBlocksHere.currentData.selections){
// insert item into selections, something like
dataBlocksHere.currentData.selections['data'] = {id: 0, data: "There are no prompts for this lesson."};
}

Related

Angular dropdown select - how to set default value from typescript

How to set default value in Select?
I am trying to create time- dropdown select with multiple options.
At present. the selectedTimeOption correctly identifies if we choose an option from dropdown, but can't detect a value that I am trying to display initially from the Typescript.
I want to first display a predefined value in select box even before selection (it basically comes from another page)
How do I implement it?
Is there a problem with TS component or the html? If someone can explain with small working example on stackblitz.com , that would be helpful.
HTML:
<select
[value]="selectedTimeOption"
(valueChange)="setTime($event)"
>
<option
"let option of timeOfOptions"
[value]="option"
>
{{ option.label }}
</option>
</select>
TS:
timeOptions:TimeItems[] =[
{ label: '0:00', value: '0' },
{ label: '1:00', value: '1' },
{ label: '2:00', value: '2' },
];
You can have a look at he stackblitz example here,
Where, you can initialize the value to be displayed first(as you said from another component) in the example it is shown as,
firstSelectValue = 'one';
and then in your template, in your element, you can have a 'getter', in the given example, it is,
*ngFor="let opt of firstSelectOptions"
Your <option tag should be like this:
<option [value]="option.value">
And your selectedTimeOption can be set to:
selectedTimeOption = '1'
There are syntax issue and coding conventions you need to follow. please rename those option variable while iterating over array, something like below.
ex: *ngFor = "let time of timeSource"
Here is the stackblitz example

AngularJS ng-repeat render once and reuse

A view of my AngularJS app makes heavy use of ng-repeat directive. It is done like this:
<div ng-repeat="branches in company">
<p>{{branches.name}}</p>
<p>{{branches.location}}</p>
<div>
<select ng-model="branches.officeInformationType">
<option ng-repeat="offices in branches">{{offices.type}}</option>
</select>
<select ng-model="branches.officeInformationMeters">
<option ng-repeat="offices in branches">{{offices.meters}}</option>
</select>
<select ng-model="branches.officeInformationColor">
<option ng-repeat="offices in branches">{{offices.color}}</option>
</select>
</div>
</div>
The fact is, the second ng-repeat and and the others after it (offices in branches) are actually the same everytime, so it wouldn't need to be recalculated for every branch. It would need to be binded to the row it belonges to, for saving it later, so the branches.officeInformation model should still be watched by angular, but I would like to make the whole program more performant.
I am using angular-ui-router and when I change the view between my "Choose your office" view and any other, the lag is tremendous, almost at a minute of wait time when you leave the "Choose your office" page. It renders fast enough, 2 seconds for the whole rendering, but when I leave the page it takes a ton of time to change to the other view.
Any ideas, taking into consideration that the ng-model binding "branches.officeInformation.." is of importance?
EDIT: I have tried remove the nested ng-repeats and for each ng-repeat that I removed, the transition between states got faster and faster. When I removed all the nested ng-repeats the transition became instantaneous, hence why I believe it has to do with the ng-repeats.
The ng-repeats are tracked by $index and where possible I used :: for one time binding.
Thanks.
We can lazy load a dropdown's options right before the user interacts with it.
First, we initialize each dropdown with only the selected option, so you can see it when the dropdown is closed.
Then we attach an ng-focus directive to each dropdown. When our callback fires we can:
fully populate the options for that dropdown
remove all but the selected option from the previously active dropdown
I wasn't entirely sure of the structure of your data (it looks like some arrays have additional properties on them). So I chose to create "view model" objects that represent the UI. You can adapt this to your own structure.
Controller:
// Set up some test office options (null for no selection)
var allOffices = [null];
for (var i = 0; i < 50; i++) {
allOffices.push(i);
}
// activeDropdown holds the dropdown that is currently populated with the full list
// of options. All other dropdowns are only populated with the selected option so
// that it shows when the dropdown is closed.
var activeDropdown;
$scope.company = [
// Branch 1
[
// These objects represent each dropdown
{
// Just the selected option until the user interacts with it
options: ["0"],
selected: "0"
}, {
// Just the selected option until the user interacts with it
options: ["1"],
selected: "1"
}, {
// Just the selected option until the user interacts with it
options: [null],
selected: null
}
],
// Branch 2
[
// These objects represent each dropdown
{
// Just the selected option until the user interacts with it
options: ["2"],
selected: "2"
}, {
// Just the selected option until the user interacts with it
options: ["3"],
selected: "3"
}, {
// Just the selected option until the user interacts with it
options: [null],
selected: null
}
]
];
// When the user interacts with a dropdown:
// - fully populate the array of options for that dropdown
// - remove all but the selected option from the previously active dropdown's
// options so that it still shows when the dropdown is closed
$scope.loadOffices = function (dropdown) {
if (activeDropdown === dropdown) {
return;
}
dropdown.options = allOffices;
if (activeDropdown) {
activeDropdown.options = [activeDropdown.selected];
}
activeDropdown = dropdown;
};
Template:
<div ng-repeat="branch in company">
<div ng-repeat="dropdown in branch">
Selected: {{ dropdown.selected }}
<select ng-focus="loadOffices(dropdown)" ng-model="dropdown.selected">
<option ng-repeat="o in dropdown.options">{{ o }}</option>
</select>
</div>
</div>
Note that ng-focus was the only directive I needed to apply to each dropdown when I tested this. But you may need to add ng-keydown, ng-mouseover, ng-click, or others to get it to work in all scenarios including mobile.
I also noticed a potential styling issue. When you focus on a dropdown, we load all of the options for that dropdown. This may cause the width of the dropdown to change, so if you can set the same width for all of them you should be good.
If the number of options in each dropdown is huge, we may be able to optimize even further by writing some custom directives that interact and allow the actual DOM element options to be shared. But I suspect we won't have to go that far for this example.
Have you tried 'track by $index' ? it will reduce angular watches overhead.
something like that:
div ng-repeat="branches in company track by $index">
<p>{{branches.name}}</p>
<p>{{branches.location}}</p>
<div>
<select ng-model="branches.officeInformationType">
<option ng-repeat="offices in branches track by $index">{{offices.type}}</option>
</select>
<select ng-model="branches.officeInformationMeters">
<option ng-repeat="offices in branches track by $index">{{offices.meters}}</option>
</select>
<select ng-model="branches.officeInformationColor">
<option ng-repeat="offices in branches track by $index">{{offices.color}}</option>
</select>
</div>
</div>
First and foremost, thanks to those that helped me find the answer.
The problem was that I nested too many ng-repeats with too many event handlers attached to each repeated element. ng-models, ng-changes and ng-clicks were really heavy, but the number of elements was also out of control.
I solved this by using a single select without any nested ng-repeats, this select (and the options) are in a modal view, so a different controller. From that controller I return the select results, having only one select for all the elements in the page. When the data is returned from the modal, I use it from the main controller of the view.
Thanks again.

AngularJS select options maintaining content after selection and resetting to blank

Here is the current code (I didn't write it just trying to work with it)-
<select type="text" id="{{for}}" name="{{for}}" ng-model="model.value" class="form-control input-sm" placeholder="{{placeholder}}" ng-options="c.name as c.name for c in countries track by c.code">
<option value="">— Select —</option>
</select>
and the countries array seems to be set up like so
$http.get('services/dictionary/countries').then(function(response) {
_.chain(response.data).map(function(country) {
return {
code: country.id,
digraph: country.digraph,
trigraph: country.trigraph,
name: country.name
};
}).sortBy('name').each(function(country) {
dictionary.countries.push(country);
});
deferred.resolve(dictionary.countries);
}, function(error) {
$log.error('dictionary:', error);
});
We are pulling the list of countries from some dictionary service, and trying to display them as select options. (we aren't really using digraph and trigraph here, but it's needed for other areas that use the same dictionary call).
Problem 1 is, we can set the choice and it saves, but the select list will not show the object I saved, especially after I store and refresh it.I assume this is because we aren't properly setting the value="" with the ng-options we've set up, but I can't get it to work properly after trying many iterations (don't really understand the documentation).
Problem 2 is, on this and all other select dropdowns we have, I can't figure out a way to revert to a null choice. I have a value="" default option but it doesn't blank out the ng-model when selected. we need this to allow for user screw ups, 'oops i didn't even mean to set that field.' type things.
Much appreciated for the help gang.
Problem 1: Are you setting the ngModel value to the saved value on refresh? Only then will it be selected on the refresh.
Problem 2: Set the model to an empty object in your controller:
model = {};

Remove blank option ng-repeat and ng-options after filter

There are several questions very similar to this one yet I have been unable to come up with a solution.
I have a select list using angularJS. I need to use the title attribute so I have an ng-repeat to create the options, there is always a blank option even if I use ng-selected to always select the first option.
Even if I make a selection and the blank option goes away, if I then filter out that selected value the blank will reappear.
I have included a select list using ng-option (which does not include my needed tittle attribute) and a default value to show that the blank will appear after filter.
The behavior I desire would be to never have a blank option (always selecting first option would be fine) and to possibly have a directive per option for special handling of click events.
Thanks in Advance!
JS Fiddle: http://jsfiddle.net/32DFM/3/
<select size="3" ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm"
ng-selected="$first"
value="{{p}}"
title="{{p.name}}">
{{p.name}}
</option>
</select>
I forked your fiddle (if I may be so blunt): http://jsfiddle.net/XsFe8/2/
This fixes it somewhat. Although I haven't gotten it to work properly together with the filter.
Anyway, what I do here, is to use the person.id as the value on each option.
<select ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm" ng-selected="$first" value="{{p.id}}" title="{{p.name}}">
{{p.name}}
</option>
</select>
And set the initial calue on the person.current model:
$scope.person.current = $scope.people[1].id;
But it's still not 100% though. I'm a bit stumped to why the blank spaces appear when you filter the select....
An alternative that might or might not work, would be to use something like ng-repeat="p in filterPeople() and filter your array in a filterPeople function. But I'm not sure if this will change anything.
UPDATE: I tested out my suggestion above, here: http://jsfiddle.net/XsFe8/2/
If you set the selected object to be the first object in the filtered array, it works. I do this each time a new filtered array is created:
$scope.filterPeople = function () {
var array = filterFilter($scope.people, $scope.person.SearchTerm);
$scope.person.current = array[0].id;
return array;
};
It looks like things get hairy when another object than what is visible in the select is actually selected. This is kind of understandable :)
Your actual problem is the value in ngModel is referencing a value which doesn't exist in the select anymore.
A solution is to whenever you alter the select options, you also check the person.current to ensure that it points to a valid entry.
This also implies that you might want to move your filter into the controller, and set the options in the scope (you can use the $filter service in your code to get same behaviour there, https://docs.angularjs.org/api/ng/filter/filter). This way you can have a function in your controller checking if person.current is valid, and if not, set it to desired options (e.g. the first one).
the hairyness cited above is due to an empty array when all items are filtered out and is fixed by:
if(array.length>0)
$scope.person.current = array[0].id;
http://jsfiddle.net/b0z6vpr8/
Hope this helps

Selecting options from a drop down

I'm building a recipe-finder for a new food blog. The design I have basically involves the user selecting ingredients, one at a time, from a drop down <select>, the option disappearing from the list (so they can't select it again) and appearing on another HTML list with a link to remove it from the list. Once they're done, they click a button and that takes them through to a results page.
Here's the select markup as generated by the PHP:
<select>
<option value="">Please select</option>
<option value="beef-mince">Beef mince</option>
<option value="carrots">Carrots</option>
...
</select>
It's not drastically complex but it does raise a few questions on how I'm going to do some of these things. I'm using jquery.
I need to store the selected items in memory so I know what to send to the search page when they've done selecting items. What's the best way of doing that in your opinion as each item has two values (its "real" value and its database-value)?
How do I make "Please select" the selected option after they've selected something (preferable without triggering the onchange event)?
Once I've stored it in memory and added it to the displayed list of things they're searching for, how do I delete that item from the available items? Can I just "hide" or disable it (safely)?
If in #3 I have to delete it from the DOM, when I add it again, can I sort the list (based on either value) and keep the please-select option at the top?
1.) You can append hidden form elements to the page whose value is the value of the selected option.
2.)
jQuery("#select-list")[0].options[0].selected = true // assuming it's the first item
3.) I would remove the element from the DOM using jQuery("#select-list option:selected").remove()
4.) You can use before(). jQuery(your_default_option).before("#select-list option:first");
You can store the 'two values' in a hidden form field as an object in JSON notation. This will make it easy to modify in jQuery as the user interacts with the page.
You will need to use a combination of the onchange, keyup and keydown event to capture possible changes to the form so that you can re-select the 'Please Select' option.
You will need to remove the option from the dom and re-add it later. You can easily do this through jquery through something like this:
$("select option:selected").remove();
You can write a sorting function for the options starting with index 1, and keep the 'Please Select' as the first option.
1)
Basic idea, you need to check to make sure the first is not picked
var selections = [];
var mySel = document.getElementById("mySelectId");
var ind = mySel.selectedIndex;
selections.push( mySel.options[ind].value ); //add to a list for you to remember
mySel.options[ind] = null; //remove
2)
mySel.selectedIndex = 0;
3)
See #1
4) Yes you can add it anywhere you want by using insertBefore
Example here: http://www.pascarello.com/lessons/forms/moveSelectOptions.html
Will leave this answer here but I think I failed to read your whole post, so it might not help much.
You need to give your select a id like this:
<select id="MySelect">
<option value="">Please select</option>
<option value="beef-mince">Beef mince</option>
<option value="carrots">Carrots</option>
...
</select>
And to get it is just something like this:
<?php
$value = $_REQUEST["MySelect"];
echo $value;
?>
Code is not tested and $_REQUEST can be replaced by $_GET or $_POST regarding what you have specified as action on your form. $_REQUEST will eat it all though.

Categories