Can anyone tell me why the model option in the first example is selected, and the second one does not for a plain array:
// Plain old array
vm.owner = ['example', 'example2', 'example3', ...];
Where the model vm.model.address.owner = 2;
// EXAMPLE 1 - Works
// Force index to be a number: using id*1 instead of it being a string:
// and the option ('example3') is selected based on the model value of 2
// indicating the index
<select id="owner"
name="owner"
placeholder="Residential Status"
ng-model="vm.model.address.owner"
ng-required="true"
ng-options="id*1 as owner for (id, owner) in vm.owner">
<option value="">Select Value</option>
</select>
Attempting to not use a hack and using track by instead index 2 is not selected even though the value is still set in the model.
// EXAMPLE 2 - Doesn't Work
// Markup doesn't show value to be a string: using track by, but the
// option set in the model doesn't cause the option to be selected it
// remains as the default with a value of ''
<select id="owner"
name="owner"
placeholder="Residential Status"
ng-model="vm.model.address.owner"
ng-required="true"
ng-options="owner for (id, owner) in vm.owner track by id">
<option value="">Select Value</option>
</select>
I find ngOptions to be super confusing so any explanation or solution for example 2 since it is cleaner and not a hack would be great.
Didn't find a solution using track by, but the AngularJS docs for Select had a solution using a parser and formatter so I could get away from using the hack in the question. I tweaked it a bit so if the key was a string it will leave it alone, otherwise it converts it, and this seems to work. Any criticisms or issues that I don't see please comment, otherwise hope this helps someone.
(function () {
'use strict';
/**
* Binds a select field to a non-string value via ngModel parsing and formatting,
* which simply uses these pipelines to convert the string value.
* #constructor
* #ngInject
* ---
* NOTE: In general matches between a model and an option is evaluated by strict
* comparison of the model value against the value of the available options.
* Setting the option value with the option's "value" attribute the value
* will always be a "string", which means that the model value must also
* be a string, otherwise the select directive cannot match them
* reliably.
*/
function selectConvertKey(_) {
return {
require: 'ngModel',
link: function ($scope, $element, $attrs, $ctrl) {
var ngModelCtrl = $ctrl;
// Do nothing if no ng-model
if (!ngModelCtrl) {
return;
}
// ---
// PRIVATE METHODS.
// ---
/**
* Convert the key to a number if the key is a number.
* #param key
* #returns {Number}
* ---
* NOTE: Using Number() instead of parseInt() means that a string
* composed of letters and numbers, and start with a number will
* not be converted.
*/
function selectConvertKeyParser(key) {
var keyAsNumber = Number(key);
// Check if the key is not a number
if(_.isNaN(keyAsNumber)) {
return key;
}
return keyAsNumber;
}
/**
* Convert the key to a string.
* #param key
* #returns {string}
*/
function selectConvertKeyFormatter(key) {
return '' + key;
}
// ---
// MODEL PROPERTIES.
// ---
/**
* Formatters used to control how the model changes are formatted
* in the view, also known as model-to-view conversion.
* ---
* NOTE: Formatters are not invoked when the model is changed
* in the view. They are only triggered if the model changes
* in code. So you could type forever into the input, and
* the formatter would never be invoked.
*/
ngModelCtrl.$formatters.push(selectConvertKeyFormatter);
/**
* Parsers used to control how the view changes read from the
* DOM are sanitized/formatted prior to saving them to the
* model, and updating the view if required.
*/
ngModelCtrl.$parsers.push(selectConvertKeyParser);
}
};
}
selectConvertKey.$inject = [
'_'
];
angular
.module('app')
.directive('selectConvertKey', selectConvertKey);
})();
Yes, the problem appears to be that the select is being bound to a non-string value. If you do the following, it would work:
//controller
vm.model.address.owner = "2"
//html
ng-options="id as owner for (id, owner) in vm.owner"
See Angularjs ng-options using number for model does not select initial value.
Also, if you want to leave the model value as a number (2, not "2") you can try this:
ng-options="vm.owner.indexOf(owner) as owner for (id, owner) in vm.owner"
However, that may not be any less "hackish" than your working first example:
ng-options="id*1 as owner for (id, owner) in vm.owner">
See the first answer at AngularJS ng-option get index.
Related
this is how the data is displayed but i want
Rhugveda desai -> flowers,Sarees,Prasad
In my application i need to use group by clause . But i am getting a syntax error.Also, What should i do if i want quantity column to be multiplied by amount to get the total? My tables are inkind and inkind_items, where inkind.id is foreign key in inkind_items table as inkind_id.
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #11
of SELECT list is not in GROUP BY clause and contains nonaggregated
column
my inkind_items tabel is inkind_items
my inkind table is inkind
My query is:
$inkinds = DB::table('inkind')
->join('inkind_items', 'inkind.id', '=', 'inkind_items.inkind_id')
->select('inkind.*', 'inkind_items.*')
->groupBy('inkind_items.inkind_id')
->get();
Try using group_concat()
$inkinds = DB::table('inkind')
->join('inkind_items', 'inkind.id', '=', 'inkind_items.inkind_id')
->select('inkind.*', DB::raw('group_concat(inkind_items.name) as items'))
->groupBy('inkind_items.inkind_id')
->get();
Here I'm assuming inkind have field name and inkind_items has fields items.
You can use Laravel collection methods for that.
Just call:
$inkinds->groupBy('inkind_id');
after ->get(). Considering that inkind_id is unique column for both tables
Hi. You asked another question earlier today (about displaying an input when a particular checkbox is checked) but deleted it before I submitted my answer, so I thought I would paste the answer here instead:
Just to get you started, here is an explanation of how to use
addEventListener and createElement to achieve your desired result.
If any part of it is still unclear after studying the code and the
accompanying comments, please search for the name of the still-unclear function on
MDN.
(For example, https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName.)
// Sets `box1` to refer to the first element on the page with the class "box".
const box1 = document.getElementsByClassName("box")[0];
// Sets `container` to be the element whose id attribute has the value "container".
// (This is where our new input element, inside a new label element, will be added.)
const container = document.getElementById("container");
// Begins listening for clicks. From now on, whenever the user clicks anywhere
// on the page, our listener will call the `noticeClick` function.
document.addEventListener("click", noticeClick);
function noticeClick(event){
// Because this function's name is the second argument in
// the call to `document.addEventListener`, above, it is
// automatically called every time a 'click' event happens on the
// page (and it automatically receives that event as an argument.)
// The "target" of the event is whatever the user clicked on.
// So we set the variable `targ` to refer to this target, and we check whether:
// 1) the clicked target is our checkbox,
// 2) this click caused the checkbox to gain the "checked" attribute, and
// 3) we have not already given the checkbox the "added" class
const targ = event.target;
if(targ.id == "box1" && targ.checked && !targ.classList.contains("added")){
// If all three conditions are met, we...
// ...set two variables, `label` and `val`
const label = event.target.id;
const val = event.target.value;
// ...call the `createInput` function, passing these variables as its two arguments
createInput(label, val);
// ...give the checkbox the "added" class (so we can avoid accidentally adding it again)
targ.classList.add("added");
}
}
function createInput(newLabel, newValue){
// When the user has checked our checkbox, the `noticeClick` function
// will call this function, which receives two arguments (which we can
// see, by examining the `noticeClick` function, are two strings: the
// `id` attribute of box1 and the `value` attribute of box1.)
// We use `document.creatElement` to create an `input` element and a
// `label` element, and `document.createTextNode` to set some text
// to be used in the label (using the "newLabel" argument.)
const myInput = document.createElement("input");
const myLabel = document.createElement("label");
const myLabelText = document.createTextNode(newLabel + " ");
// We set our new `input` element's value using the "newValue" argument.
myInput.value = newValue;
// We use `appendChild` to put both the text and the input element
// inside the label, and to put the label inside `container`.
myLabel.appendChild(myLabelText);
myLabel.appendChild(myInput);
container.appendChild(myLabel);
}
// This process can be applied to multiple checkboxes on the same page
// by adding a loop inside the `noticeClick` function, where the string
// "box1" should be replaced with a variable that can refer to the id of a
// different checkbox's `id` for each iteration of the loop.
<label>
<input type="checkbox" id="box1" class="box" value="value1" />
Label for box1
</label>
<hr />
<div id="container"></div>
I have a very simple piece of code to run a dynamic sortBy over my array. I am using a select with ng-model to return the correct key by which to sort. However, I can change the select once, and the orderBy works. But once I do it again, I get a very strange error
Controller
//change task sort
$scope.changeOrder = 'task_date';
$scope.changeOrder = (filterTask) => {
if (filterTask == "due") {
$scope.changeOrder = 'task_date';
} else if (filterTask == "imp") {
$scope.changeOrder = 'task_importence';
}
};
Template
<select ng-change=changeOrder(filterTask) ng-model="filterTask">
<option value="due">Due First</option>
<option value="imp">Importance</option>
</select>
<task-item ng-repeat="task in $ctrl.user.task | orderBy : changeOrder"></task-item>
Here is the error - There is nothing called "v2" in my system
Welcome to the untyped world that is JavaScript.
Your error is actually quite apparent: $scope.changeOrder becomes a function and a standard variable. Once you select a value in your select drop-down, it ceases to be a function and reverts to a standard variable. Then, you can no longer call it.
You would be wise to split this up into two variables instead. I'd recommend using $scope.orderState and $scope.changeOrder, where orderState just holds the strings and changeOrder is your function.
I think the problem is that both of your $scope variables have the same name. You try to assign a function and a value to $scope.changeOrder. Try splitting it up into two variables
I have an array and a var
$scope.fcFeedTypes = [{"name":"Plain Text","value":"Plain Text"},{"name":"MQ Message","value":"MQ Message"}];
}
$scope.feedEditor.ft = "MQ Message"; // this is dynamically obtained from some other source
I want a dropdown select with default selection based on the value of $scope.feedEditor.ft
HTML:
<select ng-model="fcFeedTypes"
ng-options="ft.name for ft in fcFeedTypes"
ng-init="ft=feedEditor.ft">
I am new to AngularJS and need some help...
If you just want the string "MQ Message" (or the value of whatever a user selects) as your ng-model value, then it's quite simple:
<select ng-model="someModelName"
ng-options="ft.value as ft.name for ft in fcFeedTypes"
ng-init="someModelName=feedEditor.ft">
If you want the full object as the ng-model value, then you've got to find the right one in JS. It must be referentially/strictly equal to the one in the options.
<select ng-model="someModelName"
ng-options="ft.name for ft in fcFeedTypes">
-
$scope.fcFeedTypes = [{"name":"Plain Text","value":"Plain Text"},{"name":"MQ Message","value":"MQ Message"}];
$scope.feedEditor.ft = "MQ Message"; // this is dynamically obtained from some other source
angular.forEach($scope.fcFeedTypes, function(feedType){
if(feedType.value === $scope.feedEditor.ft){
$scope.someModelName = feedType;
}
});
I am building a simple Appgyver mobile app using Angularjs and Coffeescript - I'm a beginner with both of these.
I wish to determine the total cost for a list of up to 20 items stored on the database. However, there may be less than 20 items.
I have attempted to do the calculation with ng-bind, which works perfectly as long as all strings contain values. However, if there are less than 20 pairs (values go up to q20 and p20) then the calculation returns NaN.
I would like to determine the total of all existing values for the list. I have looked at numerous examples on stackoverflow, Angularjs.org and other sites and have experimented with a myriad of alternative methods, however I think I lack the basic understanding of how to make this work. Any help would be appreciated.
This is the code I have used, shortened to 3 pairs instead of 20:
<span ng-bind="client['q1'].price * client['p1'].price + client['q2'].price
* client['p2'].price + client['q3'].price * client['p3'].price"></span>
This is the existing controller:
angular
.module('client')
.controller("ShowController", ($scope, Client, supersonic) ->
$scope.client = 0;
$scope.showSpinner = true
$scope.dataId = undefined
_refreshViewData = ->
Client.find($scope.dataId).then (client) ->
$scope.$apply ->
$scope.client = client
$scope.showSpinner = false
supersonic.ui.views.current.whenVisible ->
_refreshViewData() if $scope.dataId
supersonic.ui.views.current.params.onValue (values) ->
$scope.dataId = values.id
_refreshViewData()
$scope.remove = (id) ->
$scope.showSpinner = true
$scope.client.delete().then ->
supersonic.ui.layers.pop()
)
I think you are overloading (in the linguistic sense, not the coding sense) ng-bind. Doing all of that code in your HTML is messy and is not what it was created for. You would be better off doing the math in your controller, and then referencing it in ng-bind. You have only 3 pairs here, but you say you have 20, and could be more, so do it that way:
<span ng-bind="totalPrice"></span>
And in your controller:
var setTotalPrice = function() {
var ret = 0, i, maxClient = 6, client = $scope.client; // or however else you keep track of them
for (i=1;i<=maxClient;i++) {
if (client['q'+i] && client['q'+i].price && !isNaN(client['q'+i].price) &&
client['p'+i] && client['p'+i].price && !isNaN(client['p'+i].price)) {
ret += (client['q'+i].price * client['p'+i].price);
}
}
$scope.totalPrice = ret;
};
$scope.setTotalPrice = setTotalPrice;
setTotalPrice();
Just call setTotalPrice in your controller whenever you want, or on an ng-click.
Please don't abuse ng-bind for calculations! Instead calculate the values in your controller and bind the resulting value.
Problem with your code is -if any of the values is not a number your result becomes NaN. In the controller function you check for the presence of value and then operate. You may want to check whether the value is non-null as well as a number string and then operate on it.
It's really easy to have select options translated with angular-translate:
<select name="languageId"
ng-options="p.id as ('LANGUAGE.'+p.id)|translate for p in Const.languages | orderBy:'name'">
But that way the options are sorted by their original key not their translated one.
Is there a way I can have that list ordered by their translated value without preparing that list ahead in the controller?
The easiest way that worked for me (angular 1.2.24 and angular-translate 2.14.0):
<select name="nationality" ng-model="person.nationality" ng-options="c.id as c.name | translate for c in Const.countries | orderBy:'name | translate' ">
The credit for this goes to the writer of this comment: https://github.com/angular-translate/angular-translate/issues/1064#issuecomment-267357441
You could supply a predicate function to orderBy and have that function return the translated value. It means you also have to pass in the $filter service to your controller because you'd need to invoke translate inside your JS code.
So, to offer some code as guidance:
// CONTROLLER
// * Don't forget to inject the $filter service
$scope.translated = function(p) {
return $filter('translate')('LANGUAGE.' + p.id);
}
And then you'd modify your orderBy expression:
... | orderBy:translated
I realise the suggestion seems somewhat convoluted because one translation attempt occurs within ng-options and then another in orderBy, but it should sort the select options as you'd expect.
I had the same problem and I recommend using an own filter, because you don't want to mess up your controller! This is my current solution:
/**
* orderByTranslated Filter
* Sort ng-options or ng-repeat by translated values
* #example
* ng-options="country as ('countries.'+country | translate) for country in countries | orderByTranslated:'countries.'"
* #param {Array} array
* #param {String} i18nKeyPrefix
* #param {String} objKey
* #return {Array}
*/
app.filter('orderByTranslated', ['$translate', '$filter', function($translate, $filter) {
return function(array, i18nKeyPrefix, objKey) {
var result = [];
var translated = [];
angular.forEach(array, function(value) {
var i18nKeySuffix = objKey ? value[objKey] : value;
translated.push({
key: value,
label: $translate.instant(i18nKeyPrefix + i18nKeySuffix)
});
});
angular.forEach($filter('orderBy')(translated, 'label'), function(sortedObject) {
result.push(sortedObject.key);
});
return result;
};
}]);
Notice you need to pass your i18n prefix 'LANGUAGE.' and I saw your using an object instead of a simple string array so you can use it like this:
<select name="languageId"
ng-options="p.id as ('LANGUAGE.'+p.id)|translate for p in Const.languages | orderByTranslated:'LANGUAGE.':'id'">
I know it's an old question, but stumbled upon this today and here's how in this case you could solve it (only works for Array though):
<select name="languageId"
ng-options="p.id as name=('LANGUAGE.'+p.id)|translate for p in Const.languages | orderBy:'name'">
The trick is to assign the translation and use this assignment to sort on.