How does one pass {{row.getProperty(col.field)}} into ng-click? What happens is the id does not get propagated back, but the grid render correctly with the id.
code:
var app = angular.module('testing',['ngGrid']);
app.config(['$locationProvider', function($locationProvider)
{
$locationProvider.html5Mode(true);
}]);
app.controller('TestCtrl',function($scope)
{
$scope.details = []; //whatever dummy data
$scope.loadById = function(id)
{
$window.location.href= 'newPage/?id='+id;
};
$scope.gridOptions =
{
data: 'details',
columnDefs:[{field:'id',DisplayName:'id',
cellTemplate:'<div class="ngCellText" ng-class="col.colIndex()"><a ng-click="loadById({{row.getProperty(col.field)}})">{{row.getProperty(col.field)}}</a></div>'
}]
};
});
You can just pass row in ng-click, instead of only the value outputted by the double brackets expression.
Your cellTemplate becomes this
cellTemplate:'<div class="ngCellText" ng-class="col.colIndex()"><a ng-click="loadById(row)">{{row.getProperty(col.field)}}</a></div>'
Your function becomes this
$scope.loadById = function(row) {
window.console && console.log(row.entity);
$window.location.href= 'newPage/?id='+ row.entity.id;
};
Related
I'm dividing my functions/objects into service and factory methods, and injecting them into my controller patentTab. I had a code for a tab panel which I originally placed within the controller patentTab that worked.
Now I have placed this code in a factory method and for some reason the content isn't loading. Console log shows no errors, and when I click the relative tab the correct URL is loaded, but the content doesn't change. Is there an issue with my array in the factory? If not, what is the reason?
Orginal code
app.controller('patentTab', function($scope, $http){
$scope.tabs = [{
title: 'Patent Information',
url: 'patent-info.htm'
}, {
title: 'Cost Analysis',
url: 'cost-analysis.htm'
}, {
title: 'Renewal History',
url: 'renewal-history.htm'
}];
$http.get('../json/patent-info.json').then(function(response){
$scope.patentData = response.data.patentInfo;
})
$scope.currentTab = 'patent-info.htm';
$scope.onClickTab = function (tab) {
$scope.currentTab = tab.url; //the tabs array is passed as a parameter from the view. The function returns the url property value from the array of objects.
}
$scope.isActiveTab = function(tabUrl) {
return tabUrl == $scope.currentTab;
}
});
New code (with issue)
app.controller('patentCtrl', ['$scope', '$http', 'patentTabFactory', function($scope, $http, patentTabFactory) {
$http.get('http://localhost:8080/Sprint002b/restpatent/').then(function(response) {
$scope.patents = response.data;
});
$scope.loadPatentItem = function(url) {
$scope.patentItem = url;
}
$scope.tabs = patentTabFactory.tabs;
$scope.currentTab = patentTabFactory.currentTab;
$scope.onClickTab = patentTabFactory.onClickTab;
$scope.isActiveTab = patentTabFactory.isActiveTab;
}]);
app.factory('patentTabFactory', function() {
var factory = {};
factory.tabs = [{
title: 'Patent Information',
url: 'patent-info.htm'
}, {
title: 'Cost Analysis',
url: 'cost-analysis.htm'
}, {
title: 'Renewal History',
url: 'renewal-history.htm'
}];
factory.currentTab = 'patent-info.htm';
factory.onClickTab = function (tab) {
factory.currentTab = tab.url; //the tabs array is passed as a parameter from the view. The function returns the url property value from the array of objects.
console.log(tab.url);
}
factory.isActiveTab = function(tabUrl) {
return tabUrl == factory.currentTab; //for styling purposes
}
return factory;
});
You not calling factory.onClickTab() method from your controller.
It should be like :
$scope.onClickTab = function(currentTab) {
patentTabFactory.onClickTab(currentTab);
$scope.currentTab = patentTabFactory.currentTab;
};
and, for isActiveTab, Like :
$scope.isActiveTab = patentTabFactory.isActiveTab(currentTab);
Here is a plunker where I am using a factory. The only changes I have done are:
1. Place the factory file before the app script file.
2. Use a separate declaration for factories and then inject it in the app.
var factories = angular.module('plunker.factory', []);
factories.factory('patentTabFactory', function() {
// Factory bits
};
I have injected the factories in the app.
var app = angular.module('plunker', ['plunker.factory']);
Here is a working plunker for that. PlunkR
I'm trying to keep all my model functionality in one place. I'd like to be able to call its methods within it:
JS
/**
* This app prints "OK" a number of times, according to which button is pressed
*/
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
$scope.okModel = {
oks: [],
addOne: function(){
this.oks.push(["OK"]);
},
addTwo: function(){
this.addOK();
this.addOK();
},
buttons: [
{name:"addOK", action: this.addOne}, // THIS is where the issue is I think
{name:"add2OKs", action: this.addTwo}
]
};
}]);
HTML
<div ng-controller="MyCtrl">
<!-- print the object holding all the "OK"s -->
oks: {{okModel.oks}}
<!-- show the buttons -->
<button ng-repeat="item in okModel.buttons" ng-click="item.action()">
{{item.name}}
</button>
<!-- print the "OK"s -->
<div ng-repeat="ok in okModel.oks">
{{ok[0]}}
</div>
</div>
I'm not getting an error, but it's not working either. No "OK"s are being added to the model. It seems like the issue may be with the okModel.buttons action property.
Here's a plunker: https://plnkr.co/edit/mDk43yEKSQB37QSmiKJn?p=preview
TL;DR: I realize that the issue is probably with this in buttons, what should I use instead?
Bonus question: I'm new to angular and realize I may be using models incorrectly. If you know of a better way to do models, please let me know.
Since you asked, the "angular" way to do this is to have a service provide the model, rather than defining it in your controller:
var app = angular.module('myApp', []);
app.factory('modelService', function() {
var okModel = {
oks: [],
}
okModel.addOne = function() {
okModel.oks.push(["OK"]);
};
okModel.addTwo = function() {
okModel.addOne();
okModel.addOne();
};
okModel.buttons = [{
name: "addOK",
action: okModel.addOne
},
{
name: "add2OKs",
action: okModel.addTwo
}
];
return okModel;
});
app.controller('MyCtrl', ['$scope', 'modelService', function MyCtrl($scope, modelService) {
$scope.okModel = modelService;
}]);
Here is a plunk.
#reeverd answer seems to be correct. Here is a little more clean up.
$scope.okModel = {
oks: [],
addOne: function(){
oks.push(["OK"]); // you don't have to push an array with the OK text. You can just push in the OK text itself.
},
addMulti: function(cnt){
for (var ii = 0; cnt < ii; ii++) {
// this.addOK(); this may also be your issue addOK function is not defined.
$scope.addOne(); // calls the addOne and pushes OK into the oks array.
}
},
buttons: [
{name:"addOK", action: $scope.addOne}, // THIS is where the issue is I think
{name:"add2OKs", action: $scope.addMulti(2)}
]
};
The problem is that this inside the {name:"addOK", action: this.addOne} in the array is the object itseld, and not the object wrapping the array. In this case you could do something like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
// Here you declare the functions which are going to modify
// $scope.okModel.oks
var addOne = function() {
if ($scope.okModel) {
$scope.okModel.oks.push("OK");
}
},
addTwo = function() {
addOne();
addOne();
};
$scope.okModel = {
oks: [],
addOne: addOne, // Here you assign okModel.addOne to the functions declared above
addTwo: addTwo, // Same here
buttons: [{
name: "addOK",
action: addOne // Same here
}, {
name: "add2OKs",
action: addTwo // Same here
}]
};
}]);
First you declare the functions which will modify $scope.okModel.oks array and then you use that same funcions both in your model methods and in your model buttons.
EDIT: Here you have a working plunker: https://plnkr.co/edit/sKUxjzUyVsoYp3S7zjTb?p=preview
Instead of using this, try using $scope.okModel. The actual object where this refers to is not always what you expect. It depends on how the function is invoked.
EDIT:
You can pull the definition of the functions out of the okModel like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var addOne = function() {
$scope.okModel.oks.push(["OK"]);
};
var addTwo = function() {
addOne();
addOne();
};
$scope.okModel = {
oks: [],
addOne: addOne,
addTwo: addTwo,
buttons: [{
name: "addOK",
action: addOne
}, {
name: "add2OKs",
action: addTwo
}]
};
}]);
I am using ui-router to manage various states of my site. I have used resolve to pass data to header and home controller as displayed in following code. So now I need to update the value of resolved data from HomeController and this change should reflect across to HeaderController too.
var myapp = angular.module('myapp', ["ui.router"]);
myapp.service("DataService", [function() {
var data = { title: 'Some Title' };
this.get = function() {
return data;
};
}]);
myapp.controller("HeaderController", ["data", function(data) {
var vm = this;
vm.title = data.title;
}]);
myapp.controller("HomeController", ["data", function(data) {
var vm = this;
vm.title = data.title;
vm.updateTitle = function() {
// update the resolved data here so the header and home view
// updates with new data.title
data = { title: "Another title" };
// i know i can do vm.title = data.title; to update home view.
// But would be nice to globally update and reflect that change
// on all controllers sharing the same resolved data
};
}]);
myapp.config(function($stateProvider){
$stateProvider
.state({
name: "root",
abstract: true,
views: {
"header": {
templateUrl: "header.html",
controller: 'HeaderController as vm'
}
},
resolve: {
data: ['DataService', function(DataService) {
return DataService.get();
}]
}
})
.state({
name: "root.home",
url: "",
views: {
"content#": {
templateUrl: "home.html",
controller: "HomeController as vm"
}
}
})
});
PS:
Before looking into resolve, I was injecting service directly into the controller so please do not suggest on doing that.
EDIT: Plunkr updated and now works as expected.
Here is link to plunkr
Lesson Learnt:
Angular only watches the object that is assigned to the scope, and keeps separate reference of the objects. I mean:
data = { title: 'some title' };
vm.data = data;
vm.title = data.title;
data.title = 'another title';
{{vm.title}} // some title
/////////////////
data = { title: 'some title' };
vm.data = data;
data.title = 'another title';
{{vm.data.title}} // another title
You should take an advantage of the variable reference, where you should bind your HeaderController data to vm.data = data
Another incorrect thing is data = { title: "Another title" }; which would create an data object with new reference, and the reference of service object will lost. Instead of that you should do data.title = 'Another title';
header.html
{{vm.data.title}}
HeaderController
myapp.controller("HeaderController", ["data", function(data) {
var vm = this;
vm.data = data;
}]);
Update updateTitle method code to below.
vm.updateTitle = function() {
// update the resolved data here so the header updates with new data.title
vm.data.title = "Another title";
};
Demo here
I'd say that rather than playing with actual object reference, you should have setTitle function inside your factory, from updateTitle you will call that setter method which will update title. But in that case you need to again add the service reference on both controller. If its static data then there is no need to pass them by having resolve function. I'd loved to inject the service inside my controllers and then will play with data by its getter & setter.
Preferred Approach Plunkr
Angular directive ng-hide in cell template is not binding with latest data on change in grid data. Please check plunkr for more details :- http://plnkr.co/edit/rj0PrfyzeyVeV8Q8RWtG?p=preview
In plunker template field is not updating with change in data.
var app = angular.module('app', ['ngTouch', 'ui.grid']);
app.controller('MainCtrl', function ($scope, $timeout) {
$scope.gridOpts = {
columnDefs: [
{ name:'name', field: 'name' },
{ name:'isActive', field: 'isActive'},
{ name:'template',cellTemplate:'<div><a ng-hide={{row.entity.isActive=="Y"}}>Active</a><a ng-hide={{row.entity.isActive=="N"}}>Deactive</a></div>'}
]
};
$scope.waiting = "Waiting...";
$timeout(function () {
$scope.gridOpts.data = [{ name: 'Bob' ,isActive:'Y'}];
}, 3000)
.then(function() {
$timeout( function() {
$scope.gridOpts.data = [{ name: 'John',isActive:'N' }];
}, 3000);
$scope.waiting = "Waiting again...";
})
});
you need a expression inside the ng-hide
The ngHide directive shows or hides the given HTML element based on the expression provided to the ngHide attribute.
so your ng-hide should be like
..ng-hide=row.entity.isActive=="Y"..
not like this interpolation,
.. ng-hide={{row.entity.isActive=="Y"}}..
here is the updated DEMO
I'm building a simple app with AngularJS. The app make a async AJAX call to the server and the server returns an array like this:
{
paragraphs: [
{content: "content one"},
{content: "cnt two"},
{content: "random three"},
{content: "last one yeeaah"}
]
}
So I'm setting this content to the StorageService factory via my set method. Everything is fine here.
I'm using ng-repeat to render the results and JQuery UI sortable to be able to change the order of the elements. When an item is swapped my script is calling the StorageService.swap method and the element order in StorageService is updated, BUT ng-repeat isn't rerendering the change, but if I remove/add or change the content it's working. How I can force the angular to rerender the ng-repeat?
= JSFIDDLE =
http://jsfiddle.net/6Jzx4/3/
= Example =
When a swap occurs the ng-repeat should be rerendered, so the IDs are consecutive
= Code =
HTML
<div ng-controller="Test" sortable>
<div ng-repeat="item in data().paragraphs" class="box slide_content" id="{{$index}}">
{{item.content}}, ID: {{$index}}
</div>
<input type="button" ng-click="add()" value="Add">
</div>
JS
var App = angular.module("MyApp", []);
App.controller("Test", function($scope, StorageService) {
StorageService.set({
paragraphs: [
{content: "content one"},
{content: "cnt two"},
{content: "random three"},
{content: "last one yeeaah"}
]
});
$scope.data = StorageService.get;
$scope.add = StorageService.add;
});
App.directive("sortable", function(StorageService) {
return {
link: function(scope, element, attrs) {
$(element[0]).sortable({
cancel: ".disabled",
items: "> .slide_content:not(.disabled)",
start: function(e, t) {
t.item.data("start_pos", t.item.index());
},
stop: function(e, t) {
var r = t.item.data("start_pos");
if (r != t.item.index()) {
StorageService.sort($(this).sortable("toArray"));
}
}
});
}
};
});
App.factory('StorageService', function() {
var output = {};
return {
set: function(data) {
angular.copy(data, output);
return output;
},
get: function() {
return output;
},
add: function() {
output.paragraphs.push({
content: 'Content'
});
},
sort: function(order) {
var localOutput = [];
for (var j in order) {
var id = parseInt(order[j]);
localOutput.push(output.paragraphs[id]);
}
console.log('new order', localOutput);
output.paragraphs = localOutput;
return output;
}
};
});
Angular doesn't know you've changed the array. Executing your sort inside a scope.$apply() will address that.
Note that I've added a that variable since this changes meaning inside the apply.
var that = this;
scope.$apply(function() {
StorageService.sort($(that).sortable("toArray"));
}
But that fix uncovers other problems that appear to be caused by the interaction between the jQuery sortable and Angular (here's a fiddle that shows an attempt at resolving the problems but still has issues). These issues have been solved in Angular UI Sortable. So a good path forward may be to switch to this.