Update directive's template on controller's variable change - angularjs - json

What I'm trying to do is to get a JSON object from a $http request, requested inside a controller, and build a directive that displays the multiple objects in the JSON object in a grid.
The problem is that when the object arrives, I have to process it in the directive's controller to be able to use it in the template, as such, when the JSON object changes, it is not reflected in the template. How can I make the directive know about a change in the object and force it to reload the template?
// The Directive code
amobile.directive('grid', function() {
return {
restrict: 'E',
scope: {
break: '=break',
source: '=source'
},
controller: function($scope) {
var source = $scope.source;
$scope.final_data = new Array(source.length);
if(source){
for(var j=0; j < source.length; ++j){
var total = Math.ceil(source[j]['Division'].length / $scope.break);
var data = new Array(total);
for (var i = 0; i < total; ++i) {
data[i] = source[j]['Division'].slice(i * $scope.break, (i + 1) * $scope.break);
}
$scope.final_data[j] = data;
}
}
},
templateUrl:'directives/grid.tpl.html',
replace: true
};
});
//The template
<div ng-repeat="data in final_data">
<div layout="vertical" layout-sm="horizontal" layout-padding class="" ng-repeat="row in data">
<div class="" ng-repeat="item in row">
<div flex style="width:100px">
{{item.name}}
</div>
</div>
</div>
//index.html
<div ng-controller="DivisionsCtrl as div">
<material-button ng-click="div.go()" class="material-theme-red">Button</material-button>
<div ng-if="div.data.floors">
<gridy break="3" source="div.data.floors"/>
</div>

the simplest solution would be to use watch
controller: function($scope) {
processData = function () {
var source = $scope.source;
$scope.final_data = new Array(source.length);
if(source){
for(var j=0; j < source.length; ++j){
var total = Math.ceil(source[j]['Division'].length / $scope.break);
var data = new Array(total);
for (var i = 0; i < total; ++i) {
data[i] = source[j]['Division'].slice(i * $scope.break, (i + 1) * $scope.break);
}
$scope.final_data[j] = data;
}
}
}
$scope.$watch('div.data.floors', processData, true)
},

Related

AngularJS - Filtering out values on drop down list

