angular material md-autocomplete dynamic loading issue - angularjs

I'm using Angular Material component "md-autocomplete" in my project.
We are trying to render dynamic response that we are getting from "md-list" component's item-click's event call.
Issue: However, before the event call is invoked, md-autocomplete method is invoked.
My Requirement: Is there a way to invoke the event call before invoking md-autocomplete method.
Here we attached sample images, that show's you basic response what we need as output.
I tried below code, But it is not working. I need solution for the problem.
HTML Source Code:
md-list code
<md-list>
<div ng-repeat="object in ['object 1', 'object 2', 'object 3', 'object 4', 'object 5', 'object 6'] track by $index">
<md-list-item class="md-2-line contact-item" ng-click="listItemClick($event, object)">
<div class="md-list-item-text compact">
<h3>Object data displayed here like object 1, object 2 and etc ...</h3>
</div>
</md-list-item>
</div>
</md-list>
md-autocomplete code :
<md-autocomplete
ng-disabled="isDisabled"
md-no-cache="noCache"
md-selected-item="selectedItem"
md-search-text-change="searchTextChangeEvent(searchText)"
md-search-text="searchText"
md-selected-item-change="selectedItemChangeEvent(item)"
md-items="item in search(searchText)"
md-item-text="item.id"
md-min-length="0"
placeholder="Search by id ..."
md-menu-class="autocomplete-custom-template">
<md-item-template>
<span class="item-title">
{{id}}
</span>
</md-item-template>
</md-autocomplete>
AngularJS Script Code :
(function() {
var app = angular.module('module-name');
var controller = function($scope, $rootScope,$http, $timeout, $q, $log) {
var self = this;
self.simulateQuery = false;
self.isDisabled = false;
$rootScope.objectName = "object 1";
self.response = loadValues($rootScope.objectName);
self.search = search;
self.selectedItemChangeEvent = selectedItemChangeEvent;
self.searchTextChangeEvent = searchTextChangeEvent;
// ******************************
// Internal methods
// ******************************
/**
* Search for repos... use $timeout to simulate
* remote dataservice call.
*/
function search (query) {
var results = query ? self.response.filter( createQueryFilterFor(query) ) : self.response,
deferred;
if (self.simulateQuery) {
deferred = $q.defer();
$timeout(function () { deferred.resolve( results ); }, Math.random() * 1000, false);
return deferred.promise;
} else {
return results;
}
}
function searchTextChangeEvent(text) {
$log.info('Text changed to ' + text);
}
function selectedItemChangeEvent(item) {
$log.info('Item changed to ' + JSON.stringify(item));
}
/**
* Build `components` list of key/value pairs
*/
function loadValues(name) {
var dynamicData = '';
if(name === "object 1") {
dynamicData = [{
"id": 1,
"name": "some name here"
},{
"id": 2,
"name": "some name here"
}];
} else if(name === "object 2") {
dynamicData = [{
"id": 3,
"name": "some name here"
},{
"id": 4,
"name": "some name here"
}];
} else if(name === "object 3") {
dynamicData = [{
"id": 5,
"name": "some name here"
},{
"id": 6,
"name": "some name here"
}];
}
return dynamicData.map( function (response) {
response.value = response.id.toLowerCase();
return response;
});
}
/**
* Create filter function for a query string
*/
function createQueryFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.value.indexOf(lowercaseQuery) === 0);
};
}
$scope.listItemClick = function(event, object) {
$rootScope.objectName= object.someFiledName; // It will give md-list-item name (like object 1 or object 2 and etc ...)
self.response = loadValues($rootScope.name);
}
};
app.controller("controller", controller)
}());

I have taken your code and created a plunkr demo. I tweaked it few places, please feel free to explore the code in plunkr. Now suggestions are loading according to object selected.
I think filter logic was not correct according to use case you mentioned here so I corrected createQueryFilterFor function logic. I made changes in md-item-template section too.
function createQueryFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
//below condition updated to match search id
return (item.value.toString() === lowercaseQuery);
};
}
see demo

