There seems to be a bug where model data fetched from an http call is present in the $scope but not in a directive. Here is the code that illustrates the problem:
Jsfiddle: http://jsfiddle.net/supercobra/hrgpc/
var myApp = angular.module('myApp', []).directive('prettyTag', function($interpolate) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
var text = element.text();
//var text = attrs.ngModel;
var e = $interpolate(text)(scope);
var htmlText = "<b>" + e + "</b>";
element.html(htmlText);
}
};
});
function MyCtrl($scope, $http, $templateCache) {
$scope.method = 'JSONP';
$scope.url = 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero';
$scope.fetch = function () {
$scope.code = null;
$scope.response = null;
$http({
method: $scope.method,
url: $scope.url,
cache: $templateCache
}).
success(function (data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function (data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}
The HTML
<div ng-controller="MyCtrl">
<h1>Angular $http call / directive bug</h1>
<p>This fiddle illustrates a bug that shows that model w/ data fetched via an http call
is not present within a directive.</p>
<hr>
<h2>HTTP call settings</h2>
<li>Method: {{method}}
<li>URL: {{url}}
<br>
<button ng-click="fetch()">fetch</button>
<hr/>
<h3>HTTP call result</h3>
<li>HTTP response status: {{status}}</li>
<li>HTTP response data: {{data}}</li>
<hr/>
<h2>Pretty tag</h2>
<pretty-tag>make this pretty</pretty-tag>
<hr/>
<h3 style="color: red" >Should show http response data within pretty tag</h3>
[<pretty-tag>{{data}}</pretty-tag>] // <=== this is empty
</div>
Jsfiddle: http://jsfiddle.net/supercobra/hrgpc/
Any help appreciated.
You are replacing the content of the directive in your directive implementation. Since the $http request is async, the directive completes before the data is retrieve and assigned to the scope.
Put a watch on data variable inside the directive and then re-render the content, something like
scope.$watch(attrs.source,function(value) {
var e = $interpolate(text)(scope);
var htmlText = "<b>" + e + "</b>";
element.html(htmlText);
});
Based on #Marks feedback and your request i have update fiddle
http://jsfiddle.net/cmyworld/V6sDs/1/
Related
I’ve been playing around with Upload file - Streaming method. The original code, here:
https://github.com/aspnet/Docs/tree/master/aspnetcore/mvc/models/file-uploads/sample/FileUploadSample
However, I’m trying to get the data in the value attribute of <input value=” ”> using Angular, the idea is that I can POST the value into my MVC model instead of whatever is typed by the user (as in the original code). So, I have done this change to the input value property.
Streaming/Index.cshtml:
<div ng-app="myApp">
<div ng-controller="myCtrl">
..
<input value="#Model.name” type="text" name="Name" ng-model="name"/>
..
<button ng-click="createUser()">Create User</button>
..
</div>
</div>
#section scripts{
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="~/js/app.js"></script>
}
However, with Angular code running under app.js, the following piece of code actually fails with status code 400. This is because the passed value is “” and not the data under of value attribute of the HTML input tag.
App.js:
var User = (function () {
function User(name) {
this.name = name;
}
return User;
}());
var myApp = angular.module('myApp', []);
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
myApp.service('userService', ['$http', function ($http) {
this.createUser = function(user) {
var fd = new FormData();
fd.append('name', user.name);
return $http.post('/streaming/upload', fd, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined
}
});
};
}]);
myApp.controller('myCtrl', ['$scope', 'userService', function ($scope, userService) {
$scope.createUser = function () {
$scope.showUploadStatus = false;
$scope.showUploadedData = false;
var user = new User($scope.name);
userService.createUser(user).then(function (response) { // success
if (response.status == 200) {
$scope.uploadStatus = "User created sucessfully.";
$scope.uploadedData = response.data;
$scope.showUploadStatus = true;
$scope.showUploadedData = true;
$scope.errors = [];
}
},
function (response) { // failure
$scope.uploadStatus = "User creation failed with status code: " + response.status;
$scope.showUploadStatus = true;
$scope.showUploadedData = false;
$scope.errors = [];
$scope.errors = parseErrors(response);
});
};
}]);
function parseErrors(response) {
var errors = [];
for (var key in response.data) {
for (var i = 0; i < response.data[key].length; i++) {
errors.push(key + ': ' + response.data[key][i]);
}
}
return errors;
}
The solution must be a simple one, but after much research, I haven’t been able to find out how to modify it to make the data in the value=’’” attribute being passed across. This might be a stupid question but a headache for me however since I’m a total newbie regarding Angular. Please have some mercy, help.
Thanks
Use the ng-init directive to initialize the model:
<input ng-init="name= #Model.name" type="text" name="Name" ng-model="name"/>
console log
angular.module('Kinder.pages.member')
.controller('MemberInfoCtrl', MemberInfoCtrl);
function MemberInfoCtrl($scope,MemberModel,Constants,fileReader,$filter,AppUtils,$http,toastr,API) {
var vm = this;
vm.member = {};
vm.member.memberType = "1";
vm.member.memberSex = "1";
vm.member.avatarPath = "";
$scope.getFile = function () {
fileReader.readAsDataUrl($scope.file, $scope);
var fileInput = document.getElementById('uploadFile').files[0];
if(!AppUtils.isUndefinedOrNull(fileInput)){
var formData=new FormData();
formData.append("picUrl",fileInput);
$http({
........
}).success(function(data, status) {
console.log(data);
if(data.stat == 'success'){
//vm will have avatarPath value in there
console.log(vm);
vm.member.avatarPath = data.path;
//vm will have avatarPath value in there
console.log(vm);
}
}).error(function(data, status) {
});
}
};
$scope.submit = function() {
//vm had loss avatarPath ...
console.log(vm.member);
};
}
I do the function is to upload pictures before the preview, upload to the server to return to the path, assigned to vm。
But I can not get the avatarPath value outside the $ scope.getFile method。
I suspect that the problem of the scope of the problem, but I can not find a solution, I am the angular novice。
So who can tell me what this is for the reason。
I used google translation to describe the above questions,
I do not know if you understand me。。。
Anyway, thank you for taking the time to browse this question!
i fix it!
thanks for #JayantPatil reminders!
i am declaring controller in the route
.state('member.info', {
url: '/info/{memberId:string}',
params: {
memberId: null
},
templateUrl: 'app/pages/member/info/member-info.html',
controller: 'MemberInfoCtrl as vm'
});
and in the html , i use the ng-controller
<div class="userpic" ng-controller="MemberInfoCtrl as vm">
<input type="file" ng-show="false" id="uploadFile" ng-file-select/>
</div>
cause i duplicate declarations the controller , maybe the Scopes is Different,
that is all
Ok so there is a lot out there about sharing data between controllers using factory/service, but I'm not finding what applies to my issue. Either I'm interpreting the answers incorrectly or this is a valid question I'm about to ask! Hopefully the latter.
I would like controller 2 to recognize when a new http call has been made and the factory images object has been updated. at the moment it resolves once and then ignores any subsequent updates. What am i overlooking?
My view:
<div>
<ul class="dynamic-grid" angular-grid="pics" grid-width="150" gutter-size="0" angular-grid-id="gallery" refresh-on-img-load="false" >
<li data-ng-repeat="pic in pics" class="grid" data-ng-clock>
<img src="{{pic.image.low_res.url}}" class="grid-img" data-actual-width = "{{pic.image.low_res.width}}" data-actual-height="{{pic.image.low_res.height}}" />
</li>
</ul>
</div>
factory:
.factory('imageService',['$q','$http',function($q,$http){
var images = {}
var imageServices = {};
imageServices.homeImages = function(){
console.log('fire home images')
images = $http({
method: 'GET',
url: '/api/insta/geo',
params: homeLoc
})
};
imageServices.locImages = function(placename){
console.log('fire locImages')
images = $http({
method: 'GET',
url: '/api/geo/loc',
params: placename
})
};
imageServices.getImages = function(){
console.log('fire get images', images)
return images;
}
return imageServices;
}]);
controller1:
angular.module('trailApp.intro', [])
.controller('introCtrl', function($scope, $location, $state, showTrails, imageService) {
// run the images service so the background can load
imageService.homeImages();
var intro = this;
intro.showlist = false;
intro.data = [];
//to get all the trails based on user's selected city and state (collected in the location object that's passed in)
intro.getList = function(location) {
intro.city = capitalize(location.city);
intro.state = capitalize(location.state);
//get placename for bg
var placename = {placename: intro.city + ',' + intro.state};
imageService.locImages(placename);
... do other stuff...
controller2:
angular.module('trailApp.bkgd', [])
.controller('bkgdCtrl', ['$scope','imageService', 'angularGridInstance', function ($scope,imageService, angularGridInstance) {
$scope.pics = {};
imageService.getImages().then(function(data){
$scope.pics = data;
console.log($scope.pics);
});
}]);
Your controller2 implementation only got the images once, you probably need a $watch to keep to updated:
angular.module('trailApp.bkgd', [])
.controller('bkgdCtrl', ['$scope','imageService', 'angularGridInstance', function ($scope,imageService, angularGridInstance) {
$scope.pics = {};
$scope.$watch(function(){
return imageService.getImages(); // This returns a promise
}, function(images, oldImages){
if(images !== oldImages){ // According to your implementation, your images promise changes reference
images.then(function(data){
$scope.pics = data;
console.log($scope.pics);
});
}
});
}]);
I have a service that make some calls to retrieve data to use in my app. After I've loaded data, I need to call another service to make some operations on my data. The problem is that second service will not have access to the data of the first service.
I've made a plunker: plunkr
First service
app.factory('Report', ['$http', function($http,$q){
var Authors = {
reports : [],
requests :[{'url':'data.json','response':'first'},
{'url':'data2.json','response':'second'},
{'url':'data3.json','response':'third'}]
};
Authors.getReport = function(target, source, response, callback) {
return $http({ url:source,
method:"GET",
//params:{url : target}
}).success(function(result) {
angular.extend(Authors.reports, result)
callback(result)
}
).error(function(error){
})
}
Authors.startQueue = function (target,callback) {
var promises = [];
this.requests.forEach(function (obj, i) {
console.log(obj.url)
promises.push(Authors.getReport(target, obj.url, obj.response, function(response,reports){
callback(obj.response,Authors.reports)
}));
});
}
return Authors;
}])
Second service
app.service('keyService', function(){
this.analyze = function(value) {
console.log(value)
return value.length
}
});
Conroller
In the controller I try something like:
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports,keyService) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
});
$scope.test = function(value){
keyService.analyze($scope.report.about);
}
I think this is what you are going for? Essentially, you want to call the second service after the first succeeds. There are other ways of doing this, but based on your example this is the simplest.
http://plnkr.co/edit/J2fGXR?p=preview
$scope.result = Report.startQueue('http://www.prestitiinpdap.it', function (response,reports) {
$scope.progressBar +=33;
$scope.progress = response;
$scope.report = reports;
$scope.test($scope.report.about); //added this line
});
$scope.test = function(value){
$scope.example = keyService.analyze(value); //changed this line to assign property "example"
}
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p>Progress notification : {{progress}}!</p>
<div ng-show="show">
<progress percent="progressBar" class="progress-striped active"></progress>
</div>
<pre>{{report}}</pre>
<pre>{{report.about}}</pre>
{{example}} <!-- changed this binding -->
</body>
I have a function inside a $scope which updates a variable inside the $scope - my problem is that the variable is being "restored" to its original value once the function ends:
function MyCtrl($scope, $http, $templateCache) {
$scope.changeWeek = function (direction) {
console.log(direction);
console.log($scope.firstdayofweek);
var d = new Date();
direction == '7' ? d.setDate($scope.firstdayofweek.getDate() + 7) : d.setDate($scope.firstdayofweek.getDate() - 7);
$scope.firstdayofweek = d;
console.log(d);
console.log($scope.firstdayofweek);
}
$scope.firstdayofweek = getMonday(new Date());
$http({ method: 'GET', url: '/TimeSheet/Services/GetSubEmployees.ashx', cache: $templateCache }).
success(function (data, status) {
$scope.status = status;
$scope.fullname = data.fullname;
$scope.accountname = data.accountname;
$scope.domain = data.domain;
$scope.subemployees = data.subemployees;
$scope.domain == 'IL' ? $scope.firstdayofweek.setDate($scope.firstdayofweek.getDate() - 1) : $scope.firstdayofweek.setDate($scope.firstdayofweek.getDate());
}).
error(function (data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
}
The markup:
<div class="row" >
<div class="span1"></div>
<div ng-repeat="n in [] | range:7" class="span1">
{{(firstdayofweek.getDate()+$index)+'/'+(firstdayofweek.getMonth()+1)}}
</div>
<div class="span1"></div>
</div>
$scope.firstdayofweek is updated as expected inside the function (+-7 days) but {{firstdayofweek}} shows the updated date of "now" everytimg I call the function. (The console shows the right updated date)
I'm guessing the controller is refreshed every time - any way I can stop it from happening?
Thanks!