AngularJS/UI-Router change state from select/option - angularjs

So I have a SPA using AngularJS/UI-Router and I have a section where I need to change states from a select/option. I originally had links in an ng-repeat like this:
<ul class="unstyled">
<li data-ng-repeat="course in courses">
<a data-ui-sref=".materials({ courseCode:course.CourseCode })">{{course.Title}}</a>
</li>
</ul>
The issue is that I am going to have upwards of 50 different courses to choose from, so I want to put them into a select/option setup something like this:
<select style="padding:3px 1px; width:100%;" data-ng-model="placeholder" data-ng-options="course.Title for course in courses track by course.CourseCode"></select>
Now, I know I can change the scope with the above by setting up an ng-repeat and just using that ng-model="placeholder", but I need the ability to change states from that select/option. Basically, I have a check that occurs on state change that will either deny or allow access based on a permissions check. If I can do the permissions check from this state, I am more than open to that as an option, but it seemed easier to switch states and do the check on that.
As always, thank you in advance!
Edit: So I looked some more into this and found that I might be able to do something with ng-switch and UI-Router's $state.go. Here is what I have come up with so far:
The controller:
.controller('CoursesTrackCtrl', function ($scope, $stateParams, $http, $log, $state) {
$scope.changeState = function (course) {
$state.go('.materials({ courseCode:course.CourseCode })');
}
});
The state with the select:
<select style="padding:3px 1px; width:100%;" data-ng-model="course" data-ng-options="course.Title for course in courses" data-ng-change="changeState(course)">
<option value="">-- Select a Course --</option>
</select>
I am getting an undefined is not a function when I change the option in the select menu. I can get an alert to pop and pass through pieces of the courses so I know that course is being passed through the scope just fine. I made sure that $state had been added to the controller, but it seems like $state.go isn't going anywhere and I am still not even sure if that would be the correct syntax to actually pass through $stateParams like I would with a normal ui-sref.
Solution:
With the help of David Spence, I was able to solve the error I was having. I was both injecting $state into the controller and using it as a parameter so it was throwing an error. Thank you!
As for the solution, this was the final section of code for the controller:
.controller('CoursesTrackCtrl', function ($scope, $stateParams, $http, $log, $state) {
$scope.changeState = function (course) {
$state.go('courses.track.materials', { courseCode: course.CourseCode });
}
});
The big change here was the syntax for $state.go, which needed to resolve the full path of the state (courses.track.materials vs. just .materials) and the syntax for the parameters differed from the normal ui-sref syntax so it had to be setup as a { parameter : value }.
And for the state itself:
<select style="padding:3px 1px; width:100%;" data-ng-model="course" data-ng-options="course.Title for course in courses" data-ng-change="changeState(course)">
<option value="">-- Select a Course --</option>
</select>
This had been mostly unchanged, but the big thing was to ensure that course was being passed through the scope.
Hope this helps someone else too!

The only thing I can see that is wrong is that $state is being passed as a parameter the changeState method instead of being injected into the controller. See example below without any errors. Just check the state name is correct you are changing to. Looks a bit odd the way it starts with a dot...
function BasicController($scope) {
$scope.changeState = function(course) {
console.log(course.Title);
// todo: transition!
}
$scope.courses = [{
Title: 'maths'
}, {
Title: 'science'
}];
}
DEMO

Related

Select Dropdown not displaying $scope value in model

