Cannot read data "objectname" of undefined angular directive - javascript

Unable to read the data from parent scope to directive. Getting error like
TypeError: Cannot read property 'rowCollection' of undefined
Can you please help me out of this.
HTML
<div ng-controller="ctrl1 as one">
<ltcg-table options="one.rowCollection"></ltcg-table>
</div>
Grid HTML
<table st-table="rowCollection" class="table table-striped">
<thead>
<tr>
<th>first name</th>
<th>last name</th>
<th>birth date</th>
<th>balance</th>
<th>email</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rowCollection">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
</tbody>
</table>
Javascript Controller
(function () {
var myApp = angular.module('myApp', ['smart-table']);
function one() {
this.song="Murali";
// alert("gg");
this.rowCollection = [
{firstName: 'Laurent', lastName: 'Renard', birthDate: new Date('1987-05-21'), balance: 102, email: 'whatever#gmail.com'},
{firstName: 'Blandine', lastName: 'Faivre', birthDate: new Date('1987-04-25'), balance: -2323.22, email: 'oufblandou#gmail.com'},
{firstName: 'Francoise', lastName: 'Frere', birthDate: new Date('1955-08-27'), balance: 42343, email: 'raymondef#gmail.com'}
];
//alert($scope.gridOptions.columnDefs[1].name);
//alert($scope.gridOptions);
};
myApp.directive('ltcgTable', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'options': '='
},
templateUrl: "ltcg-table.html",
link: function(scope, element, attr) {
alert(scope.$parent.options.rowCollection);
scope.rowCollection = scope.options.rowCollection;
}
}
});
myApp.controller('ctrl1', one)
})();

So, you have a directive with isolated scope. In this case scope parameter in link function referes to this scope, in your case this next object
{
'options': '='
}
So when you do in html options="one.rowCollection" value of one.rowCollection was binded to options property, so for access to it you should use scope.options in link function, on just options in view.
also $parent property set to parent scope, in your case - "ctrl1" controller scope. So you can directly go to controller and get what you want.
When use controller as syntax reference to controller saved in controller scope. So for access controller you should use it name.
Sample:
var myApp = angular.module('myApp', []);
function one() {
this.song = "Murali";
// alert("gg");
this.rowCollection = [{
firstName: 'Laurent',
lastName: 'Renard',
birthDate: new Date('1987-05-21'),
balance: 102,
email: 'whatever#gmail.com'
}, {
firstName: 'Blandine',
lastName: 'Faivre',
birthDate: new Date('1987-04-25'),
balance: -2323.22,
email: 'oufblandou#gmail.com'
}, {
firstName: 'Francoise',
lastName: 'Frere',
birthDate: new Date('1955-08-27'),
balance: 42343,
email: 'raymondef#gmail.com'
}];
//alert($scope.gridOptions.columnDefs[1].name);
//alert($scope.gridOptions);
};
myApp.directive('ltcgTable', function() {
return {
restrict: 'E',
transclude: true,
scope: {
'options': '='
},
templateUrl: "ltcg-table.html",
link: function(scope, element, attr) {
//go to controller directly
scope.rowCollection = scope.$parent.one.rowCollection
}
}
});
myApp.controller('ctrl1', one)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="ctrl1 as one">
<ltcg-table options="one.rowCollection"></ltcg-table>
</div>
<script id="ltcg-table.html" type="text/ng-template">
<table st-table="rowCollection" class="table table-striped">
<thead>
<tr>
<th>first name</th>
<th>last name</th>
<th>birth date</th>
<th>balance</th>
<th>email</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="5">
Get data from scope.options
</td>
</tr>
<tr ng-repeat="row in options">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
<tr>
<td colspan="5">
<hr/>
</td>
</tr>
<tr>
<td colspan="5">
Get data saved from controller directly in link function
</td>
</tr>
<tr ng-repeat="row in rowCollection">
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
<td>{{row.birthDate}}</td>
<td>{{row.balance}}</td>
<td>{{row.email}}</td>
</tr>
</tbody>
</table>
</script>
</div>

