AngularJS: ngOptions not working (ngModel included, using "controller as" syntax) - javascript

I'll keep this short.
http://codepen.io/anon/pen/waXWpw?editors=101
Here's something I made that hopefully illustrates my problem (I also hope I have Plunker working correctly...). The first dropdown is my attempt, and the other is one I took from another working Plunker that no longer works here...
I simply can't figure out the reason why this isn't working. Essentially I want the user to be able to select a number of rounds from a dropdown, but no matter what I try I can't get ngOptions to function. I simply get "undefined" inside the dropdown, and no other options appear.
EDIT: I fixed the Plunker, and suddenly ngOptions started working, so now my problem is that I have no idea how to replicate my problem. I'll have to include my actual code here to see if anyone can spot the error.
To be clear, everything else on the page is functioning well, including ngMessages, ngAnimate, Angular UI, and other form elements, so I'll include only the relevant HTML.
HTML:
<div ng-controller="CreateController as create">
<form name="creationForm" novalidate>
<div>
<select name="numberOfRounds"
ng-model="create.numberOfRounds"
ng-options="item.rounds as item.name for item in create.possibleNumbersOfRounds"
required>
</select>
<label>Number of Rounds</label>
</div>
</form>
</div>
JS (Controller):
angular
.module('TabIt')
.controller('CreateController', ['$state', '$scope', 'TournamentFactory', function ($state, $scope, TournamentFactory) {
this.possibleNumbersOfRounds = [
{ "rounds": 0, "name": "Please make a selection"},
{ "rounds": 2, "name": "2 rounds" },
{ "rounds": 3, "name": "3 rounds" },
{ "rounds": 4, "name": "4 rounds" },
{ "rounds": 5, "name": "5 rounds" },
{ "rounds": 6, "name": "6 rounds" },
{ "rounds": 7, "name": "7 rounds" },
{ "rounds": 8, "name": "8 rounds" },
{ "rounds": 9, "name": "9 rounds" }
];
this.numberOfRounds = this.possibleNumbersOfRounds[0].code;
}]);

Related

Which is the best way to filter data from an array with Vue JS?

I'm looking for best practices.
I get this product list from my API:
[
{
"id": 6,
"name": "Pechuga de pollo",
"category": "Chicken",
"existencia": 100
},
{
"id": 7,
"name": "Pierna de pavo",
"category": "Chicken",
"existencia": 100
},
{
"id": 8,
"name": "Lonja de pescado",
"category": "Fish",
"existencia": 200
},
{
"id": 9,
"name": "Coca Cola",
"category": "Soda",
"existencia": 200
},
{
"id": 10,
"name": "Jugo de naranja",
"category": "Juice",
"existencia": 200
}
]
So I need to filter this array of products by the value "Chicken".
I've tried with the filter() method and It works well.
this.chickenProducts =productList.filter(product=>product.category=="Chicken")
Then I use the v-for directive with the chickenProducts array to fill the select input that only contains chicken products.
Do you know another way to filter data from an array to fill a select input considering a criteria in Vue JS?
I'm practicing with Vue yet.
computed filter array?
By the way, I remember Vue1 can used filterBy in v-for, but Vue2 removed it.
Your way of implementation is a bit strict. What if you would want to change your filtering condition? or have multiple condition in your code?
If I were you, I would create a method which returns an array based on condition passed into it. the I iterate over that result. This is more straightforward and modular in regards of maintenance and extensibility.

Set list items in ng-model in a dropdownmenu angularjs

