Ui-select ng-model is not binded with selected - ui-select

I am trying to do a simple drop down menu with a search in it.
I cant get the ng-model to be changed on this drop down.
this is what I have done, and as you can see its very simple (mostly copy paste from the get started)
I couldnt find any info about the $select. Is the problem is because I didnt add this to the ng-model? if so, what should I add to the controller
<div class="form-group">
<label class="col-md-1">Investigation type<font color="red"><b>*</b></font></label>
<div class="col-md-3">
<ui-select ng-model="investigationType" ng-change="someFunc()" theme="bootstrap">
<ui-select-match placeholder="Select or search a contries in the list...">
<span>{{$select.selected.name}}</span>
</ui-select-match>
<ui-select-choices repeat="item in contries | filter: $select.search">
<div ng-bind-html="item.name | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
</div>
</div>
{{investigationType}}
as you can see, very simple thing, though after selected I dont see anything on the page. whats wrong?
app.controller('investigationCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.investigationType ="";
$scope.toolShow=false;
$scope.someFunc = function () {
$scope.investigationType == "Alabama")
$scope.toolShow = true;
else
$scope.toolShow = false;
}
$scope.contries = [
{ id: 1, name: "Alabama" },
{ id: 2, name: "Alaska" },
{ id: 3, name: "Arizona" },
{ id: 4, name: "Arkansas" }];
}]);

You need ng-model to hold the selected object, not a string.
Rewrite to ng-model="contries.selected" (while contries is you data array).
Then add {{contries.selected}} to your template, you will see a serialization of your selected object, so just use contries.selected.name to access your item name.
Here is a working plunk
You can also do something like
ng-change="someFunc($select.selected)"
To access your selected object from the callaback, then you can avoid messing with the ng-model.

Related

Vue.js: How to change a class when a user has clicked on a link?

I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.
Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.

Event handling dynamic created buttons in Vue.js v-for loop

I have a v-for loop where a button is created for each iteration. I'm trying to make a toggle handler where clicking the button will toggle the color of the button. But since the buttons are dynamically created, all of their colors are changing ....
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="toggle">
<i v-bind:class="[{ 'red' : favorited }, 'material-icons']">favorite</i>
</button>
</div>
The pets array is being filled with a http call. My script looks like this:
<script>
export default {
name: 'home',
data() {
return {
pets: [],
favorited: false
}
},
methods: {
toggle: function() {
this.favorited = !this.favorited;
}
},
}
The Style tag just changes the color
<style scoped>
.red {
color: red;
}
Essentially, I'm trying to created a favorite button where you can toggle favoriting an object from the array pets. I know why my method would activate all my buttons. Since favorited is not unique to a button and coming from the data. So when favorited = true, the class 'red' is bound to all my buttons. My question is how do I bind the class 'red' to just the button I click on? I'm relatively new to Vue and this is driving me nuts lol! Someone please tell me how I can fix this.
Add a favorited property to your pet objects in the pets array. Then use that property.
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="pet.favorited = !pet.favorited">
<i v-bind:class="[{ 'red' : pet.favorited }, 'material-icons']">favorite</i>
</button>
</div>
Example.
If you didn't want to modify the pet object, then here is an alternate way.

Setting value of [(ngModel)] upon first page load in Angular 2

I am trying to create a simple select component which takes in some data via attributes and renders the required options. I plan to use this select component inside the template of another component, say PageComponent's template( page.template.html ).
I am binding a variable of PageComponent to the select component using [(ngModel)]. Upon selecting an option, the value of the variable get's updated as expected but how do I set it to point to the first option upon page load without triggering a manual selection on the select component?
Here's the code.
In page.template.html
<select ui-select
[options]="options"
[(ngModel)]="alertType"
[defaultValue]="'Select an alert Type'">
</select>
{{alertType}} <!-- To see the value while debugging -->
In page.component.ts, PageComponent class
alertType:any;
options = [
{id:1, value:"item-1"},
{id:2, value:"item-2"},
{id:3, value:"item-3"}
];
In select.component.ts
import {Component, Input} from '#angular/core'
#Component({
selector: '[ui-select]',
template: `
<option *ngIf="defaultValue" value="-1">{{defaultValue}}</option>
<option *ngFor="let option of options" [value]="option.id">{{option.value}}</option>
`
})
export class SelectComponent {
#Input() options: any;
#Input() defaultValue: string;
}
Right now, the {{alertType}} value initially shows nothing and updates only upon selecting a new option from the select box.
I do understand that it's happening because alertType is set to undefined by default in PageComponent but I can't figure out how to set it to the first option value when the page loads.
Update
The original question has been answered but I had one more question regarding this so updated the question.
In the updated code, the select component accepts a defaultValue custom property from the template and renders a conditional <option> for that default value.
Now, how do I say that if defaultValue is set, alertType should have that value or otherwise it should have the value of the first item in the options list.
P.S - If you have comments about the approach used in building the component, please feel free to add them in answers as it will help me learn.
So you want alertType should be set to the selected value, whether it could be from your provided options or from your defaultValue(first option).
this could be done by using AfterViewInit hook,
the modified code will be..
#Component({
selector: 'my-app',
directives:[
SelectComponent
],
template : `<select ui-select
[options]="options"
[(ngModel)]="alertType"
[defaultValue]="'Select an alert Type'" #mySelect>
</select>
{{alertType}}`
})
export class App implements AfterViewInit{
#ViewChild('mySelect', {read: ViewContainerRef}) mySelect;
options = [
{id: 1, value: "item-1", selected: false},
{id: 2, value: "item-2", selected: true},//if all options will be false then defaultValue will be selected
{id: 3, value: "item-3", selected: false}
];
alertType: any;
ngAfterViewInit() {
this.alertType = this.mySelect.element.nativeElement.value;
}
}
I have created a plunker, check this!!
You could try it like so:
options = [
{id:1, value:"item-1"},
{id:2, value:"item-2"},
{id:3, value:"item-3"}
];
alertType: any = options[0].id;
For your new question, you could leverage the selected attribute in your ui-select component:
<option *ngIf="defaultValue" selected="true" value="-1">{{defaultValue}}</option>

