Can I use a function as a conditional attribute - polymer

In the following code, I want to use a function as a condition attribute:
<polymer-element name="test-element">
<template>
<select id="type_menu" on-change="{{ onSelected }}">
<option value="0" disabled?="{{ isDisabled }}">Item 0</option>
<option value="0" disabled?="{{ isDisabled }}">Item 1</option>
<option value="0" disabled?="{{ isDisabled }}">Item 2</option>
</select>
</template>
<script>
Polymer('test-element', {
onSelected: function(event, details, sender) {
// do something here
},
isDisabled: function() {
return false; // simplified here - it doesn't matter, it always returns true
}
}
</script>
</polymer-element>
So far, I have not been able to make this work. I have tried using disabled?="{{ isDisabled() }}" also. Can it be done at all? If not, what is the best way to do this with some kind of function? Setting a data property on ready might work but it seems a bit unwieldy.

Here is an example of how I solved it:
<polymer-element name="test-element">
<template>
<select id="type_menu" on-change="{{ onSelected }}">
<option value="0" disabled?="{{ someData[0].val | isDisabled }}">Item 0</option>
<option value="1" disabled?="{{ someData[1].val | isDisabled }}">Item 1</option>
<option value="2" disabled?="{{ someData[2].val | isDisabled }}">Item 2</option>
</select>
</template>
<script>
Polymer('test-element', {
ready: function() {
// in this case, I'm just using read to put in some simple values to filter
this.someData = [{
val = 0
},{
val = 1
},{
val = 2
}];
},
onSelected: function(event, details, sender) {
// do something here
},
isDisabled: function(val) {
return val < 2;
}
}
</script>
</polymer-element>
This is a very simple example, but the gist is this:
The conditional attribute binding ( disabled? = {{ }} ) expects a value ( {{ value }} ) or a value and a filter separated by a pipe ( {{ value | filter }} ). If you give it a function as the value, I believe it evaluates the truthiness of the function directly (does not run it) - ie someFunctionName == true as opposed to someFunctionName() == true. In that context, someFunctionName will always be truthy because it's evaluating the existence of the function (not undefined or not null, etc.) as opposed to a value the function might return.
In the fix, I'm using a filter, which IS evaluated as a function return (and is passed the value). If, in the fix shown here, I were only to include the value, the first option would be disabled and the others would be enabled (because 0 is falsey). If I pass it the filter, though, the function can evaluate the value based upon its algorithm.
This really becomes useful when you're using an inner template with a loop to populate some list based upon data and you want to set disabled, selected, (whatever) dynamically.

Related

How to set Vue Core UI select value

Sorry for the beginner question, I am new to Vue.js. I am using CoreUI. Documentation/Tutorials on CoreUI+Vue are scarce.
I am using the <CForm> tag and here I have a <CSelect> tag.
<CForm #submit="test" ref="form">
<CSelect
label="Pick a name"
:options="['test', 'test1', 'test2']"
v-model="testy"
/>
<CButton type="submit" size="sm" color="primary"> Submit</CButton>
</CForm>
JavaScript:
methods: {
test(e) {
console.log("test");
debugger;
e.preventDefault();
}
}
When my breakpoint is hit and I inspect this.testy it will not return the value of the select box but instead this:
I was under the impression that putting v-model on my CSelect will expose my select box under this.* and I could somehow easily get the value (?!).
For context this is rendered in the DOM:
<select id="uid-wvkj98yh6gp" class="form-control">
<option data-key="0" value="test"> test </option>
<option data-key="1" value="test1"> test1 </option>
<option data-key="2" value="test2"> test2 </option>
</select>
My question: inside my test(e) method, how can I gather the current selected value of my select box?
In the <CSelect> API docs, it lists the value prop:
value
The value of select input. Set .sync modifier to track prop changes.
It seems they don't use v-model as expected and you probably also got an error about the value prop being incorrect.
Change your select to:
<CSelect
label="Pick a name"
:options="['test', 'test1', 'test2']"
:value.sync="testy"
/>
This way you use the .sync modifier the way the guide directs.

v-model issue on a nested object property that may not exist

I have this working, but I would like to find a way to handle any level of nesting with v-model or something re-usable rather than creating companion functions every time.
<select :value="task.settings && task.settings.task_type ? task.settings.task_type : ''"
#input="e => setSetting('task_type', e.target.value)">
<option value="">Select Task Type</option>
<option>Habit</option>
<option>Appointment</option>
<option>Recurring</option>
<option>Meeting</option>
</select>
And in methods:
setSetting(key, value) {
this.task.settings[key] = value
},

Custom Directive for Required/ngRequired

I want to validate a dropdown with required. Default required only works if value is null or blank (correct me if I'm wrong). I want required to give error and make form invalid true if value is 'not assigned'.
<select name="first" class="select" title="Select Approver" ng-model="applications.first" ng-options="x.id as x.value for x in list1" ng-change="SetToAll(applications.first,'1')" required></select>
Using this I can show error message but this does make form invalid
<span class="error" ng-show="applications.first == 'not assigned'?true:false">Select Approver</span>
SOLUTION:-
1) If you want to use required then Check Shannon Hochkins solution.
<form name="formName">
<select name="first" class="select" title="Select Approver" ng-model="applications.first" ng-options="x.id as x.value for x in list1" ng-change="SetToAll(applications.first,'1')" required="true">
<option value="">Not Assigned</option>
</select>
<span class="error" ng-show="formName.first.$invalid ?true:false">Select Approver</span>
<pre>{{formName | json}}</pre>
</form>
He Added a option with blank value <option value="">Not Assigned</option>. and set required="true" in select. This works perfectly.
2) Using custom directive.
app.directive('req', [
function() {
var link = function($scope, $element, $attrs, ctrl) {
var validate = function(viewValue) {
var comparisonModel = $attrs.req;
var invalid = $attrs.invalid;
if (viewValue == invalid) {
// It's valid because we have nothing to compare against
ctrl.$setValidity('req', false);
} else {
ctrl.$setValidity('req', true);
}
};
$attrs.$observe('req', function(comparisonModel) {
// Whenever the comparison model changes we'll re-validate
return validate(ctrl.$viewValue);
});
};
return {
require: 'ngModel',
link: link
};
}
]);
And Your html code :
<select invalid="not assigned" req={{applications.first}} name="first" class="select" ng-model="applications.first" ng-options="x.id as x.value for x in list1" title="Select Approver" ></select>
<span class="error" ng-show="Step5.first.$error.req">Select Approver</span>
Here you will have to set invalid="not assigned" or any value like invalid='0' or invalid=' '. In directive it compares with invalid attribute if value matches it will show error.
Add the required value to the select element, then you can use the FORM object to check if the field is valid or not.
You can also use the forms name, to access fields in the form to check validity.
If you want to add a 'default' option, to the dropdown, you can add in an option with an empty value which is technically invalid on a required dropdown menu. You can choose to hide it if it is valid so the user can't pick it again after a correct option has been chosen.
<form name="formName" ng-controller="testCtrl">
<select name="first" ng-options="x.id as x.value for x in list1" ng-model="applications.first" required>
<option value="" ng-hide="formName.first.$valid">Not Assigned</option>
</select>
<pre>{{formName.first.$invalid | json}}</pre>
</form>
Inside your controller, setup some options:
angular.module('sandbox', []).controller('testCtrl', function($scope) {
$scope.list1 = [{
id: 1,
value: 'apples'
}, {
id: 2,
value: 'oranges'
}];
});
Demo: https://jsfiddle.net/suunyz3e/304/

Angular 2 Dropdown Options Default Value

In Angular 1 I could select the default option for a drop down box using the following:
<select
data-ng-model="carSelection"
data-ng-options = "x.make for x in cars" data-ng-selected="$first">
</select>
In Angular 2 I have:
<select class="form-control" [(ngModel)]="selectedWorkout" (ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
How could I select a default option given my option data is:
[{name: 'arm'}, {name: 'back'}, {name:'leg'}] and my value I to default on on is back?
Add a binding to the selected property, like this:
<option *ngFor="#workout of workouts"
[selected]="workout.name == 'back'">{{workout.name}}</option>
If you assign the default value to selectedWorkout and use [ngValue] (which allows to use objects as value - otherwise only string is supported) then it should just do what you want:
<select class="form-control" name="sel"
[(ngModel)]="selectedWorkout"
(ngModelChange)="updateWorkout($event)">
<option *ngFor="let workout of workouts" [ngValue]="workout">
{{workout.name}}
</option>
</select>
Ensure that the value you assign to selectedWorkout is the same instance than the one used in workouts. Another object instance even with the same properties and values won't be recognized. Only object identity is checked.
update
Angular added support for compareWith, that makes it easier to set the default value when [ngValue] is used (for object values)
From the docs https://angular.io/api/forms/SelectControlValueAccessor
<select [compareWith]="compareFn" [(ngModel)]="selectedCountries">
<option *ngFor="let country of countries" [ngValue]="country">
{{country.name}}
</option>
</select>
compareFn(c1: Country, c2: Country): boolean {
return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
This way a different (new) object instance can be set as default value and compareFn is used to figure out if they should be considered equal (for example if the id property is the same.
Add this Code at o position of the select list.
<option [ngValue]="undefined" selected>Select</option>
just set the value of the model to the default you want like this:
selectedWorkout = 'back'
I created a fork of #Douglas' plnkr here to demonstrate the various ways to get the desired behavior in angular2.
You Can approach this way:
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
or this way:
<option *ngFor="let workout of workouts" [attr.value]="workout.name" [attr.selected]="workout.name == 'leg' ? true : null">{{workout.name}}</option>
or you can set default value this way:
<option [value]="null">Please Select</option>
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
or
<option [value]="0">Please Select</option>
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
Use index to show the first value as default
<option *ngFor="let workout of workouts; #i = index" [selected]="i == 0">{{workout.name}}</option>
According to https://angular.io/api/forms/SelectControlValueAccessor you
just need the following:
theView.html:
<select [compareWith]="compareFn" [(ngModel)]="selectedCountries">
<option *ngFor="let country of countries" [ngValue]="country">
{{country.name}}
</option>
</select>
theComponent.ts
import { SelectControlValueAccessor } from '#angular/forms';
compareFn(c1: Country, c2: Country): boolean {
return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
Struggled a bit with this one, but ended up with the following solution... maybe it will help someone.
HTML template:
<select (change)="onValueChanged($event.target)">
<option *ngFor="let option of uifOptions" [value]="option.value" [selected]="option == uifSelected ? true : false">{{option.text}}</option>
</select>
Component:
import { Component, Input, Output, EventEmitter, OnInit } from '#angular/core';
export class UifDropdownComponent implements OnInit {
#Input() uifOptions: {value: string, text: string}[];
#Input() uifSelectedValue: string = '';
#Output() uifSelectedValueChange:EventEmitter<string> = new EventEmitter<string>();
uifSelected: {value: string, text: string} = {'value':'', 'text':''};
constructor() { }
onValueChanged(target: HTMLSelectElement):void {
this.uifSelectedValue = target.value;
this.uifSelectedValueChange.emit(this.uifSelectedValue);
}
ngOnInit() {
this.uifSelected = this.uifOptions.filter(o => o.value ==
this.uifSelectedValue)[0];
}
}
Fully fleshing out other posts, here is what works in Angular2 quickstart,
To set the DOM default: along with *ngFor, use a conditional statement in the <option>'s selected attribute.
To set the Control's default: use its constructor argument. Otherwise before an onchange when the user re-selects an option, which sets the control's value with the selected option's value attribute, the control value will be null.
script:
import {ControlGroup,Control} from '#angular/common';
...
export class MyComponent{
myForm: ControlGroup;
myArray: Array<Object> = [obj1,obj2,obj3];
myDefault: Object = myArray[1]; //or obj2
ngOnInit(){ //override
this.myForm = new ControlGroup({'myDropdown': new Control(this.myDefault)});
}
myOnSubmit(){
console.log(this.myForm.value.myDropdown); //returns the control's value
}
}
markup:
<form [ngFormModel]="myForm" (ngSubmit)="myOnSubmit()">
<select ngControl="myDropdown">
<option *ngFor="let eachObj of myArray" selected="eachObj==={{myDefault}}"
value="{{eachObj}}">{{eachObj.myText}}</option>
</select>
<br>
<button type="submit">Save</button>
</form>
You can Use that [ngModel] instead of [(ngModel)]and it is Ok
<select class="form-control" **[ngModel]="selectedWorkout"** (ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
You can do as above:
<select class="form-control"
[(ngModel)]="selectedWorkout"
(ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts;
let itemIndex = index"
[attr.selected]="itemIndex == 0">
{{workout.name}}
</option>
</select>
In above code as you can see, selected attribute of the repeating option is set on checking index of the repeating loop of list. [attr.< html attribute name >] is used for setting html attribute in angular2.
Another approach will be setting model value in typescript file as :
this.selectedWorkout = this.workouts.length > 0
? this.workouts[0].name
: 'No data found';//'arm'
Add on to #Matthijs 's answer, please make sure your select element has a name attribute and its name is unique in your html template. Angular 2 is using input name to update changes. Thus, if there are duplicated names or there is no name attached to input element, the binding will fail.
I faced the same problem while using angular 11. But finally found a solution.
<option disabled selected value="undefined">Select an Option</option>
complete example with ngFor.
<select name="types" id="types" [(ngModel)]="model.type" #type="ngModel">
<option class="" disabled selected value="undefined">Select an Option</option>
<option *ngFor="let item of course_types; let x = index" [ngValue]="type.id">
{{ item.name }} </option>
</select>
Add binding property selected, but make sure to make it null, for other fields e.g:
<option *ngFor="#workout of workouts" [selected]="workout.name =='back' ? true: null">{{workout.name}}</option>
Now it will work
<select class="form-control" name='someting' [ngModel]="selectedWorkout" (ngModelChange)="updateWorkout($event)">
<option value="{{workout.name}}" *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
If you are using form there should be name field inside select tag.
All you need to do is just add value to the option tag.
selectedWorkout value should be "back" , and its done.
If you don't want the 2-way binding via [(ngModel)], do this:
<select (change)="selectedAccountName = $event.target.value">
<option *ngFor="let acct of accountsList" [ngValue]="acct">{{ acct.name }}</option>
</select>
Just tested on my project on Angular 4 and it works! The accountsList is an array of Account objects in which name is a property of Account.
Interesting observation:
[ngValue]="acct" exerts the same result as [ngValue]="acct.name".
Don't know how Angular 4 accomplish it!
Step: 1 Create Properties declare class
export class Task {
title: string;
priority: Array<any>;
comment: string;
constructor() {
this.title = '';
this.priority = [];
this.comment = '';
}
}
Stem: 2 Your Component Class
import { Task } from './task';
export class TaskComponent implements OnInit {
priorityList: Array<any> = [
{ value: 0, label: '✪' },
{ value: 1, label: '★' },
{ value: 2, label: '★★' },
{ value: 3, label: '★★★' },
{ value: 4, label: '★★★★' },
{ value: 5, label: '★★★★★' }
];
taskModel: Task = new Task();
constructor(private taskService: TaskService) { }
ngOnInit() {
this.taskModel.priority = [3]; // index number
}
}
Step: 3 View File .html
<select class="form-control" name="priority" [(ngModel)]="taskModel.priority" required>
<option *ngFor="let list of priorityList" [value]="list.value">
{{list.label}}
</option>
</select>
Output:
You just need to put the ngModel and the value you want selected:
<select id="typeUser" ngModel="Advanced" name="typeUser">
<option>Basic</option>
<option>Advanced</option>
<option>Pro</option>
</select>
For me, I define some properties:
disabledFirstOption = true;
get isIEOrEdge(): boolean {
return /msie\s|trident\/|edge\//i.test(window.navigator.userAgent)
}
Then in the constructor and ngOnInit
constructor() {
this.disabledFirstOption = false;
}
ngOnInit() {
setTimeout(() => {
this.disabledFirstOption = true;
});
}
And in the template I add this as the first option inside the select element
<option *ngIf="isIEOrEdge" [value]="undefined" [disabled]="disabledFirstOption" selected></option>
If you allow to select the first option you can just remove the usage of the property disabledFirstOption
In my case, here this.selectedtestSubmitResultView is set with default value based on conditions and an variable testSubmitResultView must be one and same as testSubmitResultView. This indeed worked for me
<select class="form-control" name="testSubmitResultView" [(ngModel)]="selectedtestSubmitResultView" (ngModelChange)="updatetestSubmitResultView($event)">
<option *ngFor="let testSubmitResultView of testSubmitResultViewArry" [ngValue]="testSubmitResultView" >
{{testSubmitResultView.testSubmitResultViewName}}
</option>
</select>
For More Information,
testSubmitResultViewArry: Array<any> = [];
selectedtestSubmitResultView: string;
getTestSubmitResultViewList() {
try {
this.examService.getTestSubmitResultViewDetails().subscribe(response => {
if (response != null && response !== undefined && response.length > 0) {
response.forEach(x => {
if (x.isDeleted === false) {
this.testSubmitResultViewArry.push(x);
}
if (x.isDefault === true) {
this.selectedtestSubmitResultView = x;
}
})
}
});
} catch (ex) {
console.log('Method: getTestSubmitResultViewList' + ex.message);
}
}
I faced this Issue before and I fixed it with vary simple workaround way
For your Component.html
<select class="form-control" ngValue="op1" (change)="gotit($event.target.value)">
<option *ngFor="let workout of workouts" value="{{workout.name}}" name="op1" >{{workout.name}}</option>
</select>
Then in your component.ts you can detect the selected option by
gotit(name:string) {
//Use it from hare
console.log(name);
}
works great as seen below:
<select class="form-control" id="selectTipoDocumento" formControlName="tipoDocumento" [compareWith]="equals"
[class.is-valid]="this.docForm.controls['tipoDocumento'].valid &&
(this.docForm.controls['tipoDocumento'].touched || this.docForm.controls['tipoDocumento'].dirty)"
[class.is-invalid]="!this.docForm.controls['tipoDocumento'].valid &&
(this.docForm.controls['tipoDocumento'].touched || this.docForm.controls['tipoDocumento'].dirty)">
<option value="">Selecione um tipo</option>
<option *ngFor="let tipo of tiposDocumento" [ngValue]="tipo">{{tipo?.nome}}</option>
</select>

Local Storage multiple fields with one field overwriting the second

I am creating a Chrome extension that restricts off-limits TV content. I have two roll-down menu forms that store values to Local Storage.
Javascript (external files):
ratings.js
window.onload=function (){
document.getElementById('saveRatings').onsubmit=saveRatings;
}
function saveRatings() {
var selectedRatings = document.forms["ratings_form"]["ratings"].value;
// store selectedRatings to local storage
localStorage.storedRatings = selectedRatings;
}
age.js
window.onload=function (){
document.getElementById('saveAge').onsubmit=saveAge;
}
function saveAge() {
var selectedAge = document.forms["age_form"]["age"].value;
// store selectedAge to local storage
localStorage.storedAge = selectedAge;
}
HTML
<summary>Select Content to Allow</summary><br>
<form name = "ratings_form" id="saveRatings">
<select name="ratings" multiple="multiple">
<option value="G">G only</option>
<option value="G/PG">G/PG only</option>
<option value="G/PG/PG13">G/PG/PG-13 only</option>
<option value="G/PG/PG13/R">G/PG/PG-13/R</option>
</select>
<div></div>
<input type="submit" value="Save"> </form>
<summary>Select Age Group to Deter</summary><br>
<form name = "age_form" id="saveAge">
<select name="age" multiple="multiple">
<option value="e">Everyone</option>
<option value="ct">Children & Teens;</option>
<option value="c">Children</option>
<option value="0">Turn off</option>
</select>
<div></div>
<input type="submit" value="Save">
</form>
The key-value pair for age_form stores correctly. However, ratings_form always gives me undefined. If I switch up the order (age first and ratings next), then the key-value pair for ratings_form would give me the correct value whereas age_value would give me undefined. It seems like the second form values are overwriting the first form values. How can I prevent this overwriting from occurring.
Thanks for your help.
Of course, your problem is that you're overwriting the window.onload function with whichever code runs last! All you need is a simple console.log(); in each function to see that the first one is not being called. You can remedy this with addEventListener() or by using jQuery's $(document).ready().
window.addEventListener('load', function() {
console.log('1');
});
window.addEventListener('load', function() {
console.log('2');
});
Just remember onload is a property of window and acts just like any variable/property would. Consider this:
var foo = 'bar';
foo = 'baz';
console.log(foo); // displays 'baz', of course! You changed the value!
That is just what you did with the onload function. You changed it to something else.