NG-Model and Array - How it works? - angularjs

I supposed to have the following code:
.JS
angularcomponents.controller('CountryCntrl', ['$scope', function($scope) {
$scope.countries = {
'India': {
'Maharashtra': ['Pune', 'Mumbai', 'Nagpur', 'Akola'],
'Madhya Pradesh': ['Indore', 'Bhopal', 'Jabalpur'],
'Rajasthan': ['Jaipur', 'Ajmer', 'Jodhpur']
},
'USA': {
'Alabama': ['Montgomery', 'Birmingham'],
'California': ['Sacramento', 'Fremont'],
'Illinois': ['Springfield', 'Chicago']
},
'Australia': {
'New South Wales': ['Sydney'],
'Victoria': ['Melbourne']
}
};
}]);
and the html:
<div ng-controller="CountryCntrl">
<div class="col-sm-4">
<select id="country" ng-model="states" ng-options="country for (country, states) in countries">
<option value=''>
Select
</option>
</select>
<br>
<small><em>Country</em></small>
</div>
<div class="col-sm-4">
<select id="state" ng-disabled="!states" ng-model="cities" ng-options="state for (state,city) in states">
<option value=''>
Select
</option>
</select>
<br>
<small><em>States</em></small>
</div>
<div class="col-sm-4">
<select id="city" ng-disabled="!cities || !states" ng-model="city">
<option value=''>
Select
</option>
<option ng-repeat="city in cities" value='[[city]]'>
[[city]]
</option>
</select>
<br>
<small><em>City</em></small>
</div>
</div>
I don't understand how it is binding the right values for cities, countries and states. In my $scope there is not any label to identify it.
Does it depend on the format of the array?

You don't need to define variables in your javascript (though you may prefer to) - when you declare a ng-model, a variable is created on $scope for you. So your country select is bound to a model states, which has no value initially.
Because you did country for (country, states), the label of that select is the array key, which is the country name. The value is bound to the array value. So when you select a country, $scope.states is populated with the full array corresponding to that country.
So now $scope.states has an array in it, which allows ng-options="state for (state,city) in states" to build a selector, and so on.
You can see it happening by displaying the contents of your model variables. Here is a fiddle to show that: https://jsfiddle.net/wbpm8bc7/
As you can see, initially it shows nothing as those variables are empty. But as you make a selection, they are populated with arrays.

Related