Remove the first item on click in Ractive.js

I have a list of 4 items and I need to remove the first item every time the button is clicked. I implemented a simple solution based on splice method but it seems to work only on the first click. Any further click doesn't change a thing.
Here is the html:
<script type="text/ractive" id="template1">
{{#each Posts}}
<div style="background-color: red">{{Text}}</div>
{{/each}}
<button on-click="removeFirst">Remove first</button>
</script>
<main></main>
, javascript:
var ractive1 = new Ractive({
template: '#template1',
el: 'main',
data: {
Posts: [ {Text: "Post 1"}, {Text: "Post 2"}, {Text: "Post 3"}, {Text: "Post 4"} ],
}
});
ractive1.on({
removeFirst: function() {
ractive1.splice('Posts', 0, 1, []);
}
});
and jsfiddle demo.
When you call splice on an array, the first argument is the index from which to start removing elements, the second argument is the number of elements to remove, and all the rest are items to insert into the array at the same location. So what you're actually doing when you call array.splice(0, 1, []) is replacing the first item with an empty array, instead of just removing it.
Ractive's array mutation methods follow the same form, except that the first argument is the 'keypath' of the array. So if you do this...
ractive1.splice('Posts', 0, 1);
...it works correctly.

Hide Parent's sbling ui-view quadrant when in a child state

When I go to a child state, I want to hide a ui-view component of a quadrant ui-view in root state. How can achieve this.
##index.html
<div ui-view="a">
</div>
<div ui-view="b">
</div>
<div ui-view="c">
</div>
##b.html
<div ui-view>
</div>
##config
$stateProvider.state('start', {
'views': {
'a': {
templateUrl: ...
},
'b': {
templateUrl: 'b.html'
},
'c': {
templateUrl: ...
}
},
controller: 'indexController
}).state('start.all', {
templateUrl: 'd.html',
controller: 'allController'
});
So when I reach start.all, I would like that the ui-view tagged c vanishes. How can I accomplish this.
There is an example demonstrating approach discussed below. The native way of ui-router, I'd say, is to manage all the views from current (active) state. We can do it with :
View Names - Relative vs. Absolute Names
... Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item
In our case, the full name of the view 'c' would be c#, i.e. c as view name, # as delimiter and empty string representing the root (a bit weird but in fact logical).
Having that we can change the start.all definition like this:
.state('start.all', {
url : '/all',
'views': {
'': {
template: '<span>this is start ALL</span>',
},
'c#': {
template: '<span></span>',
},
},
})
And we will change the content of the c view in the root. And that should be the most native way with ui-router. It does not effectively remove it, but we can replace it with some empty stuff.
Also, into your example above, I placed controller called bController as contra example to the indexController:
.state('start', {
url : '/start',
'views': {
'a': {
template: ...
},
'b': {
template: ...
// HERE a new controller bController
controller: 'bController',
},
'c': {
template: ...
}
},
// the orginal contoller
controller: 'indexController',
})
and also defined them this way:
.controller('indexController', function($scope, $state, $stateParams) {
console.log('indexConroller was invoked');
})
.controller('bController', function($scope, $state, $stateParams) {
console.log('bConroller was invoked');
})
Why? to show you, that indexController will never be invoked. Contollers belongs to templates/views not to state...
Check all that together here
You could do it in a few ways. One way would be to have an abstract state containing views a and b. That abstract state then has two concrete child states: start, which adds view c, and all, which adds view d.
Another option is to just use the ng-show directive on the c view's root element bound to some scope variable. I would go with the first option.
Notably this does not answer the question because all is no longer a child of start. If there is a real need for all to inherit from start (there appears to be no need at present) you can just make start abstract and create a start.main and start.all.
Though Radim's solution is very clever and much appreciated, I think this is much more readable and intuitive than overriding a parent view with an empty template.
<body>
<div ng-app="myApp">
<a ui-sref="start.main">start</a> | <a ui-sref="start.all">all</a>
<hr />
<div class="rootView" ui-view="a"></div>
<div class="rootView" ui-view="b"></div>
<div class="rootView" ui-view=""></div>
</div>
<script>
var myApp = angular.module('myApp', ['ui.router']);
myApp.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/start/main');
$stateProvider
// Content common to all views
.state('shell', {
abstract: true,
views: {
"a": { template: '<div>View a here.</div>' },
"b": { template: '<div>View b here.</div>' },
"": { template: '<div ui-view></div>' }
}
})
// Content common to all 'start' views (currently nothing)
.state('start', {
parent: 'shell',
url: '/start',
abstract: true,
template: '<div ui-view></div>'
})
.state('start.main', {
url: '/main',
template: '<div>View c is here</div>'
})
.state('start.all', {
url: '/all',
template: '<div>View d is here</div>'
});
});
</script>
</body>

Resources