I've fixed some issues in your code and corrected the hovering and double-clicking issue as per your comment in another answer.
Here's your updated code: http://codepen.io/anon/pen/ryXyxj
Note to communicate between controllers it is considered better practise to use a shared service rather than setting values on the $rootScope:
app.controller('mainController', function($scope, menuSelection) {
$scope.menuSelection = menuSelection; // retrieve settings object from service method and bring into scope
// now whenever one sets $scope.menuSelection.selected = "object 2", it will update the value in the other controller as well (and vice-versa)
});
app.controller('secondController', function($scope, menuSelection) {
$scope.menuSelection = menuSelection; // retrieve settings object from service method and bring into scope
});
app.factory('menuSelection', function() {
var settings = {};
settings.selected = 'Object 1'; // default
return settings;
});
You can find a demo of 2 controllers communicating via a service here: https://stackoverflow.com/a/42408380/1544886

Related

AngularJs Component and angularjs-dropdown-multiselect

I am new to AngularJs world and was trying to use angularjs-dropdown-multiselect inside component.
Component's HTML looks like:
<div>
<select id="statusSelection" class="form-control"
ng-options="status.name for status in $ctrl.statuses track by status.id"
ng-model="$ctrl.status" ng-change="$ctrl.filterChanged()"></select>
</div>
<div ng-dropdown-multiselect="" options="$ctrl.categories" selected-model="$ctrl.category"
events="$ctrl.events">
</div>
Event on status changed and category will call the same action.
MultiselectController.prototype.filterChanged = function() {
this.onFilterChange({
status: this.status,
category: this.category});
};
MultiselectController.prototype.events = {
onItemSelect: function(item) {
filterChanged();
},
onItemDeselect: function(item) {
filterChanged();
}
};
When I try to run the above code and change the status, the code works as expected but fails during Category change(error in console).
Error message: ReferenceError: filterChanged is not defined
Angular version: 1.5.8
angularjs-dropdown-multiselect: 1.11.8
Plunker: http://plnkr.co/edit/JL7N6M?p=preview
Thanks for helping me out here.
I have created an instance variable and initialize it to instance of Controller.
var _instance;
function MultiselectController($scope) {
this.statuses = testMultiselect.statuses;
this.categories = testMultiselect.categories;
this.$scope = $scope;
this.setDefault();
_instance = this;
}
Now, I am using this instance variable to access the functions on Controller.
MultiselectController.prototype.events = {
onItemSelect: function(item) {
_instance.filterChanged();
},
onItemDeselect: function(item) {
_instance.filterChanged();
}
};
I am not completely happy with this as there should be better way to do the same but until I find, I will keep this.
Updated Plunker: http://plnkr.co/edit/D7BKI9?p=preview

In AngularJS, $http doesn't change my select option tag data

I have an issue in AngularJS on using $http to fetch data from server.
Here is my HTML
<select ng-model="foo" ng-options="item as item.name for item in items"></select>
Here is my AngularJS Script
angular.module("myApp").controller("myCtrl", function($scope, $http) {
var $scope.items = [];
$scope.items = [
{
"id": 1,
"name": "item1"
},
{
"id": 2,
"name": "item2"
},
];
getData();
function getData() {
$http.get("ng/getData")
.then(function(response) {
if (response.status == 200) {
$scope.items = response.items;
/*
$scope.items = [
{
"id": 5,
"name": "item11"
},
{
"id": 4,
"name": "item22"
}
];
*/
}
});
}
});
What expect from this code is when $http fetches data from server, then select dropdown data will change. But it's not changing anything. I have also printed the response items in the console inside the success callback.
Maybe I don't understand $http usage well enough. Perhaps when I console out data after getData(); $scope.items doesn't change at all. And I think that maybe $http always run at last stage.
Can anybody help to explain this issue? If my assumption is correct, what is the solution that I am looking for?
I think you simply have to add a track by clause:
ng-options="item as item.name for item in items track by item.id"
Check the response object. When you are using 'then' callback to get the resolved data then the actual API result is stored in the 'data' property of the response. So change your code to
$scope.items = response.data;
Or you can use the success callback to attach the data directly.
$http.get("ng/getData")
.success(function(response) {
if (response.status == 200) {
$scope.items = response.items;
}
});

