angularjs call function from html return value - javascript

I'm new to AngularJS.
I want to call a function from html.
<td>
{{getComponentSubtype(component.ID)}}
</td>
However, the function calls a webapi and waits for a callback. How do i get the data to show up in the html?
function getComponentSubtype(componentId) {
apiService.get('/api/components/' + componentId + '/componentSubtype', config,
getComponentSubtypeCompleted,
getComponentSubtypeFailed);
}
function getComponentSubtypeCompleted(result) {
$scope.ComponentSubtype = result.data;
//////I WANT TO RETURN $scope.ComponentSubtype.Name//////
}

call the function from HTML and once callback is received store its value in a JSON object which can be printed in HTML. Meanwhile show a loading message in UI
HTML:
{{ getComponentSubtype(component.ID) }}
<td ng-if="componentsSubType[component.ID] != null">
{{ componentsSubType[component.ID] }}
</td>
<td ng-if="componentsSubType[component.ID] == null">Loading Component ...</td>
Controller:
function getComponentSubtype(componentId) {
apiService.get('/api/components/' + componentId + '/componentSubtype', config,
function(result) {
if ($scope.componentsSubType == null) {
$scope.componentsSubType = {};
}
$scope.componentsSubType[componentId] = result;
},
function() {
if ($scope.componentsSubType == null) {
$scope.componentsSubType = {};
}
$scope.componentsSubType[componentId] = "Load Failed";
});
}
Note: I have assumed that in HTML you are getting component from a loop (ng-repeat)

in your HTML ...
<td>{{myvariable}}</td>
in your controller ...
angular.module('yourApp').controller('ControllerName', function($scope, apiService) {
$scope.myvariable = 'please wait';
function initComponent(id) {
apiService.get('/api/components/' + id + '/componentSubtype').then(function(response) {
$scope.myvariable = response;
}).catch(function(failedResponse) {
// handle failure here
console.log('oops', failedResponse);
});
}
initComponent(500);
});

This is quite likely not ideal, but I would need to know more about your code for a better solution.
You could use an object to store the types and access that in your template:
<td>{{ componentSubtypes[component.id] }}</td>
Call getComponentSubtype for each component id:
$scope.componentSubtypes = {};
components.forEach(function(component) {
getComponentSubtype(component.id);
});
function getComponentSubtype(componentId) {
apiService.get('/api/components/' + componentId + '/componentSubtype', config,
getComponentSubtypeCompleted(componentId),
getComponentSubtypeFailed);
}
// Using a closure here for the id
function getComponentSubtypeCompleted(id) {
return function(result) {
$scope.componentSubTypes[id] = result;
// ...
}
}

Related

unable to use $q and promise

I want to use the variable StatusASof to display data in the inserthtml function as below.
App.controller("SS_Ctrl", function ($scope, $http, $location, $window, $sce, $q) {
var ShiftDetails = [];
function getMAStatusASof(Id) {
var defer = $q.defer();
$http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
}).then(function successCallback(response) {
StatusASof = response.data;
alert("getMAStatusASof : " + StatusASof); --> Got data from API here in this alert.
defer.resolve(response);
}, function errorCallback(response) {});
}
function insertHtml(dates, ShiftDetails, Id) {
// var promise = getMAStatusASof(Id); promise.then(
var defer = $q.defer();
getMAStatusASof(Id);
alert(StatusASof); --> alert says empty here
defer.resolve();
var Content;
Content = '<table class="clsTable"> <tr> <td rowspan="2">Cases ' + $scope.StatusASof + ' </td> <td rowspan="2">Total</td> ';
for (var i = 0; i <= 6; i++) {
if (i == daySeq - 1) {
Content = Content + '<td colspan="3" style="background-color:red"> {{dates[ ' + i + ']}} </td> ';
}
}
}
but $scope.StatusASof is undefined while displaying the result. Looks like $q.defer is not working for me.
How can I continue the execution of code after getting data only from the getMAStatusASof(Id);
Can somebody help here.
Update
you need to return defer.promise;
function getMAStatusASof(Id) {
var defer = $q.defer();
$http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
}).then(function successCallback(response) {
StatusASof = response.data;
alert("getMAStatusASof : " + StatusASof); --> Got data from API here in this alert.
defer.resolve(StatusASof);
}, function errorCallback(response) {
deferred.reject(false);
});
return defer.promise;
}
and the you can use this function like :
getMAStatusASof(Id).then(function(res){
if(res){
$scope.StatusASof = res;
}
})
No need to use $q.defer() here...
Just do
function getMAStatusASof(Id) {
return $http({
method: 'GET',
url: 'http://xx/api/Sxxx/GetMAStatusASof',
params: { Id: Id }
})
.then(function successCallback(response) {
return response.data;
})
.catch(function errorCallback(response) {
return null; //Effectively swallow the error an return null instead.
});
}
Then, use
getMAStatusASof(Id).then(function(result) {
console.log(result);
});
//No .catch, as we've caught all possible errors in the getMAStatusASof function
If you really would like to use $q.defer(), then the function would need to return defer.promise, like Jazib mentioned.
But as I said, since $http already returns a promise, doing the whole $q.defer() + return defer.promise thing is superfluous.
Instead, use that construction only when the thing you need to wrap isn't returning a promise on its own. E.g when you open a bootbox modal and want to be informed of when the user clicked the close button
function notifyMeOfClosing() {
var deferred = $q.defer();
bootbox.confirm("This is the default confirm!", function(result){
if(result) {
deferred.resolve();
}
else {
deferred.reject();
}
});
return deferred.promise;
}
Unable to update #Daniƫl Teunkens post with following code(from "}" to ")"). So adding as new answer.
getMAStatusASof(Id).then(function(result) {
// your code here. your HTML content.
....
console.log(result);
})
It will work hopefully.