I've used angular plenty of times, but can't seem to spot the issue I'm getting:
I pass through an ID like so as a clickable link within a repeater:
<td data-title="'Company Name'" sortable="'CompanyName'"><a ui-sref="Locations({company_id: row.CompanyID})">{{row.CompanyName}}</a> </td>
The value is correctly passed through and received by the relevant controller I use a query string service to get the relevant filters. The console.log correctly shows the correct value. However, it is not selecting the relevant value within the select dropdown:
angular.module('app').controller("LocationsController", ['$timeout',
'$scope', '$http', 'QueryStringService', 'NgTableParams', '$location',
function ($timeout, $scope, $http, QueryStringService, NgTableParams,
$location) {
var default_filters = { location_name: "", address_1: "", company_id: "" };
$scope.filterBy = QueryStringService.getFilters(jQuery.extend(true, {}, default_filters));
console.log($scope.filterBy.company_id);
}
// Displayed on relevant view
<select ng-model="filterBy.company_id" ng-options="option.CompanyID as option.CompanyName for option in companies" class="form-control form-control-query">
<option value="">All</option>
</select>
Can anyone notice anything?
I think you are facing a controller scope issue:
You are setting a variable in a controller instance, and trying to get it from another instance.
The fact is that a controller is instancied for each page, even if it is the same controller name (that may be confusing).
You should use a service to store this variable (for example your QueryStringService), and access it from your other page.
Here is a JSFiddle demo.

How to display resource object in Angular JS

I have a problem with displaying records in angular...
query like this..
Angular Controller
angular.module('MyApp')
.controller('CountriesCtrl', function ($scope, $rootScope, $routeParams, Countries) {
Countries.query({}, function (countries) {
//Countries.get() return Expected response to contain an object but got an array so i use countries.query....
$scope.countries = countries;
console.log(countries);
});
})
which returns
.. Using the returned data How can I show in angular with ng-repeat..
thank you
Use ng-repeat over the array:
<div ng-repeat="country in countries">
<span>{{country.name}}</span>
</div>
I suggest anyway to follow some good tutorial and the documentation: this is pretty much basic stuff. Documentation on ng-repeat can be found here

Selecting the proper item in a select list when the form is populated before the list

Let's say I have a form for editing customers with a cities list specified through a select element. The real form has a lot of lists, however, this will do for now.
The form is populated by making a rest api call called /customer/{customerId}.
The list is populated by making a rest api call called /cities.
The calls are made in sequence in the controller using $http.get to get the cities and the other one via a $resource get (not in the code snippet below).
The problem is that in case the call to get the customer data for comes back first, the form selects the first city in the list instead of the right city.
Here are some code snippets:
html:
<select id="Select3" class="form-control" ng-model="formData.CityId">
<option ng-repeat="j in Cities" value="{{j.CityId}}">{{j.City}}</option>
</select>
Controller:
.controller('customerInfoCtrl', ['$scope', '$http', '$modal', '$location', 'CustomerApi', function ($scope, $http, $modal, $location, CustomerApi) {
$http.get('../api/v1/cities').success(function (data) {
$scope.Cities = data;
});
$scope.formData = CustomerApi.get({ id: $scope.customerId }, function () {
...
});
....
How do I fix this? Keep in mind that the form has lots of lists. Is there a pattern that can be applied systematically?
I know about $scope.$apply() or $scope.$apply(fn). Should I call one of these in the success function for the call that gets the cities?
Thanks
An update:
Converting to ng-options seems to do the trick. I have to do more testing.
<select id="Select3" class="form-control" ng-model="formData.CityId" ng-options="j.CityId as j.City for j in Cities">
</select>
Another update:
I created a plnkr where you can see the issue: http://plnkr.co/edit/Se8OIuxYBv4QXeH3Jgqh?p=preview
if you want to make sure that some data loads before other, use the callback function you get when using both of your get() methods.
So if you wanted to load cities first, it could be something like that:
$http.get(../api/v1/cities').success(function (data) {
$scope.Cities = data;
//now that the cities are loaded, you can loead something else here.
$scope.formData = CustomerApi.get({ id: $scope.customerId }, function () {
//this will always be loaded after cities
});
$scope.loadSmthElse....
}

Angularjs select list not initialize default value

I have 2 select lists:
<select class="input-large" ng-model="bu.box.categoryId" ng-change="getSubCats()">
<option ng-repeat="cat in cats" value="{{cat.id}}">{{cat.name}}</option>
</select>
<select class="input-large" ng-model="bu.box.subCategoryId">
<option ng-repeat="subcat in subCats" value="{{subcat.id}}">{{subcat.name}}</option>
</select>
The object bu, subcats is injected to my controller from resolve and exists before bindings is render and cats i get from local storage:
$stateProvider.state('box',
{
url: '/box-card/:id',
templateUrl: '/partials/main.module/contollers/box.html?v=' + global_app_version,
controller: 'BoxController as boxCtrl',
resolve: {
Box: function ($stateParams, httpService) {
return httpService.getBox({ boxid: $stateParams.id });
}
}
})
Controller variables initialization look like this:
function boxController($scope, localStorageService, httpService, $state, appData, uiGridConstants, $modal, helpersService, $stateParams, $sce, Box) {
$scope.bu = Box.data.bu;
$scope.cats = localStorageService.get("cats");
$scope.subCats = Box.data.currentSubCats;
............
var controllers = angular.module('app.controllers');
controllers.controller('BoxController', boxController);
The problem is, when the select lists is rendered, they not initialized correctly,
The first option is selected instead of relevant initialization by ng-model.
What happen here? Why is not working correctly?
I checked all variables in debug, all fine... Need help here.
Try to solve the problem with ng-selected.
<select class="input-large" ng-model="bu.box.categoryId" ng-init="cat = cats[0]" ng-change="getSubCats()">
<option ng-repeat="cat in cats" value="{{cat.id}}">{{cat.name}}</option>
</select>
<select class="input-large" ng-model="bu.box.subCategoryId" ng-init="subcat = subCats[0]">
<option ng-repeat="subcat in subCats" value="{{subcat.id}}">{{subcat.name}}</option>
</select>
I use in my project ng-init like ng-init="subcat = subCats[0]"
change the subCats[0] and cats[0] for your init values
Try using $scope.$apply() .
From the article:
If you write any code that uses Ajax without $http, or listens for
events without using Angular’s ng-* listeners, or sets a timeout
without $timeout, you should wrap your code in $scope.$apply
It looks like exactly your case. Box is updated in a background request and angular listeners do not know that it was updated.
UPD1
Also Just noticed that the problem could hide here
$scope.bu is initialized before $scope.cats so model actually tries to match a still empty list of options.
upd2
just noticed in the comment that ng-options binding was a bit off.
try using
<select ng-options="cat.name for cat.id in cats track by cat.id" class="input-large" ng-model="bu.box.categoryId" ng-change="getSubCats()">

Sharing data between directives using attributes instead of services

I wanted to make a directive that would essentially act like a specialized input field. After some logic & user input, the 'value' attribute would be populated with a string of comma separated timeslots (hh:mm).
<time-slot value=""></time-slot>
becomes
<time-slot value="01:00,02:00,03:00"></time-slot>
I'd like to provide the flexibility for anyone to place a scope reference in the 'value' attribute tag -- whenever the attribute value is updated, so is the scope reference. (In the code below, myModel.times would be in the MyController scope).
<div ng-controller="MyController">
<time-slot value="{{ myModel.times }}"></time-slot>
</div>
I have had no problems accessing the 'value' attribute in the directive. However, I have not achieved two-way binding -- myModel.times never captures the changed value, even though the contents of the attribute have been changed when I inspect the element during runtime. I am using $attrs.$set to alter the value attribute.
I'm not sure if I'm missing something conceptually, or just missing some extra syntax. To keep this directive modular and shareable, I don't want to use a service to share data between the controller and directive, nor do I want to use a cascading scope. I think it would be optimal if the value attribute can simply be referenced by a scope variable and used as desired, much like a simple input tag:
<input ng-model="model.someText"></input>
An example with two-way data binding: See plunkr.
angular.module('myApp', [])
.directive('timeSlots', function() {
return {
scope: { value: '=' },
link: function($scope, $elem, $attrs) {
// you can access $scope.value here (after it has been interpolated)
}
};
})
.controller('MainCtrl', ['$scope', function($scope) {
$scope.value = 42;
}]);
In HTML:
<div ng-controller="MainCtrl">
<time-slots value="value"></time-slots>
</div>

Resources