I cant get the ionic autocomplete to work with

I am using guylabs/ion-autocomplete but facing some problems when running it in the ios and android emulator. Beforehand I made a mockup running it in the browser and this worked fine, but when copying the same code to my actual project I got an error. Could someone please help me.. dont have any clue!
The following error I got in the console:
[Error] Error: collection-repeat expected attribute collection-item-height to be a an expression that returns a number (in pixels) or percentage.
HTML
<div class="list card">
<ion-autocomplete
ng-model="model"
item-value-key="view"
item-view-value-key="name"
item-view-value-key="id"
items-method="getTestItems(query)"
items-method-value-key="items"
items-clicked-method="itemsClicked(callback)"
select-items-label="SEARCH"
selected-items-label="Selected:"/>
</div>
JS
tcControllers.controller('SomeCtrl', function ($scope, $stateParams, $localStorage) {
$localStorage.getObject('Something').forEach(function(ticket){
if (ticket.id == parseInt($stateParams.ticketId)){
$scope.ticket = ticket;
}
});
$scope.model = "";
$scope.callbackValueModel = "";
$scope.getTestItems = function (query) {
var zoekItems = [
{id: "1", name: "John", view: "John: "},
{id: "2", name: "Richard", view: "Richard: "},
{id: "3", name: "Steve", view: "Steve: "},
];
var returnValue = { items: [] };
zoekItems.forEach(function(item){
console.log(item);
if (item.name.indexOf(query) > -1 ){
returnValue.items.push(item);
}
else if (item.id.indexOf(query) > -1 ){
returnValue.items.push(item);
}
});
return returnValue;
};
$scope.itemsClicked = function (callback) {
$scope.callbackValueModel = callback;
}
});
This seems to be an issue with the collection-repeat directive that autocomplete is using. It's probably best to update to the latest stable Ionic version (1.0.0) which will resolve this issue. You can also go into lib/ion-autocomplete/dist/ion-autocomplete.js and add collection-item-height="52" in place of item-height (line 88) and it should work, but I would recommend updating.

AngularJS: Trying to make filter work with delayed data