Not able to obtain value in view (directive)

I have an array in the profile-form.html directive's controller.
But I am neither able to obtain the value of that array (all_languages) nor iterate over it using ng-options in the directive. It's simply printing as string. I am new to Angular and maybe doing everything terribly wrong.
Directive
app.directive("profileForm", function() {
return {
restrict: "E",
templateUrl: "/static/translatorNgApp/profile-form.html",
controller: ['$scope','$http',function($scope, $http) {
this.getCookie = function(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
console.log(cookieValue);
return cookieValue;
};
$scope.csrftoken = this.getCookie('csrftoken');
$scope.myForm={};
$scope.all_languages = ['English', 'Hindi'];
$scope.language_pairs = [];
$scope.getAllLanguages = function () {
$http.get('/getAllLanguages'
).success(function(response) {
// success
$scope.all_languages.concat(response);
}).error(function(response) {
// failed
});
};
$scope.submitForm = function() {
var postData = {
method: 'POST',
url: '/accounts/tprofile/',
// headers : {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
headers: {'X-CSRF-Token' : $scope.csrftoken },
data: $scope.myForm
};
$http(postData)
.then(function(response) {
// success
},
function(response) { // optional
// failed
});
console.log("Form submitted");
// $scope.message = "Sent Successfully 2";
// console.log(postData);
console.log(postData);
console.log($scope.myForm);
};
$document.ready(function(){
console.log("document ready");
$scope.getAllLanguages(); //This can be commented out for the question's sake.
});
}],
controllerAs: "profileFormCtrl"
};
});
Directive Template (profile-form.html)
<div class="form-group" >
<label for="id_source_language">Source language: </label>
<ul>
<li>
<pre>all_languages = {{all_languages | json}}</pre>
<select data-ng-model="source" ng-options="language for language in all_languages" class="form-control" id="id_source_language" name="source_language" placeholder="Source Language" required>
</select>
<button ng-click="language_pairs.pop($index)" aria-label="Remove">Remove</button>
<button ng-click="language_pairs.push({})">Add more pair</button>
</li>
</ul>
</div>
Using document.ready event inside a angular directive doesn't make sense to call specific method of scope. Because document.ready event has already happened after that event only angular started process page.
Ideally to make your code working state you need to remove $document.ready(function(){ .... }); code which isn't required there. And as document.ready is already accomplished, so the one which you had registered from directive wouldn't get call.

HTML table not updating, after pushing new item to knockout observableArray