Vue v-for: Dynamicly fill HTML with event handler (#change) from an array into a component template

This is my current setup:
HTML
<question
v-for="question in questions"
v-bind:id="question.id"
v-bind:title="question.title"
v-bind:statement="question.statement"
v-bind:interaction="question.interaction"
#onchange-vg="onChangeVg"/>
</question>
Definition of <question>
var questionComponent = Vue.component('question', {
props: ['id', 'title', 'statement', 'interaction', 'currentQuestion'],
template: `
<div :id="'question-' + id">
<div class="flex my-3">
<div class="py-0"><span>{{ id }}</span></div>
<div>
<p>{{ title }}</p>
<div :class="{'hidden':(id !== this.$parent.currentQuestion), 'block':(id === this.$parent.currentQuestion)}">
<p>{{ statement }}</p>
<span v-html="interaction"></span>
</div>
</div>
</div>
</div>
`
});
Vue
var app = new Vue({
el: '#app',
data() {
return {
vg: null,
currentQuestion: 1,
questions: [
{
id: 1,
title: 'Question 1',
statement: 'Details for question 1',
interaction: `
<select ref="vb-art" #change="$emit('onchange-vg')">
<option disabled value="" selected>Make a choice</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
`
},
{
title: 'Question 2',
statement: 'Details for question 2',
interaction: `
<select ref="vb-art" #change="$emit('onchange-vg')">
<option disabled value="" selected>Make a choice</option>
<option value="black">Black</option>
<option value="white">White</option>
</select>
`
},
],
}
},
methods: {
onChangeVerguetungsart() {
console.log("Value: " + event.target.value);
currentQuestion++;
},
},
});
Everything is rendered fine. My problem is, that the event handler #change in <select ref="vb-art" #change="$emit('onchange-vg')"> does not fire.
Whe I replace <span v-html="interaction"></span> whit the whole <select ref="vb-art" #change="$emit('onchange-vg')">...</select> code from interaction every thing works fine.
The interactions may be different from question to question. It could be an , a or just a simple link. That's why i try to put the code into the array and not to the component definition.
How can I solve this issue, so that <span v-html="interaction"></span> accepts the event handler from a code snippet that is delivered by an array value?
You should avoid v-html. v-html bind just plain HTML, not Vue directives.
For you case v-html is not necessary. Make array of options from interaction property and generate your markup by vue template.
interaction: [
{value: 'red', label: 'Red'},
{value: 'blue', label: 'Blue'}
]
and in your component
<select ref="vb-art" #change="$emit('onchange-vg')">
<option disabled value="" selected>Make a choice</option>
<option v-for="{value, label} in interaction" :key="value" :value="value">{{ label }}</option>
</select>
update:
IF you have dynamic content, then slot is your friend. (Refer docs
https://v2.vuejs.org/v2/guide/components-slots.html for more examples)
Your component:
<div>
<p>{{ title }}</p>
<div>
<p>{{ statement }}</p>
<slot />
</div>
</div>
And usage
<question
v-for="question in questions"
v-bind:id="question.id"
v-bind:title="question.title"
v-bind:statement="question.statement"
v-bind:interaction="question.interaction"
>
<select ref="vb-art" #change="onChangeVg">
<option disabled value="" selected>Make a choice</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
</question>
Bonus is, that you don't need to emit anything.
Btw using this.$parent is also not qood idea. Your component is coupled with parent. Use props instead.

Angular js order by date dd-mm-yyyy

I want to order date. But doesn't work correctly. Date format is dd-mm-yyyy. Just order dd format doesn't check mm and yyyy. How can I solve?
JS
$scope.sortOptions = [
{
name:'Date desc',
sortCategory:'-anndate'
},
{
name:'Date asc',
sortCategory:'anndate'
}
];
HTML
<select ng-model="selectedSortType" class="custom-select">
<option value="" selected="">Select type</option>
<option value='{{opt.sortCategory}}' ng-repeat="opt in sortOptions track by $index">{{opt.name}}</option>
</select>
<div ng-repeat="i in res| orderBy:selectedSortType">
<div>{{i.anndate}}</div>
</div>
var app = angular.module('app', [])
.controller('appController', appController);
appController.$inject = ['$scope', '$window'];
function appController($scope, $window) {
$scope.title = "date sorting example";
$scope.sortOptions = [{
name: 'Date desc',
sortCategory: '2018-07-15 '
},
{
name: 'Date desc',
sortCategory: '2018-07-12 '
},
{
name: 'Date asc',
sortCategory: '2018-07-13'
}
];
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="appController">
<p>Ascending Order</p>
<div ng-repeat="i in sortOptions | orderBy:'sortCategory'">
<p>{{i.sortCategory}}</p>
</div>
<select ng-model="selectedSortType" class="custom-select">
<option value="" selected="">Select type</option>
<option value='{{opt.sortCategory}}' ng-repeat="opt in sortOptions | orderBy:'sortCategory'">{{opt.sortCategory}}</option>
</select>
<p>Descending Order</p>
<div ng-repeat="i in sortOptions | orderBy:'-sortCategory'">
<p>{{i.sortCategory}}</p>
</div>
<select ng-model="selectedSortType" class="custom-select">
<option value="" selected="">Select type</option>
<option value='{{opt.sortCategory}}' ng-repeat="opt in sortOptions | orderBy:'-sortCategory'">{{opt.sortCategory}}</option>
</select>
</div>
Try this..
Since the date in res object is in string format the filter is applying on dd value.
For entire date filter you need to convert the anndate to Date() object
Place the following code in the line below assigning res value in controller
angular.forEach($scope.res,function(item){ item.anndate = new Date(item.anndate); });
Then in html replace with below line
<select ng-model="selectedSortType" class="custom-select">
<option value="" selected="">Select type</option>
<option value='{{opt.sortCategory}}' ng-repeat="opt in sortOptions track by $index">{{opt.name}}</option>
</select>
<div ng-repeat="i in res| orderBy:selectedSortType">
<div>{{i.anndate| date : 'dd/MM/yyyy'}}</div>
</div>

pass two values in ng-repeat and ng-change

Controller:
var response = {
"json": {
"response": {
"servicetype": "100",
"functiontype": "101",
"statuscode": "success",
"data": [
{
"countryid": 1,
"countryname": "India",
"countrycode": "91",
"currencyname": "Indian rupee",
"currencycode": "INR",
"latitude": "21.7679",
"longitude": "78.8718"
}
]
}
}
}
$scope.country = response.json.response.data;
Html:
<select name="Country" class="form-control1 drop countries" required ng-model="model.country" placeholder="Select" value="India" ng-change="getState(model.country);">
<option value="" disabled selected> Country*</option>
<option ng-repeat="item in country track by $index" value="{{item.countryid}}">{{item.countryname}}</option>
</select>
I wanted to pass both the countryname and countryid to fetch list of states , need solution . I could just pass only country id. Need assistance.
<select ng-model="country" required
ng-options="c as c.countryname for c in country track by c.countryid" ng-change="getState(country.countryid, country.countryname);" >
<option value="">Country*</option>
</select>
Use ng-options, you will get all values in country model
Use ng-options instead:
<select ng-model="model.country" required
ng-options="c.countryid as c.countryname for c in country" >
<option value="">Country*</option>
</select>
It will give you output:
<select ng-model="model.country" required=""
ng-options="c.countryid as c.countryname for c in country"
class="ng-pristine ng-invalid ng-invalid-required">
<option value="" class="">Country*</option>
<option value="0">India</option>
</select>
Demo Fillde
About your case: track by $index is what breaks your select
Demo Fillde 2

angularjs dependant dropdown option value

i have successfully set up a dependant dropdown using angularjs, but i have a little problem:
i need to define the value of every option tag because it is set up automatically by angular processor.
here is my html code:
<div ng-controller="CountryCntrl">
<select id="country" ng-model="states" ng-options="country for (country, states) in countries">
<option value=''>Choose</option>
</select>
Departement:
<select id="state" ng-disabled="!states">
<option value="">Choose</option>
<option ng-repeat="state in states" value="{{state.id}}">{{state.dep}}</option>
</select>
</div>
<br/>
<input type="submit" value="go">
and here is my angularjs code:
<script>
var Aplic = angular.module("Aplic", []);
Aplic.controller('CountryCntrl', function($scope){
$scope.countries = {
'Aquitaine': [ {'id':'22', 'dep': "Dordogne"}, { 'id':'31', 'dep' : "Gironde"} ],
'Auvergne': [ {'id' : '3', 'dep' : "Allier"}, {'id' : '15', 'dep' : "Cantal"} ]
};
});
</script>
I repeat, i have successfully set up the value for the option in the second dropdown, but for the first one it takes automatically the name of variable country.
so how should i change my code to give every option tag in the first dropdown a specific value.
to understand well my idea, please inspect the values in every dropdown. here is my working snippet on plunker:
http://embed.plnkr.co/VBdxGDQNOJSHeGVfloo2/preview
any help will be appreciated.
here is what i want to do:
<select id="state" ng-model="cou">
<option value="">Choisir</option>
<option ng-repeat="cou in countries" value="{{cou.id}}">{{cou.name}}</option>
</select>
<select id="state" ng-disabled="!cou">
<option value="">Choisir</option>
<option ng-repeat="state in cou.states" value="{{state.id}}">{{state.dep}}</option>
</select>
but now the second dropdown does not work, if you can fiw that the problemwill be solved
Here is sample implementation for you. This will keep values in option tag.
<div ng-app="Aplic" ng-controller="CountryCntrl">
<select id="country" ng-model="selectedCountry" ng-options="country.id as country.name for country in countries track by country.id">
<option value="">Choose</option>
</select>Departement:
<select id="state"
ng-model="selectedState"
ng-disabled="!selectedCountry"
ng-options="state.id as state.dep for state in ((countries | filter:{'id':selectedCountry})[0].states) track by state.id">
<option value="">Choose</option>
</select>
<div>selectedCountry id: {{selectedCountry}}</div>
<div>selectedState id: {{selectedState}}</div>
</div>
Controller:
$scope.countries =[
{
'id': '1',
'name': "Aquitaine",
'states': [{
'id': '22',
'dep': "Dordogne"
}, {
'id': '31',
'dep': "Gironde"
}]
},
{
'id': '2',
'name': "Auvergne",
'states': [{
'id': '3',
'dep': "Allier"
}, {
'id': '15',
'dep': "Cantal"
}]
}];
Working demo

Angular: Drop Down Item active based on model dependency

I'd like the active state of items in one drop-down list to be contingent on properties from a second model:
angular.module('mainApp')
.controller('MainCtrl', function($scope) {
$scope.departments = [
{id:0, name: 'Dry Goods'},
{id: 1, name:'Frozen Food'},
{id: 2, name:'Electronics'}
];
$scope.categories = [
{id: 0, name: 'cereal', departmentId: 0},
{id: 1, name: 'cookies', departmentId: 0},
{id: 2, name: 'televisions', departmentId: 2}
];
});
and the drop-downs:
<select ng-model="department" ng-options="department.name for department in departments" ng-change="changeDepartment()">
<option value="">Department</option>
</select>
<select ng-model="category" ng-options="category.name for category in categories" ng-change="changeCategory()">
<option value="">Category</option>
</select>
I'd like the Department drop-down to display all three items, but only 'Dry goods' and 'Electronics' should be selectable because there are items in Category that map to Department through the departmentId property. Frozen food should be grayed out.
Plunker : http://plnkr.co/edit/c7rZ05qqRCnB7L0ANgZ4?p=preview
Thanks in advance.
Just use the $filter 'filter' in your second select, fike this:
<select ng-model="category" ng-options="category.name for category in categories|filter:{departmentId:department.id}" ng-change="changeCategory()">
<option value="">Category</option>
</select>
Example
UPDATE
Since ng-options doesn't provide a functionality for indicating which values should be disabled, you only have 2 options:
Create a custom directive that will give you that option, this is not an easy task because that directive should be watching for the collection of the ng-select and the collection or selected value of the other select, in other words this: ng-options with disabled rows won't work for you.
Build your select with an ng-repeat, much easier, like this:
.
<div ng-controller='MainCtrl'>
<div class="btn-group">
<select ng-model="departmentID" ng-change="changeDepartment()">
<option value="">Category</option>
<option ng-repeat="department in departments" value="{{department.id}}" ng-disabled="(categories|filter:{departmentId:department.id}).length==0">{{department.name}}</option>
</select>
</div>
<div class="btn-group">
<select ng-model="categoryID" ng-change="changeCategory()">
<option value="">Category</option>
<option ng-repeat="category in categories" value="{{category.id}}" ng-disabled="departmentID!=category.departmentId">{{category.name}}</option>
</select>
</div>
</div>
Example

Resources