AngularJs Component and angularjs-dropdown-multiselect - angularjs

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

Related

How to pass value from controller to html in angular 1.4.4

I am trying to pass a value which I am getting in response to calling REST service from my controller to my view(which is in HTML). But it says "vm is not defined at errorCallback (configuration.controller.js?c133:21)". Below is my code snippet. What am I missing?
Here is my Controller
/*#ngInject*/
constructor($rootScope, ConfigurationService){
Object.assign(this, {$rootScope, ConfigurationService});
this.name = 'configuration';
let vm = this;
vm.scheduleTimePeriod = 10;
vm.dataTTL = 20;
}
loadData() {
this.ConfigurationService.getConfigurationDetails().then(function successCallback(response) {
console.log(response);
vm.scheduleTimePeriod = response.scheduleTimePeriod;
vm.dataTTL = response.dataTTL;
}, function errorCallback(response) {
console.log("error response: ", response.statusText);
});
}
}
export default ConfigurationController;
Here is my HTML view page
<form class="form-horizontal exampleForm" ng-init="vm.loadData()">
<div class="form-group required">
<label class="control-label" for="datattl">Data TTL</label>
<input ng-model="vm.dataTTL" id="datattl" name="datattl" type="text" class="form-control" style="width:100%;" required>
</div>
<div class="form-group required">
<label class="control-label" for="scheduletimeperiod">Schedule Timeperiod</label>
<input ng-bind="vm.scheduleTimePeriod" id="scheduletimeperiod" name="scheduletimeperiod" type="text" class="form-control" style="width:100%;" required>
</div>
<div class="cf clearRow">
<button ng-click="vm.postRequest()" type="button" class="btn btn-primary">Submit</button>
</div>
</form>
problem is in load data function
loadData() {
const vm = this;
this.ConfigurationService.getConfigurationDetails().then(function successCallback(response) {
console.log(response);
vm.scheduleTimePeriod = response.scheduleTimePeriod;
vm.dataTTL = response.dataTTL;
}, function errorCallback(response) {
console.log("error response: ", response.statusText);
});
}
}
You are trying to use the "Controller as vm" approach. This means that your view will be able to directly reference the controller instance as the "vm" variable. Now that this is clear, let's have a look at your code.
Your problem is that currently you are defining the vm variable directly in the controller constructor. Since typescript has well defined variable scopes, that variable does not exist outside the controller scope. As a result, when you try to access it in the loadData function you receive an error, because vm no longer exist.
As others mentioned, one possible solution is to just redefine the vm variable inside the method.
That said, you don't need the variable in the first place.
"VM" is not a magical variable that you have to use in the controller in order for things to word. The "Controller as" pattern is just what it says on the thin - a pattern that allows you to access your controller ("exported/visible" parts) from the view, by giving it an alias. By itself, the name "vm" does not bear any meaning - "vm" is just a shorthand for "view-model" and that why most samples uses that name for the controller.
Let's look at a sample (courtesy of John Papa)
angular.module('app')
.controller('Customers', [function() {
var vm = this;
vm.title = 'Customers';
vm.customers = [
{name: 'Haley'}, {name: 'Ella'}, {name: 'Landon'}, {name: 'John'}
];
}]);
As you can see, back in the Javascript world, a controller was just a function. For this reason, people preferred to explicitly define the vm variable and assign the controller properties / function to it.
But you are using Typescript.
Because of this, you probably should avoid defining the vm variable in your controller in the first place. Since your controller is now a typescript class, it is probably much clearer to just define the variables / methods directly in the controller.
Your code would become something similar to this:
export default class ConfigurationController implements angular.IController {
name: string;
scheduleTimePeriod : number;
dataTTL : number;
constructor(private $rootScope, private ConfigurationService){
this.name = 'configuration';
this.scheduleTimePeriod = 10;
this.dataTTL = 20;
}
$onInit?(): void {
// required if you use the angular.IController interface from the Angularjs type definitions. You can ignore this method if that's not the case.
}
public loadData(): void {
this.ConfigurationService.getConfigurationDetails()
.then((response) => {
console.log(response);
this.scheduleTimePeriod = response.scheduleTimePeriod;
this.dataTTL = response.dataTTL;
}, (response) => {
console.log("error response: ", response.statusText);
});
}
}