currently I stuck with an angularjs problem. The following scenario:
I have a Game entity and a Team entity. One Game can hold multiple Team objects. (ManyToMany Relationship)
In my frontend application I want to add a new Game with optional Teams. For the Teams I decided to use two dropdown-menus (each menu for one team).
Now I am not able to give the ng-model the correct values.
I tried something like this but it might be wrong:
<select class="form-control" ng-options="team.teamname for team in teams
track by team.id" ng-model="game.teams[0]">
<option value="" selected>-- No Team --</option>
</select>
<select class="form-control" ng-options="team.teamname for team in teams
track by team.id" ng-model="game.teams[1]">
<option value="" selected>-- No Team --</option>
</select>
As soon as I click the save button i get an error message "400: Unable to process JSON":
Possibly unhandled rejection: {"data":{"code":400,"message":"Unable to process JSON"},"status":400,"config":{"method":"POST","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","data":{"teams":{"0":{"id":1,"games":[{"id":1,"date":20180810}],"teamname":"BVB"},"1":{"id":2,"games":[{"id":1,"date":20180810}],"teamname":"FCB"}},"date":"20180810"},"url":"/api/games","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json;charset=utf-8"}},"statusText":"Bad Request"}
When creating the two Teams for a Game with Postman it works:
{
"date": 20180810,
"teams": [{"id": 1}, {"id": 2}]
}
Output:
{
"id": 1,
"date": 20180810,
"teams": [
{
"id": 1,
"games": [
{
"id": 1,
"date": 20180810
}
],
"teamname": "BVB"
},
{
"id": 2,
"games": [
{
"id": 1,
"date": 20180810
}
],
"teamname": "FCB"
}
]
}
Any suggestions how to set the ng-model (Game) with the values of the first and second dropdown-menus? thanks
I have added a screenshot of the form that i want to have:
If i understand well, when you do the request with postman, you send this:
{
"date": 20180810,
"teams": [{"id": 1}, {"id": 2}]
}
But your angular form is actually sending this:
{
"date": "20180810"
"teams": {
"0": {
"id": 1,
"games": [
{
"id": 1,
"date": 20180810
}
],
"teamname": "BVB"
},
"1": {
"id": 2,
"games": [
{
"id": 1,
"date": 20180810
}
],
"teamname": "FCB"
}
}
}
While the date looks fine, your teams is an object but your backend is expecting an array. You also have additional informations in your teams (like games and teamname, but i can't know if this is a problem for your backend.
Most of the time this kind of problems with Angularjs are not in the html template but in the controller. In your controller you should have something like this:
scope.game = {
date: 'something',
teams: []
};
I think your problem is that you don't initialize scope.game.teams as an array properly. So ng-model just initliaze it by itself with an object.
From what i gather you want to add multiple teams for each game, follow the checklist model, its probably what you want, https://vitalets.github.io/checklist-model/ from there you can pass the server a list of games for each team

Angular ngRepeat multiple select fields without displaying already selected data through ng-options

So the overview of the problem; I am retrieving data from an api and creating a CRUD page for it. The data has a set of labels that the user can select.
Below is a code sample representing my problem. The labels selected by the user are represented by the user.labels relationship and the total available labels that can be selected are represented by user.parent.grandparent.labels.
I'm able to sync the selection. What I can't seem to figure out is how to get rid of options that have already been selected from the list of options on any other subsequent select field.
angular.module('app', [])
.controller('select', ['$scope', '$filter', '$location',
function($scope, $filter, $location) {
$scope.user = {
"parent": {
"grandparent": {
"labels": [{
"id": 28,
"name": "Label 1",
}, {
"id": 17,
"name": "Label 2",
}, {
"id": 39,
"name": "Label 3",
}, {
"id": 77,
"name": "Label 4"
}, {
"id": 100,
"name": "Label 5"
}]
}
},
"labels": [{
"id": 28,
"name": "Label 1",
"meta": {
"score": 3
}
}, {
"id": 17,
"name": "Label 2",
"meta": {
"score": 5
}
}]
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="select">
<div ng-repeat="label in user.labels track by $index">
<div class="form-field">
<span>Label</span>
<select ng-model="user.labels[$index]" ng-options="department.name for department
in user.parent.grandparent.labels track by department.id">
</select>
</div>
<div>
<span>Score</span>
<select ng-model="label.meta.score">
<option value="1">1 (lowest)</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5 (highest)</option>
</select>
</div>
</div>
<button ng-click="user.labels.push({})">Add Label</button>
</div>
You can use a filter function inside the ng-repeat to achieve this, here is a sample Codepen showing you how:
http://codepen.io/anon/pen/ZYveOo
You need to pass the filter in the repeat definition:
<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | filter:removeSelected track by department.id ">
Which refers to this function on scope:
$scope.removeSelected = function(val){
return !_.find($scope.user.labels, function(label) {
return label.id === val.id;
});
};
Even then though I think you are missing one use case which is that you want to be able to have the currently selected label included in the options, by removing all selected options you are removing that ability.
Updated:
Ok then, so after giving this some thought I have come up with the following filter which could be optimised but does seem to work as expected:
.filter('duplicatesFilter', function() {
return function(items, index, selected) {
var res = [selected[index]];
_.forEach(items, function(item){
if(!_.find(selected, function(label) {
return label.id === item.id;
})){
res.push(item);
}
});
return res;
};
})
Use it like so:
<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | duplicatesFilter:$index:user.labels track by department.id "></select>
This is something I have hit a few times and each time I've worked around it. I'll take a look later if I can find a custom filter that better solves the problem and if I can't I'll tidy up this code and release one; however this should be good to go for your use-case.
Working code-pen:
http://codepen.io/anon/pen/ZYveOo

Populate AngularJS {{expression}} within ng-repeat using a second array

I'm completely rebuilding my website (originally hacked together with Wordpress) using Laravel and AngularJS. It's been a massive learning experience and I think I'm nearly there but for one problem.
On my site 'schemes' (or courses) are made up of 'units' which are made up of 'lessons'. Retrieving this data is fine, using Eloquent I retrieve valid JSON like this made up example...
[
{
"id": "1", //Scheme Id
"title": "Sports",
"description": "This is a Sports course!",
"units": [
{
"id": "1",
"title": "Tennis",
"lessons": [
{
"id": "6",
"title": "Serving"
},
{
"id": "7",
"title": "Hitting the ball with top-spin"
}
]
},
{
"id": "2",
"title": "Athletics",
"lessons": [
{
"id": "1",
"title": "Long Jump"
},
{
"id": "2",
"title": "Hurdling Technique"
}
]
},
{
"id": "4",
"title": "Golf",
"lessons": [
{
"id": "4",
"title": "Pitching"
},
{
"id": "5",
"title": "Putting"
}
]
}
]
}
....
]
Separately I have a simple array of completed lesson ids for a particular user like this...
[2, 6, 8, 9] ///User has completed lessons with ids of 2,6,8 and 9
In my view I'm using nested ng-repeat loops like so...
...
<div ng-controller="SchemesController">
<div ng-repeat="scheme in schemes">
<h1>{{scheme.title}}</h1>
<div ng-repeat="unit in scheme.units">
<h3>{{unit.title}}</h3>
<div ng-repeat="lesson in unit.lessons">
<div>{{lesson.title}}: {{status}}</div>
</div>
</div>
</div>
</div><!--[end of ng-controller="SchemesController"]-->
....
SchemesController (v simple!) looks like this...
var app = angular.module('schemesApp', []);
app.controller('SchemesController', function($scope){
$scope.schemes=jsonData;
});
The problem is I have no idea how to populate the {{status}} field which I want to state simply 'Complete' or 'Incomplete. I investigated whether I could somehow add this info to my original array like this...
"lessons": [
{
"id": "6",
"title": "Serving",
"status": "Complete" //populated somehow
},
{
"id": "7",
"title": "Hitting the ball with top-spin",
}
]
but I got nowhere slowly. Is there a way to do this (I've played around with underscore.js and felt this could help?).
Or do I populate {{status}} from creating and calling a javascript function?!?
ANY help that anyone could offer would be incredible. I'm a school teacher and for some sadistic reason I find a bit of programming/web design a fun use of my spare time so I apologise if this is a stupid question. THANKS in advance!!!
btw if anyone has a better 'title' for this question then please let me know.
I'm assuming you don't need to persist the status back to the database...
This is where you're having the problem:
<div>{{lesson.title}}: {{status}}</div>
You really don't need to store the status in your data model, because it's just used for presentation purposes.
Let's say your array of completed lessons is defined like this:
$scope.completedLessons = [1, 2, 3, 4, 5] // Or however you'd assign it
You need to create a function in your scope like this:
$scope.isLessonCompleted = function(lessonId) {
return $scope.completedLessons.indexOf(lessonId) > -1;
};
Then you need to change the html from above to this:
<div>{{lesson.title}}: {{isLessonCompleted(lesson.id) && 'Complete' || 'Incomplete'}}</div>
If lessons are also a model and each lesson should have a status, which isn't a column/field in your table but is something you'll add logic to determine, you could add a custom model accessor by adding the following to your models/Lesson.php:
// Append custom accessor attributes
protected $appends = ['status'];
public function getStatusAttribute() {
// Add logic here
return 'Complete';
}
This way, when you use Eloquent to retrieve your data, you'll also see a status attribute as part of the object, so you could then access it as usual $lesson->status (PHP) or lesson.status (JS).
For more information, see the official Laravel documentation on accessors and mutators

Dynamic index in ng-repeat value

I have a simple ng-repeat iterating through an array of objects.
The ng-repeat contains a ng-model input element for which I need to use a dynamic value as the array index. Probably very unclear explanation so here is the code :
<div ng-repeat="property in current_data.object_subtype.object_property_type" ng-init="set_input_state()" class="input-group ng-scope disabled">
<span class="input-group-addon">{{property.name}}</span>
<input type="text" placeholder="{{property.name}}" ng-model="current_data.properties[getIndexFromId(current_data.properties, property.object_property_type_id)].value" class="form-control" disabled="disabled">
The problem is that the input stays empty. I've tested some combinations and found this to work :
getIndexFromId(current_data.properties, property.object_property_type_id) == 0
current_data.properties[0].value gives the expected output
So somehow getIndexFromId(current_data.properties, property.object_property_type_id)is not well accepted by Angular or I made a stupid mistake somewhere ...
Does anyone know what's wrong with this?
Thanks!
[edit]
Here is a sample of the data behind all this :
{
"id": 1,
"name": "Robert Smith",
"object_subtype_id": 1,
"object_subtype": {
"id": 1,
"description": "Manager",
"object_property_type": [
{
"id": 1,
"description": "Phone number"
},
{
"id": 2,
"description": "Hair color"
},
{
"id": 3,
"description": "Nickname"
}
]
},
"properties": [
{
"id": 1,
"value": "819-583-4855",
"object_property_type_id": 1
},
{
"id": 2,
"value": "Mauves",
"object_property_type_id": 2
},
{
"id": 3,
"value": "Bob",
"object_property_type_id": 3
}
]
}
From what I've seen of Angular, the content of the attributes are not executed as javascript. It's a custom parsed and executed mini-language that doesn't support complex indexing.
With that said, its probably for the best. Any sufficiently complex logic should be handled by the controller or a service.
function MyController($scope) {
$scope.set_current_data_value = function (current_data, property) {
var index = $scope.getIndexFromId(current_data.properties, property.object_property_type_id);
current_data.properties[index].value = $scope.property_name;
}
}
Then your html would look something like:
<input type="text" placeholder="{{property.name}}" ng-model="property_name" ng-change="set_current_data_value(current_data, property)" class="form-control" disabled="disabled">
You may also be able to use ng-submit if you don't need to update your model in real time.

Categories

Resources