I have this html:
<div ng-app='myApp'>
<div ng-controller='testCtrl'>
<h3>All possesions</h3>
{{possessions}}
<h3>Green cars</h3>
{{greenishCars}}
</div>
</div>
And this script:
angular.module('myApp', [])
.factory('getPossessionsService', ['$timeout',
function($timeout) {
var possessions = {};
$timeout(function() {
possessions.cars = [{
model: "Mazda 6",
color: "lime green"
}, {
model: "Audi A3",
color: "red"
}, {
model: "Audi TT",
color: "green"
}, {
model: "Volkswagen Lupo",
color: "forest green"
}];
possessions.jewelry = [{
type: "ring",
metal: "gold"
}, {
type: "earring",
metal: "silver"
}];
}, 1000);
return possessions;
}
])
.controller('testCtrl', ['$scope', 'filterFilter', 'getPossessionsService',
function($scope, filterFilter, getPossessionsService) {
$scope.possessions = getPossessionsService;
$scope.greenishCars = filterFilter($scope.possessions.cars, carColorIsGreenShade);
function carColorIsGreenShade(car) {
return ['green', 'forest green', 'lime green'].indexOf(car.color) != -1;
}
}
]);
I am trying to get $scope.greenishCars to update correctly when the data is available, but it is not. I understand that this has because $scope.possessions.cars is an array and therefore not a reference to the data, so it is not updated. But how should I alter my script so that greenishCars get updated when the data arrives? I am guessing I should use $scope.possessions "directly", but I do not quite see how I should rewrite this nicely....
See this plunk.
Edit: Thoughts on which answer to choose
As in comments in the answers to Oliver and MajoB, I ended up with using both filter and watch. In my special case the request for the data was made in another place (not in the controller of my page in question), so it was not so easy to act on the resolving of the promise (with promise.then as suggested by Oliver), I therefore used a watch. But there is a couple of things to be aware of with watches. If the variable you want to watch is not on the scope, then you must provide the variable by returning it from a function. And if you want to watch for a change in an existing property (say somebody repaints my existing Mazda 6 in a different color), then none of the watch-answers works, as you need to add "true" when calling $watch. When you add 'true' as the second paramenter to $watch, then it watches for changes in the actual values of the variable, and it also does this check for values deeply in an object/array (without it just checks references, see this blog). I ended up with this controller/$watch for my real-life use-case (which is a little bit different that in my example above, and is based on Olivers filter-plunk), and it looks like this (changed to fit the plunk):
.controller('testCtrl', ['$scope', 'greenFilter', 'getPossessionsService',
function($scope, greenFilter, getPossessionsService) {
var none-scope-possessions = getPossessionsService; // I do not want to expose all properties on the scope....
$scope.greenishCars = [];
$scope.$watch(function() {return none-scope-possessions.cars}, function(newValue){
if (newValue) {
$scope.greenishCars = greenFilter(newValue);
}
},true);
}
]);
You can watch the data changes:
$scope.$watch('possessions.cars', function(){
$scope.greenishCars = filterFilter($scope.possessions.cars, carColorIsGreenShade);
});
I would recommend just building your own custom filter like so:
.filter('green', function() {
return function(possessions) {
if (!possessions) return null;
var filtered = [];
angular.forEach(possessions, function(possesion){
if (['green', 'forest green', 'lime green'].indexOf(possesion.color) != -1) {
filtered.push(possesion);
}
});
return filtered;
};
})
Then pass your collection through that filter like so:
<div ng-controller='testCtrl'>
<h3>All possesions</h3>
{{possessions}}
<h3>Green cars</h3>
{{possessions.cars|green}}
</div>
Everything else will happen automatically. See the updated plunkr.
You can watch possesions collection for changes:
$scope.$watchCollection('possessions', function (newValue) {
if (newValue)
{
$scope.greenishCars = filterFilter(newValue.cars, carColorIsGreenShade);
}
});
http://plnkr.co/edit/Sl6dW0pqKr593OXrgDb9?p=preview
First thing is , var possessions = {}; you have declared an object , cars is an field in this object , which has array of cars . so either return possessions.cars or in the markup call it via possessions.cars[0].. if you want to call only one value ..or if you want to display all the values the use ng-repeat=" car in possessions.cars" .

WebSQL data into AngularJs DropDown