You added options to your directive scope, so you can directly access it throughscope.options. By the way, directives scopes are isolated (with the scope: {} notation), so you can't just go up and try to read parent scopes.

Related

Knockout JS Cannot bind to an array

I am trying to bind array to a table, so it shows all my array contents.
I tried first example, which works (purely in HTML):
<table>
<thead>
<tr><th>First name</th><th>Last name</th></tr>
</thead>
<tbody data-bind="foreach: people">
<tr>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
function Model() {
this.people = [
{ firstName: 'Bert', lastName: 'Bertington' },
{ firstName: 'Charles', lastName: 'Charlesforth' },
{ firstName: 'Denise', lastName: 'Dentiste' }
];
}
ko.applyBindings(new Model());
</script>
Then I got to the next level, and tried bigger example, which always shows error
Unable to process binding "foreach: function(){return interests }"
Message: Anonymous template defined, but no template content was provided
Below is faulty code:
// Activates knockout.js when document is loaded.
window.onload = (event) => {
ko.applyBindings(new AppViewModel());
}
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
this.fullName = ko.computed(() => this.firstName() + " " + this.lastName(), this);
this.interests = ko.observableArray([
{ name: "sport" },
{ name: "games" },
{ name: "books" },
{ name: "movies" }
]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody data-bind="foreach: interests"></tbody>
<tr>
<td data-bind="text: name"></td>
</tr>
</table>
I tired already with regular array, but with no luck.
You are closing the <tbody> before the inner template:
<tr>
<td data-bind="text: name"></td>
</tr>
So, the tr is now not in the context of the foreach binding.
Move the </tbody> to after the </tr> and before the </table> tags:
// Activates knockout.js when document is loaded.
window.onload = (event) => {
ko.applyBindings(new AppViewModel());
}
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
this.firstName = ko.observable("Bert");
this.lastName = ko.observable("Bertington");
this.fullName = ko.computed(() => this.firstName() + " " + this.lastName(), this);
this.interests = ko.observableArray([
{ name: "sport" },
{ name: "games" },
{ name: "books" },
{ name: "movies" }
]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody data-bind="foreach: interests">
<tr>
<td data-bind="text: name"></td>
</tr>
</tbody> <!-- close here -->
</table>
Change your HTML to
<table>
<thead>
<tr>
<th>Interest</th>
</tr>
</thead>
<tbody>
<tr data-bind="foreach: interests">
<td data-bind="text: name"></td>
</tr></tbody>
</table>

Return Distinct Rows of Data using KnockoutJS

I have a model which contains relationship between Tag and Task. So many TaskIDs can relate to many TagIDs
However, I want to display unique TagIDs and TagNames in a table. So instead of the duplicate rows in the JSFiddle example, it should return distinct rows i.e. only 2 rows in the table.
The second table I have works fine as I am returning just one column.
Below is my code...
var viewModel = function(data) {
var self = this;
self.tagTaskMappings = ko.observableArray([
{TagID: 2, TagName: "A", TaskID: 1, TaskName: "ManualItems"},
{TagID: 2, TagName: "A", TaskID: 2, TaskName: "Trades"},
{TagID: 3, TagName: "B", TaskID: 1, TaskName: "ManualItems"},
{TagID: 3, TagName: "B", TaskID: 2, TaskName: "Trades"},
{TagID: 3, TagName: "B", TaskID: 3, TaskName: "Cash"},
{TagID: 3, TagName: "B", TaskID: 4, TaskName: "ReportA"}
]);
self.filteredtagMappings = ko.computed(function () {
var types = ko.utils.arrayMap(self.tagTaskMappings(), function (item) {
return { TagID: item.TagID, TagName: item.TagName, IsTagActive: item.IsTagActive};
});
return ko.utils.arrayGetDistinctValues(types).sort();
}, this);
self.filteredtagMappings2 = ko.computed(function () {
var types = ko.utils.arrayMap(self.tagTaskMappings(), function (item) {
return item.TagName;
});
return ko.utils.arrayGetDistinctValues(types).sort();
}, this);
};
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>
<table class="table table-hover">
<thead>
<tr>
<th>Tag ID</th>
<th>Tag name</th>
<th>Task ID</th>
<th>Task name</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: filteredtagMappings -->
<tr>
<td class="ui-state-default" data-bind="text: TagID"></td>
<td class="ui-state-default" data-bind="text: TagName"></td>
<td></td>
</tr>
<!-- /ko -->
</tbody>
</table>
<hr />
<table class="table table-hover">
<thead>
<tr>
<th>Tag ID</th>
<th>Tag name</th>
<th>Task ID</th>
<th>Task name</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: filteredtagMappings2 -->
<tr>
<td></td>
<td class="ui-state-default" data-bind="text: $data"></td>
<td></td>
</tr>
<!-- /ko -->
</tbody>
</table>
http://jsfiddle.net/4qc570eo/2/
here is a solution using underscorejs and the knockout mapping pluggin.
http://jsfiddle.net/4qc570eo/6/
self.filteredtagMappings2 = function() {
var returnObject = ko.observableArray('');
var uniques = _.map(_.groupBy(ko.toJS(self.tagTaskMappings), function(item) {
return item.TagID;
}), function(grouped) {
return grouped[0];
});
ko.mapping.fromJS(uniques, {}, returnObject);
return returnObject;
};
};

AngularJS search using filter not working

I am trying to search using angularjs filter method but its not working. I am not getting any errors in the console but still the search result is not showing up. Can someone help me on this.
This is my controller.js code:
.controller('SearchController',
[
'$scope',
'dataService',
'$location',
'$routeParams',
function ($scope, dataService, $location, $routeParams){
$scope.searchMovies = [ ];
$scope.searchCount = 0;
var getSearchResult = function (terms) {
dataService.getSearchResult(terms).then(
function (response) {
$scope.searchCount = response.rowCount + ' movies';
$scope.searchMovies = response.data;
$scope.showSuccessMessage = true;
$scope.successMessage = "All movie Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
if ($routeParams && $routeParams.term) {
console.log($routeParams.term);
getSearchResult($routeParams.term);
}
}
]
);
This is the services.js code:
this.getSearchResult = function (terms) {
var defer = $q.defer(),
data = {
action: 'search',
term: terms
}
$http.get(urlBase, {params: data, cache: true}).
success(function(response){
defer.resolve({
data: response.ResultSet.Result,
rowCount: response.RowCount
});
}).
error(function(err){
defer.reject(err);
});
return defer.promise;
};
This is my app.js code:
. when('/search', {
templateUrl : 'js/partials/search.html',
controller : 'SearchController'
}).
This is the search.html code:
<div>
<label>Search: <input ng-model="searchMovie"></label><br><br><br><br>
</div>
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Category</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="search in searchMovies | filter:searchMovie">
<td>
{{search.title}}
</td>
<td>
{{search.description}}
</td>
<td>
{{search.name}}
</td>
</tr>
</tbody>
</table>
The search data is being retrieved from the database. I have tested the SQL and it works fine. Just thee problem is in the angularjs server side. Thank you.
You need to give Alias for controller in your app.js file
.when('/search', {
templateUrl : 'js/partials/search.html',
controller : 'SearchController'
controllerAs: 'movies',
});
Now After This in your search.html file pass controllerAs
<div>
<label>Search: <input ng-model="searchMovie"></label><br><br><br><br>
</div>
<table class="table table-hover table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Category</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="search in searchMovies | filter:searchMovie">
<td>
{{movies.search.title}}
</td>
<td>
{{movies.search.description}}
</td>
<td>
{{movies.search.name}}
</td>
</tr>
</tbody>

How can I use ng-switch to toggle displaying a nested table with AngularJS?

I'm trying to use ng-switch in AngularJS to expand a row in a table list to show another table (containing more details about that list item) when it's clicked.
However, I'm having trouble displaying the nested table. I've noticed that the problem only happens with nested tables. If I'm using ng-switch to expand a td instead of a table, it works as expected.
Here's a JSFiddle and my code to illustrate the problem: fiddle
The html:
<body ng-app="listAndDetails">
<div>
<table class="table table-bordered" ng-controller="ListAndOneDetailCtrl">
<thead>
<tr>
<th>Name</th>
<th>e-mail</th>
</tr>
</thead>
<tbody ng-repeat="user in users" ng-click="toggleSelected()" ng-switch on="isSelected(user)">
<tr>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
</tr>
<tr ng-switch-when="true" class="light-gray">
<table>
<tr>
<td>Country</td>
<td>Address</td>
<td>Description</td>
</tr>
<tr>
<td>{{user.country}}</td>
<td>{{user.address}}</td>
<td>{{user.desc}}</td>
</tr>
</table>
</tr>
</tbody>
</table>
</div>
<div ng-controller="ListAndManyDetailsCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>e-mail</th>
</tr>
</thead>
<tbody ng-repeat="user in users" ng-controller="UserCtrl" ng-click="toggleSelected()" ng-switch on="isSelected()">
<tr>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
</tr>
<tr ng-switch-when="true" class="light-gray">
<td>{{user.country}}</td>
<td>{{user.address}}</td>
<td>{{user.desc}}</td>
</tr>
</tbody>
</table>
</div>
</body>
The javascript:
angular.module('listAndDetails', [])
.value('users', [
{ name:'Damien', email:'damien#domain.com', desc:'Damien details go here...', country:'US', address:'address1'},
{ name:'Alex', email:'alex#domain.com', desc:'Alex details go here...', country:'UK', address:'address2'}
])
.controller('ListAndOneDetailCtrl', function ($scope, users) {
$scope.users = users;
$scope.selectUser = function (user) {
$scope.selectedUser = user;
};
$scope.isSelected = function (user) {
return $scope.selectedUser === user;
};
})
.controller('ListAndManyDetailsCtrl', function ($scope, users) {
$scope.users = users;
})
.controller('UserCtrl', function ($scope) {
$scope.toggleSelected = function () {
$scope.selected = !$scope.selected;
};
$scope.isSelected = function (user) {
return $scope.selected;
};
});
The div on top attempts to show a nested table upon clicking the list item, but nothing happens.
The div at the bottom attempts to show child td items upon clicking the list item and it works exactly as expected.
Does anyone have any idea what the problem is when I'm trying to expand to show a nested table using ng-switch? And how to solve it?
You have a two errors in your code.
First
In controller ListAndOneDetailCtrl on ng-click you don't have a function toggleSelected.
Second
In first table with controller ListAndOneDetailCtrl you don't have tag td in nested table.
See correct code in jsfiddle
angular.module('listAndDetails', [])
.value('users', [
{ name:'Damien', email:'damien#domain.com', desc:'Damien details go here...', country:'US', address:'address1'},
{ name:'Alex', email:'alex#domain.com', desc:'Alex details go here...', country:'UK', address:'address2'}
])
.controller('ListAndOneDetailCtrl', function ($scope, users) {
$scope.users = users;
$scope.selectUser = function (user) {
$scope.selectedUser = user;
};
$scope.isSelected = function (user) {
return $scope.selectedUser === user;
};
})
.controller('ListAndManyDetailsCtrl', function ($scope, users) {
$scope.users = users;
})
.controller('UserCtrl', function ($scope) {
$scope.toggleSelected = function () {
$scope.selected = !$scope.selected;
};
$scope.isSelected = function (user) {
return $scope.selected;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="listAndDetails">
<div>
<table class="table table-bordered" ng-controller="ListAndOneDetailCtrl">
<thead>
<tr>
<th>Name</th>
<th>e-mail</th>
</tr>
</thead>
<tbody ng-repeat="user in users" ng-click="selectUser(user)" ng-switch on="isSelected(user)">
<tr>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
</tr>
<tr ng-switch-when="true" class="light-gray">
<td>
<table>
<tr>
<td>Country</td>
<td>Address</td>
<td>Description</td>
</tr>
<tr>
<td>{{user.country}}</td>
<td>{{user.address}}</td>
<td>{{user.desc}}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div ng-controller="ListAndManyDetailsCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>e-mail</th>
</tr>
</thead>
<tbody ng-repeat="user in users" ng-controller="UserCtrl" ng-click="toggleSelected()" ng-switch on="isSelected()">
<tr>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
</tr>
<tr ng-switch-when="true" class="light-gray">
<td>{{user.country}}</td>
<td>{{user.address}}</td>
<td>{{user.desc}}</td>
</tr>
</tbody>
</table>
</div>
</body>
There were two problems:
you needed a td section enclosing your table;
<tr>
<td>
<table>
<!-- table information -->
</table>
</td>
</tr>
the controller needed some modifications so that the selected user could have a value.
.controller('ListAndOneDetailCtrl', function ($scope, users) {
$scope.users = users;
$scope.toggleSelected = function(user) {
$scope.selectedUser = user;
};
$scope.selectUser = function (user) {
$scope.selectedUser = user;
};
$scope.isSelected = function (user) {
return $scope.selectedUser === user;
};
})
Here's an updated fiddle: https://jsfiddle.net/jp43agsb/2/

Handlebars not parsing data

I am trying to use handlebars to parse a javascript object but for some reason Handlebars is not loading the data properly...
Here is my template file:
{{> header}}
<h1>Handlebars JS Example</h1>
<script id="some-template" type="text/x-handlebars-template">
<table>
<thead>
<th>Name</th>
<th>Job Title</th>
<th>Twitter</th>
</thead>
<tbody>
{{#users}}
<tr>
<td>{{fullName person}}</td>
<td>{{jobTitle}}</td>
<td>#{{twitter}}</td>
</tr>
{{/users}}
</tbody>
</table>
</script>
<body>
<!-- Insertion point for handlebars template -->
<div id="main" style="margin-left:100px">
</div>
</body>
<script type="text/javascript" src="javascript/templating.js"></script>
Here is my .js file:
$(document).ready(function() {
var source = $("#some-template").html();
var template = Handlebars.compile(source);
var data = {
users: [ {
person: {
firstName: "Garry",
lastName: "Finch"
},
jobTitle: "Front End Technical Lead",
twitter: "gazraa"
}, {
person: {
firstName: "Garrasd",
lastName: "Finch"
},
jobTitle: "Photographer",
twitter: "photobasics"
}, {
person: {
firstName: "Garry",
lastName: "Finch"
},
jobTitle: "LEGO Geek",
twitter: "minifigures"
} ]
};
Handlebars.registerHelper('fullName', function(person) {
return person.firstName + " " + person.lastName;
});
console.log(template(data));
$("#main").append(template(data));
});
Note - when I console.log(template(data)), I get the following:
<table>
<thead>
<th>Name</th>
<th>Job Title</th>
<th>Twitter</th>
</thead>
<tbody>
</tbody>
Anyone have any idea what I'm doing wrong?? I'm using node.js + express.
Thank you!!
You didn't define any block helper for users but trying to use it in the template.
Change these lines
...
{{#users}}
...
{{/users}}
...
to these:
...
{{#each users}}
...
{{/each}}
...
Check out the documentation here: http://handlebarsjs.com/builtin_helpers.html#iteration
The each block helper
You can iterate over a list using the built-in each helper. Inside the
block, you can use this to reference the element being iterated over.

Categories

Resources