HTML view cannot see AngularJS - javascript

I am making a backend call using AngularJS using AJAX. My call returns the JSON successfully. This code is written in a JavaScript file.
I have a html file in the same code base which is unable to iterate through this JSON.
My JavaScript snippet is as below:
angular.module('kmapp').controller("FieldCodesCtrl", ['$scope', function($scope){
var model = {};
console.log("FieldCodesCtrl");
$http.get("/my/rest/URL").success(function (data) {
model.items = data;
console.log("data set on items");
$scope.fieldCodes = model;
console.log($scope.fieldCodes.items.length);
});
// $scope.fieldCodes = model;
}]);
My html is as below:
<tr ng-repeat="item in fieldCodes.items">
<td>{{item.id}}</td>
<td>{{item.comment}}</td>
</tr>
My issue is that the "fieldCodes.items" has nothing in the html. So it does not display the ID and Comment that I get from my JSON.
Can someone please help. I am new to AngularJS. So please excuse me if it is something obvious.

Instead of using model.items = data; , Use model = data; Otherwise it will not defined properly. As you are using in your view (model bind) item.id looks ok. so try with this (model = data) Hope this will work. I can Answer you more specify, If you can sent the sample JSON.
Thanks
\Riyadh

$http needs to be injected into your controller.
.controller("FieldCodesCtrl", ['$scope', '$http', function($scope, $http){
Make sure you have your module registered to the HTML tag in the document. I think it is something like "ng-app."

Maybe I'm looking at this wrong, but it seems that you are assigning this object to model.items
{ fieldCodes: {
items: [
{ id: XXX name: CCC value: DD comment: AA },
{ id: aaaa name: aaaaadd value: ddf comment: ee }]
}
}
Wouldn't you instead want model = data.fieldCodes; to be able to use fieldCodes.items?

Related

Angular use Json data in controller

I am closing this issue because the issue has been resolved, 1 problem was I was using $scope.data = data (instead of $scope.data = data.data). The other reason is because of the answer I selected.
What I am trying to do, is get data (an object) from a json file and assign it to another variable (or $scope property I suppose?).
But when I do this, the object I've initialized gets assigned to my alternate variable before the $http.get() call happens?
<!DOCTYPE html>
<html>
<head>
<title>Playing with Angular</title>
<script src="angular.min.js"></script>
</head>
<body data-ng-app="myApp" ng-controller="myCtrl" ng-init="">
<p>{{ element1 }}</p>
<script type="text/javascript">
var app = angular.module('myApp', []);
app.controller('myCtrl', function ($scope, $http) {
$scope.data = {};
$http.get("data.json").then(function(data) {
$scope.data = data.data;
});
$scope.element1 = $scope.data.element1;
});
</script>
</div>
</body>
</html>
data.json is very simple:
{
"element1": {
"sub1":1,
"sub2":2
}
}
Am I trying to do something that isn't possible in Angular?
And if this question has been answered elsewhere please point me to it.
Edit
One step closer
I changed the above $scope.data = data; to $scope.data = data.data;. Which was what my biggest problem was (in my opinion anyways, supported by the fact that no one else saw this).
Now my problem is that I am looking for a better solution than Narain Mittal suggested below. I can place $scope.element1 = $scope.data.element1; inside of $http.get("data.json").then(function(data) {});, and it will work. This in not what I want.
But Im going to consolidate what exactly I want, then go over to Code Review. Thanks for all the help!
You need to have the statement $scope.element1 = $scope.data.element1; inside .thenfunction callback, something like this:
$http.get("data.json").then(function(data) {
$scope.data = data;
$scope.element1 = $scope.data.element1;
});
The reason it doesn't work for you is javascript execution doesn't wait for the $http call to complete before proceeding with the assignment unless you put it in the callback.
You need to parse JSON data like this
$http.get("data.json").then(function(data) {
var data = JSON.parse(data)
$scope.element1 = data.element1
});
It is likely that data is a string that needs to be parsed as json.
$scope.data = {}
$http.get("data.json").then(function(data) {
$scope.data = angular.fromJson(data);
});
I have edited this, since i think a better option would be to do the above and to have your template drill into data
<p>{{ data.element1 }}</p>
EDIT
Sorry, it's fromJson

Angular Translate synchronicity issue in JS

Angular Translate works great as a filter in the view.
I am using angular-translate-loader-static-files with external files like locale-en.json etc.
The problem is when I try to do something like this:
var placeholder = $translate('placeholder.NAME')
.then(function (translatedValue) {
return translatedValue;
});
I always get a promise back, and in the UI it shows as {} instead of the word NAME for english etc.
What is the correct way to translate in JS using angular-translate?
EDIT:
Tried this and got the following result (still not solved)
var placeholder = '';
$translate('placeholder.NAME').then(function (translatedValue) {
console.log(translatedValue);
placeholder = translatedValue;
}, function(err){
console.log(err); // returns placeholder.NAME
});
console.log(placeholder); // returns empty string
var placeholder = '';
$translate('placeholder.NAME').then(function (translatedValue) {
placeholder = translatedValue;
});
I'd recommend to keep your controller free from translation logic and translate your strings directly inside your view like this:
<h1>{{ 'TITLE.HELLO_WORLD' | translate }}</h1>
You can use instant function to get the value without the promise:
var translation = $translate.instant('placeholder.NAME');
However, this does not wait for the translation files to get loaded. You should make sure you are calling this after translation files are loaded.
From the website http://angular-translate.github.io/docs/#/guide/03_using-translate-service635
app.controller('Ctrl', ['$scope', '$translate', function ($scope, $translate) {
$translate(['HEADLINE', 'PARAGRAPH', 'NAMESPACE.PARAGRAPH']).then(function (translations) {
$scope.headline = translations.HEADLINE;
$scope.paragraph = translations.PARAGRAPH;
$scope.namespaced_paragraph = translations['NAMESPACE.PARAGRAPH'];
});
}]);

Angular.js render data in controller

I have a rather simple question. I have a simple controller and its $scope.coords = []; renders JSON in HTML:
[24.43359375, 54.6611237221]
[25.2905273438, 54.6738309659]
[25.3344726562, 54.6102549816]
[25.2685546875, 54.6801830971]
[25.2960205078, 54.6611237221]
How can I render that JSON not in html, but in my controller itself ? The code looks like that. Please see the comment in code:
propertyModule.controller('propertyController', ['$scope', 'Property', function ($scope, Property) {
// Query returns an array of objects, MyModel.objects.all() by default
$scope.properties = Property.query();
// Getting a single object
$scope.property = Property.get({pk: 1});
$scope.coords = [];
$scope.properties = Property.query({}, function(data){
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
$scope.positions = //$Resource('realestate.property').items();
[
[54.6833, 25.2833], [54.67833, 25.3033] // those coordinates are hardcoded now, I want them to be rendered here by $scope.coords
];
}]);
First off, you're showing us a bunch of arrays, not a JSON document. But since your code seems to be working, I'll assume you do have a valid JSON to work with.
You need to consider the fact that you are making an asynchronous request here :
$scope.properties = Property.query({}, function(data) {
console.log(data);
angular.forEach(data , function(value){
$scope.coords.push(value.coordinates);
});
});
This means you won't be able to fetch data from $scope.coords before anything has arrived.
There are several ways to solve that :
You could simply fetch data while you're still in the loop :
angular.forEach(data , function(value) {
$scope.coords.push(value.coordinates);
if('your condition') {
$scope.positions.push(value.coordinates);
}
});
You could use a promise, see the angular doc.
Or you could watch over $scope.coords with $scope.$watch.

How to populate select filters on ng-table from async call

tl:dr
How can I populate an ng-table including 'select' filters using ajax/json?
Plunk showing the problem: http://plnkr.co/Zn09LV
Detail
I am trying to get to grips with AngualrJS and the ng-table extension and although I can get some nice tables with working filters and such when I'm using static data defined in the javascript - once I get to trying to load real data into the table I hit a snag.
The main body of the ng-table is populated correctly and as long as I only use the text filter everthing seems to be working:
<td data-title="'Name'" filter="{ 'Name': 'text' }" sortable="'Name'">
{{user.Name}}
</td>
Works just fine.
However, if I update this to use the select filter:
<td data-title="'Name'" filter="{ 'Name': 'select' }" sortable="'Name'" filter-data="Names($column)">
{{user.Name}}
</td>
I run into a syncronisation issue in that the Names variable is always evaluated before the data has returned from the server. (Possibly the Names varibale is evaluated before the request to the server is even sent.) This means I get an empty list for the filter.
Once the data returns from the server - I can't seem to find a way of updating the select filter. Re-running the code that creates the filter list initially seems to have no effect - I'm not sure how to trigger the ng-table to re-check its filters so the updated variable isn't read.
I also can't figure out a way to postpone the evaluation of the variable until after the async call is complete.
For my javascript I have pretty much used the example ajax code from the ng-table GitHub page and added onto it the example code for the select filter.
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page
sorting: {
name: 'asc' // initial sorting
}
}, {
total: 0, // length of data
getData: function($defer, params) {
// ajax request to api
Api.get(params.url(), function(data) {
$timeout(function() {
// update table params
var orderedData = params.sorting ?
$filter('orderBy')(data.result, params.orderBy()) :
data.result;
orderedData = params.filter ?
$filter('filter')(orderedData, params.filter()) :
orderedData;
$scope.users = orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count());
params.total(orderedData.length); // set total for recalc pagination
$defer.resolve($scope.users);
}, 500);
});
}
});
var inArray = Array.prototype.indexOf ?
function (val, arr) {
return arr.indexOf(val)
} :
function (val, arr) {
var i = arr.length;
while (i--) {
if (arr[i] === val) return i;
}
return -1
};
$scope.names = function(column) {
var def = $q.defer(),
arr = [],
names = [];
angular.forEach(data, function(item){
if (inArray(item.name, arr) === -1) {
arr.push(item.name);
names.push({
'id': item.name,
'title': item.name
});
}
});
def.resolve(names);
return def;
};
I've tried a few attempts at adding on an additional $q.defer() and wrapping the initial data get followed by the $scope.names function - but my understanding of promise and defer isn't strong enough to get anything working.
There are a few notes on GitHub suggesting this is a bug in ng-table, but I'm not sure if that's the case or I'm just doing something daft.
https://github.com/esvit/ng-table/issues/186
Pointers on how to procede greatly appreciated
-Kaine-
I had a similar but slightly more complex issue. I wanted to be able to update the list of filters dynamically which seemed totally doable since they should just in a $scope variable anyway. Basically, I expected that, if I have $scope.filterOptions = []; then I could set filter-data="filterOptions" and any update to that list would be automatically reflected. I was wrong.
But I found a solution that I think is pretty good. First, you need to override the ngTable select filter template (in case you don't know how to do this, it involves using $templateCache and the key you need to override is 'ng-table/filters/select.html').
In the normal template, you'll find something like this ng-options="data.id as data.title for data in $column.data" and the problem with that is that $column.data is a fixed value that won't change when we update $scope.filterOptions.
My solution is to pass only the $scope key as filter-data instead of passing the whole list of options. So, instead of filter-data="filterOptions", I'll pass filter-data="'filterOptions'" and then, put a small change in the template like: ng-options="data.id as data.title for data in {{$column.data}}".
Obviously, this is a significant change to how the select filter works. In my case, it was for a very small app that only had one table but you may be concerned that a change like this will break your other selects. If that's the case, you may want to build this solution into a custom filter instead of just overriding 'select'.
You can achieve that with a custom filter:
The code for the standard select filter on ngtable says:
<select ng-options="data.id as data.title for data in column.data"
ng-model="params.filter()[name]"
ng-show="filter == 'select'"
class="filter filter-select form-control" name="{{column.filterName}}">
</select>
When you call this data you pass: filter-data="names($column)" and ngtable takes care of getting the data for you. I don't know why this does not work with an external resource. I bet it has something to do with the $column and the promise, as you pointed out.
I did a quick workaround in my code to avoid that. Writing my own select filter template like:
<select id="filterTest" class="form-control"
ng-model="tableParams.filter()['test']"
ng-options="e.id as e.title for e in externaldata">
</select>
You fetch this externaldata in your controller:
$scope.externaldata = Api.query(); // Your custom api call
It works perfectly, but I do have an id on my data, so no need of the name function.
I understand this solution is not optimal. Let's see if somebody writes here more than this 'workaround' and enlightens us. Even esvit is here sometimes ;)
This works for me:
HTML:
<td data-title="'Doc type'" filter="{ 'doc_types': 'select' }" filter-data="docTypes()" sortable="'doc_types'">
{{task.doc_type}}
</td>
AngularJS:
$scope.docTypes = function ($scope)
{
var def = $q.defer();
//var docType = [
// {'id':'4', 'title':'Whatever 1'},
// {'id':'9', 'title':'Whatever 2'},
// {'id':'11', 'title':'Whatever 3'}
//];
// Or get data from API.
// Format should be same as above.
var docType = $http.get('http://whatever.dev', {
params: { param1: data1 }
});
//Or with Restangular
var docType = Restangular.all('/api/doctype').getList();
def.resolve(docType);
return def;
};
As mentioned by #AndiĆ³n You can achieve with custom filter.
It is easy to achieve Asynchronous data population with Promises (the $q service in Angular), interesting Andy Article about Promises
You can amend the $scope.names method and add $http service that return the asynchronous data and resolve the deferred object as:
$scope.names = function(column) {
var def = $q.defer();
/* http service is based on $q service */
$http({
url: siteurl + "app/application/fetchSomeList",
method: "POST",
}).success(function(data) {
var arr = [],
names = [];
angular.forEach(data, function(item) {
if (inArray(item.name, arr) === -1) {
arr.push(item.name);
names.push({
'id': item.name,
'title': item.name
});
}
});
/* whenever the data is available it resolves the object*/
def.resolve(names);
});
return def;
};
I encountered a similar issue but did not want to make the additional AJAX call to get the filter values.
The issue with the OP's code is that filter-data function executes before $scope.data is populated. To get around this I used the Angular $watch to listen for changes on $scope.data. Once $scope.data is valid the filter-data is populated correctly.
$scope.names2 = function () {
var def = $q.defer(),
arr = [],
names = [];
$scope.data = "";
$scope.$watch('data', function () {
angular.forEach($scope.data, function (item) {
if (inArray(item.name, arr) === -1) {
arr.push(item.name);
names.push({
'id': item.name,
'title': item.name
});
}
});
});
def.resolve(names);
return def;
};
The original plunk forked with the change: http://plnkr.co/edit/SJXvpPQR2ZiYaSYavbQA
Also see this SO Question on $watch: How do I use $scope.$watch and $scope.$apply in AngularJS?
I solved the issue with $q.defer() as mentioned by Diablo
However, the is code actually pretty simple and straightforward:
in HTML:
<td ... filter-data="countries">
in controller:
$scope.countries = $q.defer();
$http.get("/getCountries").then(function(resp){
$scope.countries.resolve(resp.data.data);
})
"First, you need to override the ngTable select filter template (in case you don't know how to do this, it involves using $templateCache and the key you need to override is 'ng-table/filters/select.html')."
I added the overrided script below the script of ng-table and everything worked well...
<script id="ng-table/filters/select.html" type="text/ng-template">
<select ng-options="data.id as data.title for data in {{$column.data}}" ng-table-select-filter-ds="$column" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" class="filter filter-select form-control" name="{{name}}"> <option style="display:none" value=""></option> </select>
</script>
What I did is just put the select tag with values and have the ng-model return the values for the filter.
This was helpful since I needed to translate the plain text.
<td data-title="'Status'| translate" ng-bind = "("cc_assignment_active"== '0') ? ('Inactive' | translate) : ('Active'| translate)"
filter="{ cc_assignment_active: 'select3'}" >
</td>
<script id="ng-table/filters/select3.html" type="text/ng-template">
<select class="filter filter-select form-control" ng-model="params.filter()[name]" name="{{name}}">
<option active value="" translate>---All---</option>
<option value="1" translate>Active</option>
<option value="0" translate>Inactive</option>
</select>

Passing data between controllers using service and confusion with using [ '$scope' , 'service' , function($scope, service){}]

I am badly stuck with this problem of passing data from one controller to other in angularJS. Before my code was: whenever I click on templateController's div, it would trigger setTemplate with the help of ng-click... Now my objective was to send templateController's selected data to ReplyController...
After reading forum posts here, i decided to create a service called 'selectionService', so that i can transmit data between 2 controllers...
//Defined Service
proApp.service('selectionService', function() {
var selected_template;
addTemplate = function(newObj) {
selected_template = newObj;
};
getTemplate = function(){
return selected_template;
};
});
//Controller 1... where templates are selected
proApp.controller('TemplateController',function($scope, selectionService)
{
$scope.templates = [
{template_name:"Template 1", template_text:"We apologize for the interruption."} ,
{template_name:"Template 2", template_text:"Thank you for contacting us."} ,
} ,
];
// on ng-click
$scope.setTemplate = function(tmp)
{
selectionService.addTemplate(tmp);
}
});
// Controller 2 ... supposed to catch the selected template.
proApp.controller('ReplyController', function($scope, selectionService)
{
$scope.template = selectionService.getTemplate();
});
So whenever i run this code, i started getting this error
Object [object Object] has no method addTemplate...
Again I tweeked the code where they were suggesting to use Squarebrackets and put $scope , servicename and then write function with same parameters.. I don't understand why I should do this? Event after doing some changes like
[ '$scope' , 'service' , function($scope, service){}] ,
I am still not able to figure out the solution to pass data from one controller to other using service.
Could you help? What am I missing? I am very new to angularJS way of doing stuff.
I think it's actually quite simple. You just need to add your methods to the service by using this.. Currently they are declared on window. Change your service declaration to use this...
proApp.service('selectionService', function() {
var selected_template;
this.addTemplate = function(newObj) {
selected_template = newObj;
};
this.getTemplate = function(){
return selected_template;
};
});
As for using the array notation for dependencies, it's a good practice but it's not required. It'll save you from headaches if you ever run your code through a minifier.

Categories

Resources