I am having trouble updating my HTML UI.
When the document load and calls "getAllProducts()", the HTML UI displays all my items and with the right css class for 'styleStatusCss', and the right 'displayName', the problem is that when I try to update my observableArray with a newly updated product (product name or status has changed), the html UI does not update and remains the same
So here is a quick list of what is happening:
getUpdatedProducts() is called every 25 sec, and returns, any product
that has been updated
I check how many products my observable array has: appVM.itemList.length and it does have 100 (as expected!), I also check that the json product that has been sent back has some modified data, and indeed it has changed!
I then create the javascrip obj MyProduct using that product json object
Now I add my newly created javascript obj MyProduct to the observablearray: appVM.itemList.push(newUpdatedProduct);
And finally I check how many items my observablearray has, after doing the push, (and since I cannot see any changes on the HTML UI), and appVM.itemList.length now says 101 !!! How can that be? the HTML UI still displays the data as it was after the initial load
Please see below most of the code
HTML
<table >
<tbody data-bind="foreach: itemList">
<tr>
<td>
<div data-bind="css: styleStatusCss"></div>
</td>
<td>
<div data-bind="text: displayName"></div>
</td>
</tr>
</tbody></table>
And here is the javascript:
<script type="text/javascript">
var appVM;
var initializeItems = false;
$.ajaxSetup({
// Disable caching of AJAX responses
cache: false
});
$(document).ready(function () {
getAllProducts();
});
setInterval(function () {
if (initializeItems) {
getUpdatedProducts();
}
}, 25000);
function getAllProducts() {
var url = '#Url.Action("_AllProducts", "Home")';
$.ajax({
url: url,
type: 'GET',
dataType: 'JSON',
})
.success(function (result) {
initializeItems = true;
appVM = new AppViewModel();
var mappedProducts = ko.utils.arrayMap(result.ItemList, function (item) {
var con = new MyProduct(item);
return con;
});
appVM.itemList = mappedProducts;
ko.applyBindings(appVM);
})
.error(function (xhr, status, error) {
alert("FATAL ERROR");
})
}
function getUpdatedProducts() {
var url = '#Url.Action("_UpdateProducts", "Home")';
$.ajax({
url: url,
type: 'GET',
dataType: 'JSON',
})
.success(function (result) {
if (result.HasNewData) {
alert("we have some data");
alert("START COUNT: " + appVM.itemList.length); //this shows all my 100 products -> START COUNT: 100
alert("Number of new items: " + result.ItemList.length); // i only get one product back for easy debugging
for (index = 0; index < result.ItemList.length; ++index) {
var updatedProdJson = result.ItemList[index]; //get the json for the product
alert("New prod json: " + objToString(updatedProdJson)); //just for debugging print out in a readable format
var newUpdatedProduct = new MyProduct(updatedProdJson);//create our MyProduct object (which has all properties as observable)
appVM.itemList.push(newUpdatedProduct); //add our new MyProduct to the list
alert("FINAL COUNT: " + appVM.itemList.length); // --> FINAL COUNT: 101
}
}
})
.error(function (xhr, status, error) {
alert("Error: " + status);
})
}
function AppViewModel() {
var self = this; //so it references the viewModel object
self.itemList = ko.observableArray([]);
self.doAlert = function () {
alert("HERE");
}
}
function MyProduct(data) {
//alert("DATA: " + objToString(data));
this.Id = ko.observable( data.Id);
this.Name = ko.observable(data.Name);
this.status = ko.observable(data.Status);
this.displayName = ko.computed(function () {
var fullnmae = this.Id() + " " + this.Name();
return fullnmae;
}, this);
this.styleStatusCss = ko.computed(function () {
var pStatus = 'divstatusnone';
if (this.status() === 'H')
pStatus = 'divlowstatus';
if (this.status() === 'D')
pStatus = 'divhighstatus';
return pStatus;
},this);
}
function objToString (obj) {
var str = '';
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str += p + '::' + obj[p] + '\n';
}
}
return str;
}
Hope somebody can tell me where i went wrong.
Many thanks,
in getAllProducts, you're assigning the results to itemList, losing your observable array:
appVM.itemList = mappedProducts;
you need to do this instead:
appVM.itemList(mappedProducts);

Angular Controller Loading Before Data Is Loaded