I've been working on my drop down list, and while I know the HTML is working fine, I can't seem to be getting the values to populate within the list correctly. I can't even seem to get into the for loop in the provided code, am I missing something?
AngularJS:
$scope.vendorUserList = [];
SpringDataRestService.get(
{
"collection": "user"
},
function (response) {
var users = response;
for (var i = 0, len = users.length; i < len; i++) {
if (users[i].type === "VENDOR") {
if (users[i].status !== "PENDING_DEACTIVATION") {
var newUser = {id: users[i].id, name: users[i].name};
$scope.vendorUserList.push(newUser);
}
}
}
},
}
);
JSON:
http://localhost:8080/api/users
{
"status": "ACTIVE",
"userName": "CLIENT 2"
}
I used AngularJS $http.get to first return the entire list of users and then I filtered out the users living in the city of Wisokyburgh that also have a zip-code, using your code (slightly modified).
var root = 'https://jsonplaceholder.typicode.com';
// Create an Angular module named "usersApp"
var app = angular.module("usersApp", []);
// Create controller for the "usersApp" module
app.controller("usersCtrl", ["$scope", "$http", function($scope, $http) {
$scope.vendorUserList = [];
var url = root + "/users"
$http.get(url)
.then(function(data) {
var users = data.data;
console.log(users);
for (var i = 0; i < users.length; i++) {
if (users[i].address.city === "Wisokyburgh") {
if (users[i].address.zipcode !== "") {
var newUser = {
id: users[i].id,
name: users[i].name
};
$scope.vendorUserList.push(newUser);
}
}
}
console.log($scope.vendorUserList);
});
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.2/angular.min.js"></script>
<div class="container" data-ng-app="usersApp">
<div data-ng-controller="usersCtrl">
<select>
<option value="0">Choose from the list</option>
<option ng-repeat="user in vendorUserList" value="user.id">{{user.name}}</option>
</select>
</div>
</div>
The case is similar to yours, but the url is public (it needs to be, so that the snippet works). Hope it helps.

how to bind data of api into ng-multiselect-dropdown?

I want to bind data into a multi-select dropdown, how it is possible?
HTML code:
<div class="form-group">
<!--
<select class="form-control custom-selec" formControlName='stoppage_point'>
<option *ngFor="let cities of city.data" [(ngValue)]="cities.city">{{cities.city}}</option>
</select>
-->
<ng-multiselect-dropdown
name="city"
[placeholder]="'Select City'"
[data]="city.data"
formControlName="city"
[disabled]="disabled"
[settings]="dropdownSettings"
(onSelect)="onItemSelect($event)">
</ng-multiselect-dropdown>
</div>
.ts file code:
this.http.get(environment.api_url + `/admin/get_only_city/`
).subscribe((data:any[])=> {
console.log(data);
this.city = data;
var i;
for(i=0;i<=8;i++){
console.log(this.city.data[i]['_id']) ;
}
this.dropdownSettings = {
singleSelection: false,
selectAllText: 'Select All',
unSelectAllText: 'UnSelect All',
itemsShowLimit:5,
allowSearchFilter: true
};
});
I want to display all records in a dropdown, but it's not displaying values.
I don't how to bind a particular value in the data, so please can someone help me to solve this problem?
Please check my example Stackblitz
You call a webservice and fetch the result data into an array. And this array you push into your dropdownlist.
getData(): void {
let tmp = [];
this.http.get<any>('https://jsonplaceholder.typicode.com/users').subscribe(data => {
for(let i=0; i < data.length; i++) {
tmp.push({ item_id: i, item_text: data[i].name });
}
this.dropdownList = tmp;
});
}
Put return in this.dropdownList
getData(): void {
let tmp = [];
this.http.get<any>('https://jsonplaceholder.typicode.com/users').subscribe(data => {
for(let i=0; i < data.length; i++) {
tmp.push({ item_id: i, item_text: data[i].name });
}
return this.dropdownList = tmp;
});
}
console.log(this.dropdownList);

the ko viewmodel is updated on js but not displaying in html view

I'm just starting to use knockout js and after lots tets, I can't solve this : the js viewmodel is updated by my function js, but no visual variation on the browser is displayed. This only happens to me for this model called TableViewModel, for the other models, the addition of other elements works correctly on view.
in the past I tried to refer at the child elements of this view model, but I don't have the necessary information to modify the table interested (the table is the father on the elements that are viewModelGroups).
I have search on https://knockoutjs.com/documentation/ and few forum but I did not solve this problem.
This is my short function on js file (into the tableViewModel) :
TableViewModel = (function() {
function TableViewModel(data) {
var g, group;
this.id = data.id;
//...
//...
group = (function() {
var j, len, ref, results;
ref = data.Group;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
g = ref[j];
results.push(new GroupViewModel(g));
}
return results;
})();
this.Group = group;
return;
}
TableViewModel.prototype.newObject = function() {
var self = this;
var ggrrpp = new GroupViewModel(self.Group[0]);
this.Group.push(ggrrpp);
};
TableViewModel.prototype.toJS = function() {
var group;
return {
id: this.id,
//...
//...
Group: (function() {
var j, len, ref, results;
ref = this.Group;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
group = ref[j];
results.push(group.toJS());
}
return results;
}).call(this)
};
};
return TableViewModel;
})();
and this is my html(aspx)part used to call the function above :
<div class="row" data-bind="with: Table">
...
...
<i class="config-button fa fa-plus" title="Adding object" aria-hidden="true"
data-bind="click: function() { newObject(); }"> </i>
<div data-bind="foreach: Group">
...
...
in the newObject function, the push works and add the object in the group list (each time it is called it contains the previous addition)...but the view does not change.
Thanks in advance for the answers.
If you want the DOM to be updated when a new item is added to Group, you need to convert it to an observableArray
this.Group = ko.observableArray(group);
Also, inside newObject, you can't use bracket notation directly on observableArray. You need to get the value using the parenthesis first and then use the indexer:
var ggrrpp = new GroupViewModel(self.Group()[0])

DOM element in mdTab not updated with angular .css method

I got progress bar in my table where I show how many hours has been spend on project. When I click on some tab, currentTab method is fired to get data from database and after I promise is resolved my TimeService function is fired also to calculate hours for progress bar, but when angular .css() is reached it doesnt update my progress bar at all and I dont know why since it definitely 100% working code. Is there some feature in mdTabs which prevent this?
HTML code below, I deleted plenty of stuff to make it more readable
<md-tab label="Testing" md-on-select="currentTab('Testing')">
<md-content class="md-padding">
<table id="projects-active" class="table table-bordered table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Description</th>
<th>Estimated time</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="project in projects.Data">
<td>{{project.Id}}</td>
<td>{{project.Name}}</td>
<td>{{project.Description}}</td>
<td>
<div class="progress-group">
<div class="progress sm">
<div class="progress-bar progress-bar-success progress-bar-striped" id="progress-bar-pId-{{project.Id}}">
</div>
</div>
</div>
</td>
<td>{{project.Status}}</td>
</tr>
</tbody>
</table>
<div class="box-footer clearfix">
<ul uib-pagination total-items="totalItems"
items-per-page="maxSize"
ng-model="currentPage"
max-size="maxSize"
class="pagination-sm"
boundary-links="true"
num-pages="pages"
ng-change="setPage(currentPage, 'Testing')"></ul>
</div>
</md-content>
Here is my function in TimeService modul
projectProgressArray: function (array) {
var items = array.Data;
angular.forEach(items, function (key, value) {
var id = key.Id;
var maxTime = key.EstimatedTime;
var currentTime = key.TimeSpend;
var percentageComplete = Math.round(((currentTime / maxTime) * PERCENTAGE) * 100) / 100;
if (!("#progress-bar-pId-" + id).length == 0) {
angular.element("#progress-bar-pId-" + id).css("width", percentageComplete + "%");
if (percentageComplete > 100) {
angular.element("#progress-bar-pId-" + id).removeClass("progress-bar-success");
angular.element("#progress-bar-pId-" + id).addClass("progress-bar-danger");
}
}
});
Here is my controller function where I fetch data to my data tables
var MAX_SIZE_PER_PAGE = 5;
$scope.currentPage = 1;
$scope.maxSize = MAX_SIZE_PER_PAGE;
$scope.pages = 0;
$scope.totalItems = 0;
$scope.setPage = function (pageNo, status) {
$scope.currentPage = pageNo;
$scope.projects = ProjectService.queryPaged({ pageSize: $scope.maxSize, pageNumber: $scope.currentPage, status: status });
$scope.projects.$promise.then(function (data) {
setTimeout(function () {
TimeService.projectProgressArray($scope.projects);
}, 0);
});
};
$scope.currentTab = function (status) {
$scope.projects = ProjectService.queryPaged({ pageSize: $scope.maxSize, pageNumber: 1, status: status });
$scope.projects.$promise.then(function (data) {
$scope.totalItems = data.TotalCount;
setTimeout(function () {
TimeService.projectProgressArray($scope.projects);
}, 0);
});
}
UPDATE: I added image where i copy progress bars outside of md-tab to show its working outside of md-tabs but not inside it.
So after some time of I found out that I cannot manipulate with css in Angular material using angular.element().css() method. I had to edit my TimeService method where I created array filled with information about single progress bar per project and send it back to my controller and use ng-style
to manipulate with my css inside ng-material DOM
projectProgressArray: function (array) {
var progressBarArray = [];
var items = array.Data;
angular.forEach(items, function (key, value) {
var progressBar = {
projectId: null,
percentage: null,
barClass: null
}
var id = key.Id;
var maxTime = key.EstimatedTime;
var currentTime = key.TimeSpend;
var percentageComplete = Math.round(((currentTime / maxTime) * PERCENTAGE) * 100) / 100;
progressBar.projectId = id;
progressBar.percentage = percentageComplete + "%";
if (!("#progress-bar-pId-" + id).length == 0) {
if (percentageComplete > 100) {
progressBar.barClass = "progress-bar-danger";
}
else {
progressBar.barClass = "progress-bar-success";
}
}
progressBarArray.push(progressBar);
});
return progressBarArray;
Reworked HTML binding
<div class="progress-group">
<div class="progress sm" ng-repeat="item in progressBars" ng-hide="item.projectId != project.Id">
<div ng-class="{'progress-bar-success': item.barClass == 'progress-bar-success',
'progress-bar-danger': item.barClass == 'progress-bar-danger' }"
class="progress-bar progress-bar-success progress-bar-striped" id="progress-bar-pId-{{project.Id}}"
ng-style="{'width':item.percentage}">
</div>
</div>

Polymer - reload core-list data

I wanted reload a core-list element to show new data, but it´s not refreshing.
I re-call the JS function thats generate the data but doesn t work... and reload like a 'normal' div doesn t work either! The list only shows the new data if i reload the entire page...
function values(sender, textomsg, datacriacao, senderfoto){
var sender2 = sender.split(",");
var textomsg2 = textomsg.split(",");
var datacriacao2 = datacriacao.split(",");
var senderfoto2 = senderfoto.split(",");
var namegen = {
generateString: function (inLength) {
var s = '';
for (var i = 0; i < inLength; i++) {
s += String.fromCharCode(Math.floor(Math.random() * 26) + 97);
}
return s;
},
generateName: function (inMin, inMax) {
return this.generateString(Math.floor(Math.random() * (inMax - inMin + 1) + inMin));
}
};
Polymer('list-test', {
count: sender.length,
ready: function () {
this.data = this.generateData();
},
generateData: function () {
var names = [], data = [];
for (var i = 0; i < this.count; i++) {
names.push(namegen.generateName(4, 8));
}
names.sort();
for (var i = 0; i < this.count; i++) {
data.push({
index: i,
sender: sender2[i],
textomsg: textomsg2[i],
datacriacao: datacriacao2[i],
senderfoto: senderfoto2[i]
});
}
return data;
},
tapAction: function (e) {
console.log('tap', e);
}
});
}
<%----%>
<template id="templateConversas" runat="server">
<div id="item" class="item {{ {selected: selected} | tokenList }}" ><%--onClick="conversa('{{name}}');"--%>
<div class="message" style="background-image: url({{senderfoto}});">
<span class="from"><br/>{{sender}}</span>
<span class="timestamp">{{datacriacao}}</span>
<div class="subject"><br/>{{textomsg}} </div><%--------Infinite List. {{index}}--%>
<%--<div class="body"><br/>Mensagem de teste...........</div>--%>
</div>
</div>
</template>
The problem is also reload the 'list-test'. if i call the js function after the list is loaded it doesn't apply the new data...
Your code isn't complete so it is hard to understand but I think that the problem is that you don't assign the result of the generateData() function to the template's model. Try following script for your component
Polymer('list-test', {
created: function () {
this.data = [];
},
refresh: function () {
this.data = this.generateData();
},
generateData: function () {
// your original code here
}
});
Now the list content should be updated with newly generated data when you call refresh() of the list-test element. To fill the list when element is created add
ready: function () {
this.refresh();
},