I have a custom directive for soundcloud that requires the soundcloud url. The soundcloud url is fetched from the database through the $http service, however, the div for the soundcloud custom directive is loaded and requires the value of the soundcloud url before it is even defined.
The Plangular Directive Code I got is here:
https://github.com/jxnblk/plangular/blob/master/src/plangular.js *I did not develop this
This is my HTML code:
<div plangular="{{soundcloud}}">
<button ng-click="playPause()">Play/Pause</button>
<progress ng-value="currentTime / duration || 0">
{{ currentTime / duration || 0 }}
</progress>
</div>
And this is the Angular Code:
displaySong.controller('song', ['$scope', '$http', 'fetchSong', function($scope, $http, fetchSong) {
$scope.songID
$scope.songName;
//Controller properties
$scope.songPromise; //The song promise for fetching
$scope.init = function(songID, userID) {
$scope.songID = songID;
$scope.userID = userID;
$scope.songPromise = $http({
method: "post",
url: fetchSong,
data: {
song_id: $scope.songID
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function(successResponse) {
console.log('Successfully fetched song');
console.log(successResponse);
var song = successResponse.data;
$scope.songID = song.song_id;
$scope.songName = song.song_name;
$scope.songType = song.song_type;
$scope.songEmbed = song.song_embed;
$scope.soundcloud = song.song_embed;
}, function(errorResponse) {
console.log('Error fetching');
$scope.songID = null;
});
};
}]);
I know it's a problem with the asynchronous nature because when I add this line in the beginning of my song controller:
$scope.soundcloud = "https://soundcloud.com/jshigley/shine";
It works perfectly fine. I've also noticed that when I spam the play/pause button that DOES come up from the directive, I get multiple console errors of "HTTP 404 Not Found", which leads me to believe it's trying to find a track of undefined url
Since it's a div directive and not a function call I can't use promises such as chaining a then to my $scope.songPromise. I've thought of putting it into a controller and having the controller do something like $timeout for 5 seconds, but I don't think this delays the execution of the DOM.
The soundcloud URL DOES end up getting loaded, but it remains undefined in the eyes of the plangular directive (I've actually encountered lots of these problems with bad timing of loading scope and directives). Any Angular Wizards willing to teach me how to tame the asynchronous nature of AngularJS?
You can use $watch in the custom directive to watch when url attributes is changed.
In
link: function(scope, el, attr) {
change from
if (src) {
resolve({ url: src, client_id: client_id }, function(err, res) {
if (err) { console.error(err); }
scope.$apply(function() {
scope.track = createSrc(res);
if (Array.isArray(res)) {
scope.tracks = res.map(function(track) {
return createSrc(track);
});
} else if (res.tracks) {
scope.playlist = res;
scope.tracks = res.tracks.map(function(track) {
return createSrc(track);
});
}
});
});
}
to
scope.$watch('attr.plangular', function(newVal) {
resolve({ url: attr.plangular, client_id: client_id }, function(err, res) {
if (err) { console.error(err); }
scope.$apply(function() {
scope.track = createSrc(res);
if (Array.isArray(res)) {
scope.tracks = res.map(function(track) {
return createSrc(track);
});
} else if (res.tracks) {
scope.playlist = res;
scope.tracks = res.tracks.map(function(track) {
return createSrc(track);
});
}
});
});
}, true);
If you dont want to change the directive then you might want to use ng-if to load that plangular div only when you get the url.
<div plangular="{{soundcloud}}" ng-if="haveurl">
and in the angular code :
}).then(function(successResponse) {
console.log('Successfully fetched song');
console.log(successResponse);
$scope.haveurl = true;
Try using ng-show like this to only show the div once your $http request has been completed.
<div ng-show="httpRequestComplete" plangular="{{soundcloud}}">
<button ng-click="playPause()">Play/Pause</button>
<progress ng-value="currentTime / duration || 0">
{{ currentTime / duration || 0 }}
</progress>
</div>
displaySong.controller('song', ['$scope', '$q', '$http', 'fetchSong', function($scope, $http, fetchSong) {
/* add $q promise library */
$scope.songID
$scope.songName;
var httpRequest = function() {
var deferred = $q.defer();
$http({
method: "post",
url: fetchSong,
data: {
song_id: $scope.songID
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function(successResponse) {
deferred.resolve({response: successResponse});
console.log('Successfully fetched song', successResponse);
var song = successResponse.data;
$scope.songID = song.song_id;
$scope.songName = song.song_name;
$scope.songType = song.song_type;
$scope.songEmbed = song.song_embed;
$scope.soundcloud = song.song_embed;
}).error(function(error) {
console.log(error);
});
return deferred.promise;
};
httpRequest().then(function(response) {
$scope.httpRequestComplete = true;
console.log('div will show');
};
}]);
I would do something like this that delays the showing of the div until httpRequestComplete = true, or until your promise ($q) is fulfilled. This will make sure that your div isn't loaded until you have the information available.
Related
What am I missing ? I am new to Angularjs. Trying angularjs with asp.net mvc. I am unable to access an asp.net mvc controller to return me a JsonResult using $resource of angular.
However, I get success otherwise using $.getJson of javascript but not using angularjs. What am I missing ? please guide. Thank you for replying any.
Following is my Service
EbsMvcApp.factory('classListService', function ($resource, $q)
{
var resource = $resource
(
'/Home/ClassList'
, {}
//{ method: 'Get', q: '*' }, // Query parameters
, { 'query': { method: 'GET' , isArray:false } }
);
function get($q)
{
console.log('Service: classListServic > Started');
var Defered = $q.defer();
resource.get
(
function (dataCb)
{
console.log('success in http service call');
Defered.resolve(dataCb);
}
, function (dataCb)
{
console.log('error in http service')
Defered.reject(dataCb);
}
);
return Defered.promise; // if missed, would throw an error on func: then.
};
return { get: get };
});
angular Controller:
var EbsMvcApp = angular.module('myApp', ['ngResource']);
//'classListService',
EbsMvcApp.controller
(
'myAppController',
['$scope','classListService','$q' , function ($scope, classListService, $q)
{
console.log('controller myAppController started');
var classList = classListService.get($q);
classList = classList.then(
function ()
{
(
function (response)
{
console.log('class list function response requested');
return response.data;
}
);
}
);
console.log(classList.ClassName);
console.log(classList);
console.log('end part of ctrl');
$scope.classList = classList;
$scope.SelectedClassID = 0;
$scope.message = ' message from Controller ';
}
]
);
Asp.net MVC Controller
namespace EBS_MVC.Controllers
{
public class HomeController : BaseController
{
ApplicationDbContext db = new ApplicationDbContext();
public JsonResult ClassList()
{
var List = new SelectList(db.tblClass, "ID", "ClassName");
return Json(List, JsonRequestBehavior.AllowGet);
}
}
}
Brower's response (F12):
ControllerTry1.js:11 controller myAppController started
serviceGetClassList.js:16 Service: classListServic > Started
ControllerTry1.js:28 undefined
ControllerTry1.js:29 c
ControllerTry1.js:31 end part of ctrl
angular.js:12520 Error: [$resource:badcfg]
[Browers response: screen shot][1]
Oky, finally, I got a solution using the $http service. from here
http://www.infragistics.com/community/blogs/dhananjay_kumar/archive/2015/05/13/how-to-use-angularjs-in-asp-net-mvc-and-entity-framework-4.aspx
in csHtml file, a reference to the service.js and Controler.js is required.
I am not sure if I have added it earlier or later now. but its required.
ng-Controller:
EbsMvcApp.controller('ClassListController', function ($scope, ClassListService2) {
console.log('ClassListController Started');
GetClassList();
function GetClassList()
{
ClassListService2.GetJson()
.success(function (dataCallBack) {
$scope.classList = dataCallBack;
console.log($scope.classList);
})
.error(function (error) {
$scope.status = 'Unable to load data: ' + error.message;
console.log($scope.status);
});
}
});
ng-Service:
EbsMvcApp.factory('ClassListService2', ['$http', function ($http) {
console.log('ClassListService2 Started');
var list = {};
list.GetJson = function () {
return $http.get('/Home/ClassList');
};
return list;
}]);
csHtml View:
<div class="text-info" ng-controller="ClassListController">
<h3> Text from Controller: </h3>
#*table*#
<table class="table table-striped table-bordered">
<thead>
<tr><th>DisplayName</th><th>Value</th>
</thead>
<tbody>
<tr ng-hide="classList.length">
<td colspan="3" class="text-center">No Data</td>
</tr>
<tr ng-repeat="item in classList">
<td>{{item.Text}}</td>
<td>{{item.Value}}</td>
</tr>
</tbody>
</table>
Sorry for the delay, I just wrote up some code to quickly test the ngResource module as I haven't used it yet.
I've got the code working to do what you want using the ngResource module. I think part of the problem was that you was configuring the query method but calling the get method so your configurations was not applied.
Here is the service class that I wrote to test against a controller the same as yours.
(function () {
'use strict';
angular
.module('myApp')
.service('classService', ClassService);
ClassService.$inject = ['$resource', '$q'];
function ClassService($resource, $q) {
var resource = $resource
(
'/Home/ClassList',
{},
{
'get': { method: 'GET', isArray: true },
'query': { method: 'GET', isArray: true }
}
);
var service = {
get: get
};
return service;
////////////
function get() {
var Defered = $q.defer();
resource.get(function (dataCb) {
console.log('success in http service call');
Defered.resolve(dataCb);
}, function (dataCb) {
console.log('error in http service')
Defered.reject(dataCb);
});
return Defered.promise;
};
};
})();
The controller looks like this
(function () {
'use strict';
angular
.module('myApp')
.controller('classController', ClassController);
ClassController.$inject = ['$scope', 'classService'];
function ClassController($scope, classService) {
var vm = this;
vm.data = null;
activate();
/////////////
function activate() {
var classList = classService.get().then(function (response) {
console.log('class list function response requested');
vm.data = response;
console.log(vm.data);
});
console.log('end part of ctrl');
$scope.SelectedClassID = 0;
$scope.message = ' message from Controller ';
};
};
})();
I've included some of your original code just so you can see how it would fit in.
Glad to see you have got it working though!
I am making my first project using Angularjs 1.4.3.
In my controller I am making a http request, in the success method of this http request I am updating a scope variable. In http call I am getting the latest values but in the view side its not updating the values.
Plunker Link (#rupesh_padhye thanks). (Since it is calling the servlet action, so no data will be shown in Plunker)
app.controller('measuresCtrl', ['$scope', '$modal', '$http', function($scope, $modal, $http) {
$scope.groups = []
$scope.loadAllMeasure = function() {
$http.get("fetchAllMeasure")
.success(function(data) {
console.log("Before insert");
console.log($scope.groups);
$scope.groups = data.measures;
console.log("After insert");
console.log($scope.groups);
})
.error(function() {
});
};
$scope.loadAllMeasure();
$scope.submit = function (form) {
$http({
method: 'POST',
url: 'saveMeasure',
data: {
id: form.id,
name: form.name,
description: form.description
},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function(data, status, headers, config) {
$scope.loadAllMeasure();
}).error(function(data, status, headers, config) {
});
}
})
And whenever I am performing any CRUD operation on measures I am calling a method $scope.loadAllMeasure();. But its not updating the values in the view (jsp) page.
I have tried $scope.$apply method but I am getting Error: $digest already in progress.
When I printed the value for $scope.groups using console.log inside success method, then its showing the latest values.
In my view (jsp) page I am just using ng-repeat function to show all the records in table format.
Code for my view page (minimal code) -
<div ng-repeat="group in groups | orderBy:'name'">
<div>
<input type="checkbox" id="checkbox-{{group.id}}" class="ui-checkbox" /><label for="checkbox-{{group.id}}">{{group.name}}</label>
</div>
<div>{{ group.description}}</div>
<div>
<div class="fa fa-pencil button" data="{{group.id}}" id="{{::elementId}}" ng-click="showEditForm(group.id, $event)"></div>
<div class="fa fa-trash button" data="{{group.id}}" ng-click="deleteGroup(group.id)"></div>
<div class="fa fa-clone button" data="{{group.id}}" id="{{::elementId}}" ng-click="showCloneForm(group.id, $event)"></div>
</div>
</div>
Values in console.log are
Before insert
Object { id=1, description="Measure Description1", name="Demo"}]
And
After Insert
[Object { id=1, description="Measure Description1", name="Demo"}, Object { id=2, description="Description2", name="Demo2"}]
How to update scope variable value in view after http call?
After assigning the new data to a $scope variable call:
$scope.$digest();
This will update the current scopes values
reference here: https://docs.angularjs.org/api/ng/type/$rootScope.Scope
I cant see anything wrong with your example code.
I have created a JSFiddle to try and help you.
The server call has been replaced by a setTimeout function that returns a promise.
Please see JSFiddle https://jsfiddle.net/sjwkbzxa/
Please see example below:
<div data-ng-controller="TestController as vm">
<button data-ng-click="loadAllMeasure()">Load List from Server</button>
<ul>
<li data-ng-repeat="group in groups | orderBy:'name'">
<span>{{group.description}}</span>
</li>
</ul>
</div>
The javascript:
angular.module('application',[]).controller("TestController", ['$scope', '$q', function($scope, $q){
$scope.groups = [{ id:1, description:"Initial List", name:"Demo"}];
$scope.loadAllMeasure = function(){
loadData().then(function(data){
$scope.groups = data;
});
};
function loadData(){
var deferred = $q.defer();
setTimeout(function(){
var data = [{ id:1, description:"Measure Description1", name:"Demo"}, { id:2, description:"Description2", name:"Demo2"}];
deferred.resolve(data);
}, 3000);
return deferred.promise;
}
}]);
Maybe you are missing something on your side that we cant see?
I'm a little late to answer this, but here are my 2 cents:
A simple assignment of the server data (response.data) to a $scope object didnt seem to work for me
$scope.something = response.data //didn't work
So, I returned a promise object from the service into the controller and then use
angular.copy(response.data,$scope.something)
to copy the values returned from the server. You could also pass the $scope.something to the service as a parameter to the function and have angular.copy in the service function as well, but i don't know if it's a good practise to do that.
$scope.loadAllMeasure = function() {
CalltoServer();
};
CalltoServer = function() {
return $http.get("fetchAllMeasure")
.success(function(data) {
$scope.groups = data.measures;
})
.error(function() {
});
}
try this , the success will be after 2 or 3 seconds so i guess inside the event it takes rist to bind
Hey I also faced the same issue and if anyone is still looking, it is caused by change in the $scope variable inside the $http. I think a new $scope is being created inside the success function(some prototypical inheritance stuff).
So make a copy of the variable $scope, something like
var s = $scope;
and then change
s.groups = someData;
Your code:
app.controller('measuresCtrl', ['$scope', '$modal', '$http', function($scope, $modal, $http) {
var s = $scope;
$scope.groups = []
$scope.loadAllMeasure = function() {
$http.get("fetchAllMeasure")
.success(function(data) {
console.log("Before insert");
console.log($scope.groups);
s.groups = data.measures;
console.log("After insert");
console.log($scope.groups);
})
.error(function() {
});
};
$scope.loadAllMeasure();
$scope.submit = function (form) {
$http({
method: 'POST',
url: 'saveMeasure',
data: {
id: form.id,
name: form.name,
description: form.description
},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function(data, status, headers, config) {
$scope.loadAllMeasure();
}).error(function(data, status, headers, config) {
});
}
})
i am using the below format to get a JSON object from my localhost. The JSON is pretty complicated and lengthy so , using jquery to populate the HTML is getting complicated.
function processmsg(msg) {
var jobj = JSON.parse(msg);
if (typeof jobj === 'object')
{
// Parse the JSON
}
document.getElementById("messages").innerHTML = globalTag;
}
}
function waitForMsg() {
$.ajax({
type: "GET",
url: "1.json",
cache: false,
timeout: 50000,
success: function (data) {
processmsg(data);
if (!data) {
setTimeout(
waitForMsg,
1000
);
};
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
setTimeout(waitForMsg, 15000);
processmsg("error");
}
});
};
$(document).ready(function () {
waitForMsg();
processmsg("loading");
});
I would like to use the format like {{jobj.entries}}. something like this. This can be done on angularJS. can you guys please suggest me how to do the same in angular ?
i want to query the JSON every 1 min and when the data is found i want to cancel the interval. I dono how to do it in angularjs.
==================update================
i got below code snippet. It is working fine, But how do i stop the url query once the json object is obtained..
var app = angular.module('urlanalyzer', []);
app.controller('jsonController', function($scope, $http) {
$scope.getData = function(){
var url = "{% static "" %}tmp/{{url_hash}}/{{url_json}}";
$http.get(url)
.success(function(data, status, headers, config) {
console.log(data);
});
};
if (!$scope.data){
setInterval($scope.getData, 2000);
}
The issue here is the json object will be available after 3 sec only.
var app = angular.module('urlanalyzer', []);
app.controller('jsonController', ['$scope','$http','$timeout',function($scope, $http, $timeout) {
$scope.getData = function(){
var url = "{% static "" %}tmp/{{url_hash}}/{{url_json}}";
$http.get(url)
.success(function(data, status, headers, config) {
if(!data)
$timeout(function(){
$scope.getData()
}, 2000)
else{
$scope.myData = data.data? data.data:data;
$scope.showError = false;
}
})
.error(function(msg) {
$timeout(function(){
$scope.getData()
}, 2000)
$scope.processMessage(msg)
});
};
$scope.processMessage = function(msg){
if(angular.isString(msg))
$scope.errorMessage = msg;
else if(angular.isObject(msg)){
$scope.errorMessage = msg.message // the property you want;
}
$scope.showError = true;
}
$scope.getData();
}])
HTML:
<div ng-controller="jsonController">
<div ng-show="showError">
{{errorMessage}}
</div>
<div id="myDatacontainer">
//you can bind any propery of your data by using angular direvtives
//look at ng-bing, ng-if etc. directives
{{myData.name}} ...
</div>
</div>
Hope it help.
Consider you have following JSON data stored in a scope variable named data:
$scope.data = {
"array": [
1,
2,
3
],
"boolean": true,
"null": null,
"number": 123,
"object": {
"a": "b",
"c": "d",
"e": "f"
},
"string": "Hello World"
}
Then you write your HTML in the following way like:
<div>
Boolean: {{data.boolean}}
</div>
<div>
Number: {{data.number * 2}}
</div>
<div>
Array:
<p ng-repeat="(key, value) in data.object"> {{key}} : {{value}}</p>
</div>
Another way to bind <div ng-bind="data.string"></div>
Here you can stop your call. You can use enhanced angular service $interval for this:
$scope.getData = function(){
var url = "{% static "" %}tmp/{{url_hash}}/{{url_json}}";
$http.get(url)
.success(function(data, status, headers, config) {
console.log(data);
$interval.cancel($scope.intervalObject); // cancel the interval when data is loaded
});
};
if (!$scope.data){
$scope.intervalObject = $interval($scope.getData, 2000);
}
if (!$scope.data){
setInterval($scope.getData, 2000);
}
since $scope.data is not set it'll continue calling the request(since you are not setting $scope.data anywhere).
Edit: Also, use angularjs $interval since it's the angular way of using setInterval and it keeps track of the $digest cycle
I have a loader that I show while an async service call is completed, and simply want to hide the loader when complete. Here is my controller:
app.controller('DataController',
function($scope, DataService) {
// UI state
$scope.loading = true;
DataService.getData({ "count": 10 }).then(function(data) {
$scope.data = data;
// UI state
$scope.loading = false; // does not update ng-view
$scope.$apply(function() { // generates error
$scope.loading = false;
});
});
});
And the view:
<div ng-controller="DataController">
<div id="container">
<div>
{{ loading }}
</div>
<div class="spinner large" ng-show="loading"></div>
<div class="data-container" ng-show="!loading">
...
</div>
</div>
</div>
Note the the {{ loading }} value gets updated properly in the view. Using the wrapping $scope.$apply() call resulted in an error:
Error: [$rootScope:inprog]
UPDATE
As this might be promise-related, here's the promise generating getData() method from the DataService factory:
getData: function(params) {
var deferred = $q.defer();
APIService.data(params).then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And the last piece, the APIService.data() method:
data: function(params) {
var deferred = $q.defer();
$resource(endpoint + '/data/feed', {}, {
'query': {
method: 'POST',
headers: headers
}
}).query(params).$promise.then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
I would solve this by binding the show/hide directive to the data-property in the controller. It will be the same as false if the data is undefined.
<div class="spinner large" ng-hide="data"></div>
<div class="data-container" ng-show="data">
Try to use
$scope.$evalAsync(function() {
$scope.loading = false;
});
Found the issue - as this is in a Chrome Extension, I needed to include the Angular CSS CSP file, which includes the ng-hide class definition. Including that file resulted in the code working as expected. Thanks everyone for the help. More info:
https://docs.angularjs.org/api/ng/directive/ngCsp
Below I've got an angular app and controller where the controller have data access inside of it (bad idea, I know)
var app = angular.module('app',[]);
app.controller('HomeController',function($scope,$http){
$scope.people = null;
$scope.get = function() {
$http({
url: 'largeTestData.json',
method: 'GET'
}).then(function(data){
console.log('request successful, here is your data: ');
console.log(data['data']);
$scope.people = data['data'];
},function(reason){
console.log('this failed, this is the reason: ');
console.log(reason);
})
}
});
app.controller('ControllerWithService',function($scope, MyService){
$scope.get = MyService.get;
$scope.get(function(data){
console.log('you succeeded');
},function(reason){
console.log('you failed');
console.log(reason);
})
})
This will work in retrieving data and putting it onto the page. Knowing that having data Access in the controller is no bueno I tried to abstract that out into a service:
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
Here my 'data layer' is a service that only has one method: get from the above listed URL.
app.service('MyService',function($http,$q){
var get = function(){
var deferred = $q.defer();
var url = 'test.json';
$http.get(url).success(deferred.resolve).error(deferred.reject);
}
return {
get: get
}
})
and my HTML
<body>
<script src="libs/angular-1.2.15.js"></script>
<script src="app/app.js"></script>
<script src="app/DocumentService.js"></script>
<script src="libs/jQuery-2.1.1.js"></script>
<div ng-controller="HomeController">
<button ng-click="get()" href="#">Get data</button>
<div>{{message}}</div>
<!--<div ng-repeat="p in people" >-->
<!--<b>Business Doc ID: </b><h1>{{p['busDocId']}}</h1>-->
<!--<b>DOC ID: </b>{{p['docId']}}-->
<!--<b>FILE NAME: </b><div style="color: green">{{p['fileName']}}</div>-->
<!--</div>-->
</div>
<div ng-controller="ControllerWithService">
{{message}}
<button ng-click="get()">get data</button>
<div>{{data}}</div>
</div>
</body>
I'm not getting any error messages, and the commented out out stuff in my HomeController works as expected. What am I doing wrong in trying to make my AJAX calls a service?
working solution changes:
app.service('MyService',function($http,$q){
this.get = function(){
return $http.get('test.json')
}
})
app.controller('ControllerWithService',function($scope, MyService){
$scope.data = null;
$scope.get = function() {
MyService.get().then(function (data) {
console.log('this is the success data: ');
console.log(data)
$scope.data = data;
}, function (reason) {
console.log('this is the fail reason');
console.log(reason);
$scope.data = reason;
})
}
})
It looks like it could be a couple different things. I'll post an example I have working in one of my projects right now. It should be extremely similar and simple with what you're goal is.
Service:
'use strict';
angular.module('srcApp')
.service('Getlanguage', function Getlanguage($location, $http, $log, $state, $rootScope) {
this.getContent = function() {
var language = $location.path().split('/'),
languageCulture = language[1];
if (!languageCulture) {
languageCulture = 'en';
}
$rootScope.cultureCode = languageCulture;
return $http({method: 'GET', url: '/languages/' + languageCulture + '.json'})
.error(function() {
// If service cannot find language json file, redirect to index
$state.go('lang', {lang: 'en'});
});
};
});
Controller Call to service:
After passing in the service as a dependency into the controller.
Getlanguage.getContent().then(function(res) {
$scope.content = res.data;
});
Hope this helps.