I am writing a simple app that loads movie info from an API. After this loading, I am attempting to use Angular to display the movies in a simple list view. I am correctly loading the movies, but it seems like the angular controller is created and sends the movie array to the view before the movie array is populated. I am unsure how to get around this.
var movieList = [];
var app = angular.module('top250', []);
// immediately make a call to the server to get data (array of movies from text file)
$.post('/', {}, function(data) {
init(data);
});
app.controller('MovieController', function() {
// should be setting this.movies to an array of 250 movies
this.movies = movieList;
});
function init(data) {
// cycle through array, use title to retrieve movie object, add to array to be sent to view
$.each(data, function(index, value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
$.getJSON(url, function(data) {
console.log('in get json', data);
var movieObj = data;
storeMovie(movieObj);
});
});
}
function storeMovie(movieObj) {
movieList.push(movieObj);
}
And my HTML (although I'm certain this isn't the problem:
<body ng-controller="MovieController as MovieDB">
<div class="row">
<div class="large-12 columns">
<h1>IMDB Top 250 List</h1>
</div>
</div>
<div class="row">
<div class="large-12 columns" id="movie-list">
<div class="list-group-item" ng-repeat="movie in MovieDB.movies">
<h3>{{movie.Title}} <em class="pull-right">{{movie.Plot}}</em></h3>
</div>
</div>
<script src="js/foundation.min.js"></script>
<script>
$(document).foundation();
</script>
</body>
First I transformed your ajax calls to an angular factory:
app.factory('MoviesService', function($http, $q) {
function getMovie(value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
return $http.get(url).then(function(res){ return res.data; });
}
return $http.post('/').then(function(res) {
return $q.all(res.data.map(getMovie));
});
});
Then I can consume it like so:
app.controller('MovieController', function(MoviesService) {
var self = this;
MoviesService.then(function(movies) {
self.movies = movies;
});
});
don't use jquery
use angular $http or $resource
using $http, you set scope var to the data inside promise, and life will be good
You need to wait for you init method to complete:
function init(data, complete) {
$.each(data, function(index, value) {
var title = value.split(' (')[0];
title = title.split(' ').join('+');
var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
$.getJSON(url, function(data) {
console.log('in get json', data);
var movieObj = data;
storeMovie(movieObj);
}).always(function(){ // count competed ajax calls,
// regardless if they succeed or fail
if(index === data.length -1)
complete(); // call callback when all calls are done
});
});
}
Now you can do this:
app.controller('MovieController', function() {
$.post('/', {}, function(data) {
init(data, function(){
this.movies = movieList;
});
});
});
Personally I would just keep the movieList inside of the init method and send it with the callback when you're done, but that's just a preference.

How to send JSON object list to light box?

In MVC 4 application I want when click a link, to show some related Products list in lightbox. I have method returns products I need:
public ActionResult GetRelatedProducts(int id)
{
var realProducts = GetRelatedProducts(id);
List<object> productsObjectList = new List<object>();
foreach (var item in realProducts)
{
productsObjectList .Add(new
{
id = item.Id,
fullname = item.Name
});
}
return Json(productsObjectList , JsonRequestBehavior.AllowGet);
}
HTML is:
<a class="show" id="show">Show</a>
<div id="productBox" style="display: none;">
// Product list will get here
</div>
And script:
$('#show').click(function (e) {
url = '#Url.Action("GetRelatedProducts", "Product")';
var data = { id: '#Model.Id' };
$.post(url, data, function (result) {
$('#productBox').lightbox_me({
onLoad: function () {
//How to send returned product list to light box, to show them by foreach loop
}
});
e.preventDefault();
});
});
How can I send product list to productBox to show products?
You code:
$('#show').click(function (e) {
url = '#Url.Action("GetRelatedProducts", "Product")';
var data = { id: '#Model.Id' };
$.post(url, data, function (result) { // <- "result" will contain array
$('#productBox').lightbox_me({
onLoad: function () { ... }
});
e.preventDefault(); // <- this must prevent "a" tag, put it outside
});
});
You could use your list on client side like this:
$.post(url, data, function (result) {
var list = '<ul>';
for(var i = 0; i < result.length; i++)
{
list += '<li>' + result[i].fullname + '</li>';
}
list += '</ul>';
$('#productBox').html(list).lightbox_me();
});
OR as Vladimir Bozic wrote, just use PartialViewResult, from controller return PartialView, it is like normal view, but without layout, just html block and you can use it like:
$.post(url, data, function (result) {
$('#productBox').html(result).lightbox_me();
});

Categories

Resources