I have very simple question about getting data from WebSql
I have DropDown i.e
<select id="selectCatagoryFood" data-role="listview" data-native-menu="true"
ng-init="foodCatagory = foodCatagories.cast[0]"
ng-options="foodCatagory as foodCatagory.text for foodCatagory in foodCatagories.cast"
ng-model="foodCatagory"
ng-change="changeFoodCatagory()">
</select>
now i want to add data init from webSQL. I already get Data from webSql but i am confuse that how to add that data into DropDown
An example or hints maybe very helpful for me.
Update 1 :: Add Controller Code
myApp.controller('foodSelection',function($scope,foodCatagories){
$scope.foodCatagories = foodCatagories;
$scope.changeFoodCatagory = function(){
alert($scope.foodCatagory.value);
}
});
Update 2 webSQL and JayData
_context.onReady({
success: showData,
error: function (error){
console.log(error);
}
});
function showData(){
var option = '';
_context.FoodGroup.forEach(function(FG)
{
option += '<option value="'+FG.FoodGroupID+'">'+FG.Description+'</option>';
}).then(function(){
console.log(option);
});
}
Update 3
var myApp = angular.module('myApp',[]);
myApp.factory('foodCatagories',function(){
var foodCatagories = {};
foodCatagories.cast = [
{
value: "000",
text: "Select Any"
}
];
return foodCatagories;
});
Update 4
One thing that i didn't mention is that I am using JayData for getting data from webSQL to my App
I will try to explain how it works:
EDIT: Live demo
html
Here is your stripped down select.
<select ng-options="item as item.text for item in foodCategories"
ng-model="foodCategory"
ng-required="true"
ng-change="changeFoodCategory()">
</select>
The directive ng-options will fill automatically the option elements in your select. It will take the foodCategories variable from the $scope of your controller and foreach item in the collection, it will use the text property as the label shown (<option>{{item.text}}</option>') and it will select the whole objectitemas the value of the selectedoption. You could also refer to a property as the value like ({{item.text}}). Then yourng-modelwould be set to theid` value of the selected option.
The directive ng-model corresponds to the variable in the $scope of your controller that will hold the value of the selected option.
The directive ng-required allows you to check if a value has been selected. If you are using a form, you can check if the field is valid formName.ngModelName.$valid. See the docs for more details on form validation.
The directive ng-change allows you to execute a function whenever the selected option changes. You may want to pass the ng-model variable to this function as a parameter or call the variable through the $scope inside the controller.
If no default value is set, angular will add an empty option which will be removed when an option is selected.
You did use the ng-init directive to select the first option, but know that you could set the ng-model variable in your controller to the default value you would like or none.
js
Here I tried to simulate your database service by returning a promise in the case that you are doing an async request. I used the $q service to create a promise and $timeout to fake a call to the database.
myApp.factory('DbFoodCategories', function($q, $timeout) {
var foodCategories = [
{ id: 1, text: "Veggies", value: 100 },
{ id: 2, text: "Fruits", value: 50 },
{ id: 3, text: "Pasta", value: 200 },
{ id: 4, text: "Cereals", value: 250 },
{ id: 5, text: "Milk", value: 150 }
];
return {
get: function() {
var deferred = $q.defer();
// Your call to the database in place of the $timeout
$timeout(function() {
var chance = Math.random() > 0.25;
if (chance) {
// if the call is successfull, return data to controller
deferred.resolve(foodCategories);
}
else {
// if the call failed, return an error message
deferred.reject("Error");
}
}, 500);
/* // your code
_context.onReady({
success: function() {
deferred.resolve(_contect.FoodGroup);
},
error: function (error){
deferred.reject("Error");
}
});
*/
// return a promise that we will send a result soon back to the controller, but not now
return deferred.promise;
},
insert: function(item) {
/* ... */
},
update: function(item) {
/* ... */
},
remove: function(item) {
/* ... */
}
};
});
In your controller you set the variables that will be used in your view. So you can call your DbFoodCategories service to load the data into $scope.foodCategories, and set a default value in $scope.foodCategory that will be used to set the selected option.
myApp.controller('FoodSelection',function($scope, DbFoodCategories){
DbFoodCategories.get().then(
// the callback if the request was successfull
function (response) {
$scope.foodCategories = response; //response is the data we sent from the service
},
// the callback if an error occured
function (response) {
// response is the error message we set in the service
// do something like display the message
}
);
// $scope.foodCategory = defaultValue;
$scope.changeFoodCategory = function() {
alert($scope.foodCatagory.value);
}
});
I hope that this helped you understand more in detail what is happening!
See this example and how use $apply to update the data in scope.
in the new version we released a new module to support AngularJS. We've started to document how to use it, you can find the first blogpost here
With this you should be able to create your dropdown easily, no need to create the options manually. Something like this should do the trick:
myApp.controller('foodSelection',function($scope, $data) {
$scope.foodCatagories = [];
...
_context.onReady()
.then(function() {
$scope.foodCatagories = _context.FoodGroup.toLiveArray();
});
});
provided that FoodGroup has the right fields, of course

Resources