angular material md-autocomplete dynamic loading issue

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

AngularJS ng-click doesn't work in digest cycle

Html:
<a href id="link "ng-click="print(arg)"> print </a>
Angularjs controller:
$scope.return_promise =function(arg){
return $http.post('\path');
)};
$scope.print = function(arg){
url ="other/path/"
$scope.return_promise(arg).then(function(r){
if(r){
$('#link').attr('href', url);
});
};
Problem: I checked with chrome debugger, the href actually updated, but the event doesn't trigger (i.e. not go to the url). If I click it again, it works.
If I add a statement document.getElementById('#link').click() at the end of if clause, it will prompt an error "digest cycle is in progress"
How can i solve this.
Not sure if I get your question. First, check if the code you paste is the code you wanted add here, because it has numerous errors. If you would like to replace dynamically href attribute do it like so:
<div ng-controller="SomeCtrl as ctrl">
print
</div>
(function () {
'use strict';
angular
.module('module')
.controller('SomeCtrl', SomeCtrl);
SomeCtrl.$inject = ['$scope'];
function SomeCtrl($scope) {
var vm = this;
vm.url = "#";
vm.return_promise = function (arg) {
return $http.post('/path');
};
vm.print = function (arg) {
var url = "other/path/";
vm.return_promise(arg).then(function (r) {
if (r) {
vm.url = url;
}
});
};
}
}());

How to Change Color of background in Angularjs Dynamically

I have a Poller that I have setup that has 2 files which are being queried. When new data has been found I am trying to set the color of my text background in the view but its just not happening.
If someone can solve this issue that would be great I am also welcome to suggestions to improving the structure of the code.
Service:
function Poller($http, $timeout) {
var projectcache = { response: [], calls: 0 };
var msgcache = { response: [], calls: 0 };
var newdata = false;
var msgdata = false;
var msgcolor = {};
var projectcolor = {};
var poller = function () {
$timeout(poller, 10000);
console.log("Begin Poller!");
$http.get('http://localhost/app/controllers/php/getProjects.php')
.then(function(r) {
if (r.data.projects.length > projectcache.response.length) {
newdata = true;
projectcolor = 'green';
} else {
newdata = false;
projectcolor = 'green';
};
angular.copy(r.data.projects, projectcache.response);
console.log("New Data Found: " + newdata);
});
$http.get('http://localhost/app/controllers/php/getMessages.php')
.then(function(m) {
if (m.data.messages.length > msgcache.response.length) {
msgdata = true;
msgcolor = 'green';
} else {
msgdata = false;
msgcolor = 'green';
};
angular.copy(m.data.messages, msgcache.response);
console.log("New Msg Found: " + msgdata);
});
};
poller();
return {
projects: projectcache.response,
messages: msgcache.response,
newdata: newdata,
msgdata: msgdata,
msgcolor: msgcolor,
projectcolor: projectcolor
};
};
View:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Controller:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller',
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller) {
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}]);
My first thought is to use ng-class for this. I see you already have ng-class handling the display of your 'active' class.
If you'd like to try this approach out, I would:
1. Create css clases for each state/color you want to change to. (Can do this in external css file or between tags you create at the beginning of your page.
.successBackground {
background-color:green;
}
.errorBackground {
background-color:red;
}
Modify your ng-class attributes. Here I am assuming that success means that msgdata=true and error means that msgdata=false
Current html:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Updated html:
<li ng-class="{active: selectTab=='inbox', successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects',successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='projects'">Projects</li>
Now when your msgdata is updated, the successBackground and errorBackground are automatically updated based on the latest msgdata value.
Hope this helps!
#Elevant, the comment option didn't allow me to format my code snippets, so I am replying to your latest comment in this answer post.
I'm not sure if the watcher can listen to just the Poller object or if it'll need to listen to each attribute separately (msgColor, projectColor). In my code snippet here, I'll assume we cannot and we'll need to listen to each individually.
Current code:
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
Updated with watchers:
$scope.$watch('Poller.msgcolor', function(newValue,oldValue) {
$scope.msgcolor = Poller.msgcolor;
});
$scope.$watch('Poller.projectcolor', function(newValue,oldValue) {
$scope.projectcolor = Poller.projectcolor;
);
Though if you still wanted to look into the option to move $timeout, I would make the following changes (not sure if this matches what you had tried).
In the Poller service definition remove $timeout. Updated snippet:
function Poller($http)
Still in Poller, remove this line:
$timeout(poller, 10000);
In the Controller add $timeout - updated snippet:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller','$timeout'
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller,$timeout)
Then in the controller, you would add:
$timeout(function(Poller) {
Poller.poller();
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}, 10000);
I hope this helps, I haven't had a chance to test the code, so you may have to tinker around with it a bit. Let me know how it goes!

Submit multiple checkbox to firebase

I am using Angularfire and I'd like to save data by multiple checkbox.
HTML
<form role="form" ng-submit="addTask(task)">
<label class="checkbox-inline" ng-repeat="(key, value) in students">
<input type="checkbox" id="{{key}}" value="{{key}}" ng-model="task.student[value.name]">{{value.name}}
</label>
<button type="submit">Submit</button>
</form>
JS
var ref = new Firebase(FURL);
var fbTasks = $firebaseArray(ref.child('tasks'));
$scope.addTask = function(task) {
fbTasks.$add(task);
}
This was the result
student
--Alison Kay: true
--Jessica Cook:false
--John Smith: true
--Kevin Hunt: true
My question is there any way to save them like this?
student
--(key)
--name:Alison Kay
--checked: true
--(key)
--name:Jessica Cook
--checked: false
--(key)
--name:John Smith
--checked: true
--(key)
--name:Kevin Hunt
--checked: true
I threw together a rough example PLNKR to demonstrate one way to do this by extending the AngularFire services.
Note that the documentation states:
These techniques should only be attempted by advanced Angular users who know their way around the code.
Solution
You can create a factory that extends $firebaseObject, and adds a method .addTask() which uses .push() to generate a new key for a new task.
Factories:
app.factory("TaskList",function($rootScope, $q, fbUrl, TaskListFactory){
return function(studentKey){
var ref = new Firebase(fbUrl+'/tasks/'+studentKey);
return new TaskListFactory(ref);
}
});
app.factory("TaskListFactory",function($firebaseObject, $q, fbUrl, $rootScope){
return $firebaseObject.$extend({
addTask: function(name, checked){
// use push to generate a new key, set `name` and `checked`
this.$ref().push({name: name, checked: checked}, function(error){
if(!error){
console.error(error);
} else {
console.log("Pushed new task.");
}
});
}
});
});
Controller:
Note: I used mock objects. I couldn't decode your data structure, and took a best guess approach.
app.controller('HomeController',function($scope,fbUrl, $firebaseObject, TaskList) {
// create mock student and task
$scope.students = {tester: {name: 'tester'} };
$scope.task = {tester: {name: 'test this'}};
var taskList = new TaskList('student123');
// get tasks list for debug:
var tasksRef = new Firebase(fbUrl+'/tasks');
$scope.tasks = $firebaseObject(tasksRef);
$scope.addTask = function(task) {
console.debug(task);
taskList.addTask('Tester McGee', task.student['tester']);
}
});
Result (<firebaseUrl>/tasks):
{
"$id": "tasks",
"$priority": null,
"student123": {
"-JoMxWoX0tQrGtdP6Qvm": {
"checked": true,
"name": "Tester McGee"
}
}
}
Again, the focus of this is on the factories, and not on the data structure. The form data in my example doesn't make sense.
Hope that helps.

Resources