I'm getting this annoying bug with Angular where I broadcast data to a directive but the directive's $on doesn't receive it.
Therefore, my table doesn't populate at all and looks terrible to users.
test_results.html (contains an instance of the directive):
<div>
<h1>Test Results</h1>
...
<results></results>
</div>
resultsCtrl.js controller:
$timeout(function () {
$rootScope.$broadcast('show-results', test_session.question_objects);
}, 100);
results.html directive template (most fields stripped out):
<div class="results">
<table class="table table-bordered table-striped">
<thead>
<tr>
<td ng-if="average_times && !$root.is_mobile">
<span ng-click="sortType = 'question.average_timing'; sortReverse = !sortReverse">
Avg. Time
</span>
</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="q in questions | orderBy:sortType:sortReverse | filter:searchQuestions track by $index">
<td ng-if="q.average_timing && !$root.is_mobile" ng-click="$root.openDirections('question', q)">{{ q.average_timing }}</td>
</tr>
</tbody>
</table>
</div>
results.js directive:
scope.$on('show-results', function(event, test_session) {
setTestSessionData(test_session);
});
...
function setTestSessionData (test_session) {
scope.questions = test_session;
}
I cannot figure out when exactly this happens. At first I thought it's when I load the site for the first time, but I've tried that since and the data is rendered.
Whenever you do a $rootScope.$broadcast all the child scopes who have registered to those events via $scope.$on will catch the event. Same goes with directive scope also. If you update the model in the event listener , it will be updated in the view.
Working plunker : https://plnkr.co/edit/4iokGsfPH5IqNdecjaGd?p=preview
Related
<table class="table table-bordered">
<tbody>
<tr ng-repeat="playerOrTeam in template.editableTable track by $index">
<td style="text-align: center;" ng-repeat="playerOrTeamCat in playerOrTeam track by $index">
<input ng-model="playerOrTeamCat" type="text" class="form-control input-sm">
</td>
</tr>
</tbody>
</table>
template.editableTable is a multi dimensional array just filled with standard variables. when I change one of the values in the input box and then i look at the output of the template.editable table, i don't see the changes. Am I missing something obvious?
EDIT with more details because i'm getting no responses =\
//Template Class
app.factory('Template', function () {
var Template = function () {
/*
* Public Variables
*/
this.editableTable = someMultiDimensionalTable;
}
/*
* Functions
*/
Template.prototype.SeeEditableTableContents = function () {
console.log(this.editableTable);
}
}
//Main Controller
app.controller('MainController', function () {
$scope.template = new Template();
//etc
}
You cannot perform direct in-line modifications within ng-repeat. You can update your array entry using a function.
You'd want something like:
$scope.saveEntry = function (idx) {
console.log("Saving entry");
$scope.template.editableTable[idx] = angular.copy($scope.model.selected);
$scope.reset();
};
See JSFiddle for sample.
okay i actually got it to work so that i CAN make direct in-line modifications with ng-repeat by making my multidimensional table full of objects rather than soley a value.
so by doing that, i modified the table to look like this
<table class="table table-bordered">
<tbody>
<tr ng-repeat="playerOrTeam in template.editableTable track by $index">
<td style="text-align: center;" ng-repeat="playerOrTeamCat in playerOrTeam track by $index">
<input ng-model="playerOrTeamCat.value" type="text" class="form-control input-sm">
</td>
</tr>
</tbody>
</table>
got the idea looking here
Modifying objects within Angular Scope inside ng-repeat
I am trying to get some data into a tab. My application uses ng-route, so here is my config code:
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/EventList',{
templateUrl:'html/eventsList.html',
controller: 'eventListController',
controllerAs:'eventListCrtl'
})
.otherwise({redirectTo:'/'});
}]);
I define here the controller and the name the controller will have inside my template with controllerAs
Here is my controller code:
app.controller('eventListController',['$scope', 'EventListHolder', function($scope, EventListHolder){
var eventList = EventListHolder.get();
window.alert(eventList.eventHead[1].numeroDossier);
}]);
It retrieves the data from a factory. The data is loaded into the controller since the window.alert works.
But for some reson it doesnt see to show up in my template, where I am trying to display the eventHead table in an HTML table.
Here is the HTML:
<div align="center">
<div class="container-fluid">
<header>
<h1>Event List</h1>
</header>
</div>
<table class="row-spacer40 table-fill" >
<thead>
<tr>
<th class="text-left">Numero</th>
<th class="text-left">Dossier/Client</th>
<th class="text-left">Lieu</th>
</tr>
</thead>
<tr ng-repeat="event in eventListCrtl.eventList.eventHead">
<td> {{event.numeroDossier}}
</td>
<td>{{event.designationDossier}}<br>{{event.nomClient}}
</td>
<td>{{event.adresse}}
</td>
</tr>
</table>
</div>
Is there something specific to ng-route that I am overlooking ?
PS: If it helps, here is the JSON containing the data. (If you want a clear definition of my data structure.)
{
"eventHead": [
{
"numeroDossier": 96549,
"designationDossier": "TECHNIQUE MARIAGE ",
"nomClient": "Olivia John & Donovan Hamlet",
"adresse": "ABBAYE DES VAUX DE CERNAY"
},
{
"numeroDossier": 98986,
"designationDossier": "TOURNEE FDJ OLA",
"nomClient": "LA FRANCAISE DES JEUX",
"adresse": "MAGNUM"
}
],
"success": true
}
You should be using
this.eventList = EventListHolder.get();
instead of var eventList. ControllerAs syntax expects your view models to be binded to the 'this' object instead of $scope.
First of all, sorry about the title, I just wan't sure how to word this question.
So I'm making a task manager using AngularJS. I have a form for the user to fill with the details when he's creating a new task. I use ng-model to save these values to my $scope. Here's how I save them:
$scope.add = function(tasks)
{
{
$scope.tasks.push({
'id': tasks.id,
'title': tasks.title,
'start_day': tasks.start_day,
'start_time':tasks.start_time,
'start_date':tasks.start_day + " " + tasks.start_time,
'end_day': tasks.end_day,
'end_time': tasks.end_time,
'end_date':tasks.end_day + " " + tasks.end_time,
'type': tasks.type,
'description': tasks.description
});
localStorage.setItem('tasks',JSON.stringify($scope.tasks));
}
};
Then, as you can see, I save these values to the local storage. I have an array called tasks with all the tasks the user created. I then display these tasks in a table with this:
<table id="datatable" class="display" ng-cloak>
<thead>
<tr>
<th><b>ID</b></th>
<th><b>Title</b></th>
<th><b>Start Date</b></th>
<th><b>End Date</b></th>
<th><b>Type</b></th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="task in tasks track by $index">
<td ng-click="details(task)">{{task.id}}</td>
<td ng-click="details(task)">{{task.title}}</td>
<td ng-click="details(task)">{{task.start_date}}</td>
<td ng-click="details(task)">{{task.end_date}}</td>
<td ng-click="details(task)">{{task.type}}</td>
<td><a ng-click="remove(task)" class="btn-floating waves-effect waves-light red<i class="material-icons">clear</i></a></td>
</tr>
</tbody>
</table>
Then, the objective is that you can click any row and load a new page with all the task details. I do this with the function details(), which has the following code:
$scope.details = function (task)
{
$window.location.href = 'table.html';
}
This loads the file table.html and what I want is to display in this page all the task details, i.e., the ID, the title, the description, etc.
What I don't know is how to only display the specific task that you click on. For example, if I click on the row with the task "Todo #1", I only want to see the details for the task "Todo #1".
to access this variable in other html you can use factory or service like this
(function() {
"use strict";
angular.module('dataModule',[])
.factory('datafactory',function(){
return {
};
});
})();
Now datafactory is factory we need to inject this module(dataModule) in your module and factory(datafactory) in controller
Now how to use this factory
in your function
$scope.details = function (task)
{
datafactory.currentTask =task
$window.location.href = 'table.html';
}
Now this datafactory stores your variable that can we used in any controller and later on also you can use this factory to store any such variable for global use
like this datafactory.Myvariable ="hasd"//assign here
Now to use this variable
Suppose you want to use this variable in another page table.html there on
// in html ng-init ="$scopeInit()"
in controller
$scopeInit =function(){
$scope.localTask =datafactory.currentTask
}
and use $scope.localTask
suppose html looks something like this
<div ng-controller ="my-controller" ng-init ="$scopeInit()">
<table id="datatable" class="display" ng-cloak>
<thead>
<tr>
<th><b>ID</b></th>
<th><b>Title</b></th>
<th><b>Start Date</b></th>
<th><b>End Date</b></th>
<th><b>Type</b></th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="task in tasks track by $index">
<td ng-click="details(task)">{{task.id}}</td>
<td ng-click="details(task)">{{task.title}}</td>
<td ng-click="details(task)">{{task.start_date}}</td>
<td ng-click="details(task)">{{task.end_date}}</td>
<td ng-click="details(task)">{{task.type}}</td>
<td><a ng-click="remove(task)" class="btn-floating waves-effect waves-light red<i class="material-icons">clear</i></a></td>
</tr>
</tbody>
</table>
<div>
//in controller
$scopeInit =function(){
$scope.task =datafactory.currentTask
}
//$scope.task contains required array and hence table can be created
Caveat: I've just started with client side scripting and Angular JS is the first thing I'm learning and now I feel I should've started with javascript.
PS: I don't wanna use any third party libraries. I wanna learn to code.
Anyway,I have dynamic table which I want to make editable using content-editable=true attribute of HTML.
Problem: How to I get the edited data? whenever I click on submit and pass the this object to the check() function. I doesn't contain edited values. is there a possible way to pass only edited value if it's dirty. It has pagination so If g to the next page the edited values are gone. I know I've give unique Id to every td element with $Index concatenated to it. But I don't know how should I proceed.
Any help or guidance will be appreciated. Controllers and others are defined in my route.
<div>
<form ng-submit="check(this)">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td contenteditable="true >{{data.EmpNo}}</td>
<td contenteditable="true">{{data.isActive}}</td>
<td contenteditable="true">{{data.balance}}</td>
<td contenteditable="true">{{data.age}}</td>
<td contenteditable="true">{{data.eyeColor}}</td>
<td contenteditable="true">{{data.fname}}</td>
</tr>
</tbody>
<tfoot>
<td>
<div class="pagination pull-right">
<li ng-class="{'disabled': previousPage}">
<a ng-click="previousPage()" >Previous</a>
</li>
<li ng-repeat="page in pageLengthArray track by $index">
<a ng-click="pagination($index)">{{$index+1}} </a>
</li>
<li disabled="disabled">
<a ng-click="nextPage()" ng-class="{'disabled':nextPage}>Next </a>
</li>
</div>
</td>
</tfoot>
</table>
<input type="submit" value="Submit">
</form>
$scope.currentPage=0;
$scope.pageSize=10;
$scope.currentItems;
$scope.tableData;
$http.get('../json/generated.json').then(function(response){
$scope.tableData=response.data;
$scope.pageLength=Math.ceil($scope.tableData.length/$scope.pageSize);
$scope.currentItems=$scope.tableData.slice($scope.currentPage,$scope.pageSize);
$scope.pageLengthArray= new Array($scope.pageLength);
});
$scope.pagination=function(currentPage){ $scope.currentItems=$scope.tableData.slice($scope.pageSize*currentPage,$scope.pageSize*currentPage+$scope.pageSize);
$scope.currentPage=currentPage;
}
$scope.nextPage=function nextPage(argument) {
$scope.currentPage++; $scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
$scope.previousPage=function previousPage(argument) {
$scope.currentPage--;
$scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
In the usual case, you can not get a change model for contenteditabe because to change the model used ngModel.
But we can create a directive that we have updated the value of the model.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope, $timeout) {
$scope.data = {
EmpNo: "123"
};
})
.directive('contenteditable', function($timeout) {
return {
restrict: "A",
priority: 1000,
scope: {
ngModel: "="
},
link: function(scope, element) {
element.html(scope.ngModel);
element.on('focus blur keyup paste input', function() {
scope.ngModel = element.text();
scope.$apply();
return element;
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<table>
<tr>
<td ng-model="data.EmpNo" contenteditable="true"></td>
</tr>
</table>
<pre>{{data|json}}</pre>
</div>
</div>
I would store any object that gets modified in a seperate array using the ng-keyup directive. When the form is submitted, you will have an array of only elements which have been modified. You may have some UX issues if your pagination is done by server as when you change page and come back, it will show your old data, but hopefully this helps.
$scope.check = function () {
// check modifiedItems
console.log(modifiedItems);
};
// store modified objects in a seperate array
var modifiedItems = [];
$scope.modifyItem = function (data) {
// check if data has already been modified and splice it first
for(var i = 0, j = modifiedItems.length; i < j; i++) {
var currentItem = modifiedItems[i];
if (currentItem.id === data.id) {
modifiedItems.splice(i, 1);
break;
}
}
// add to modified
modifiedItems.push(data);
console.log('modifiedItems: ', modifiedItems);
};
HTML
<form ng-submit="check()">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td ng-repeat="(key, value) in data" contenteditable="true"
ng-keyup="modifyItem(data)">
{{data[key]}}
</td>
</tr>
</tbody>
<tfoot>
</table>
<input type="submit" value="Submit">
</form>
I'm trying to build a template for a application and want to display a dynamic list with names. so i got this code to show the list and add/remove rows;
<table ng-init="page.businessRows = []">
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Phone</th>
</tr>
</thead>
<tr ng-repeat="row in page.businessRows">
<td>
<input type="text" ng-model="row.name" />
</td>
<td>
<input type="text" ng-model="row.contact" />
</td>
<td>
<input type="text" ng-model="row.phone" />
</td>
<td>
<button ng-click="page.businessRows.splice($index,1)">
Remove
</button>
</td>
</tr>
</table>
<button class="btn" ng-click="page.businessRows.push({})">addRow</button>
the thing as that when this template is loaded page.busnessRows will most likely be loaded with rows so i want to change the ng-init to only create the empty array if businessRows is not initialised.
I have tried ng-init="page.businessRows = page.businessRows.length < 1 ? [] : page.businessRows but it did not work. How am i inteded to do conditions in jsangular expressions?
All help appreciated. Thanks in advance
You can do this instead:
<table ng-init="page.businessRows = page.businessRows || []">
Update
I look at the parser code of AngularJS and notice that version 1.2 (currently RC) supports ternary expression. So if you use AngularJS 1.2, this will also work (although more verbose than the above code):
<table ng-init="page.businessRows = page.businessRows == null ? [] : page.businessRows">
See demo here.
However, your original code might not work if page.businessRows is null, because the parser will fail to dereference length property of null. So just be careful there.
I don't think the ng-init will evaluate conditional statements properly. But you could refactor the condition into a controller function and call the function from ng-init.
<table ng-init="initializeBusinessRows(page.businessRows)">
The just put your conditional evaluation in the function on the controller scope.
I think you're trying to solve the wrong problem.
The problem is that you're allowing an action to occur before the data is loaded or ready. A secondary problem is you're using an expression in an ng-click where a scope function or controller function should be.
So...
Disable that button if the form isn't ready.
Use your controller to control these interactions.
So here's an example of the controller. The $timeout was added to simulate a delayed load of data into your $scope.page variable.
app.controller('MyCtrl', function($scope, $timeout, $window) {
//Timeout to simulate the asynchronous load
//of the page object on the $scope
$timeout(function(){
$scope.page = {
businessRows: []
};
}, 2000);
//scope method to add a row.
$scope.addRow = function (){
//for safety's sake, check to see if the businessRows array is there.
if($scope.page && angular.isArray($scope.page.businessRows)) {
$scope.page.businessRows.push({});
}
};
//scope method to remove a row
$scope.removeRow = function(index, row) {
if($window.confirm('Are you sure you want to delete this row?')) {
$scope.page.businessRows.splice(index, 1);
}
};
});
... and the HTML view (notice the ng-disabled and the ng-click) (and lack of ng-init):
<div ng-controller="MyCtrl">
<table>
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in page.businessRows">
<td>
<input type="text" ng-model="row.name" />
</td>
<td>
<input type="text" ng-model="row.contact" />
</td>
<td>
<input type="text" ng-model="row.phone" />
</td>
<td>
<button ng-click="removeRow($index, row)">
Remove
</button>
</td>
</tr>
</tbody>
</table>
<button class="btn" ng-disabled="!page" ng-click="addRow()">addRow</button>
</div>
Also, here's the obligatory Plunker for you to see this in action.