Hi I'm learning AngularJS and I have a question. I want to display a table on a button click. On clicking the button, JSON data gets fetched but I have to press the button twice for the data to be displayed.
This is the HTML page.
<html>
<body>
<div class="container">
<label for="tags" style="margin-top: 30px;margin-left: 15px;">GSTIN </label>
<input id="tags">
<button ng-click="searchfunction()">search</button>
</div>
<br/>
<hr>
<div class="container">
<div ng-show="tshow" ng-repeat="x in searchdata">
<table class="table table-bordered table-responsive">
<thead>
<tr>
<th>MON</th>
<th>SGST</th>
<th>CGST</th>
<th>IGST</th>
<th>CESS</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in x">
<td>{{i.mon}}</td>
<td>{{i.sgst}}</td>
<td>{{i.cgst}}</td>
<td>{{i.igst}}</td>
<td>{{i.cess}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
This is the controller:
app.controller("searchcontroller", function ($scope,$http) {
$scope.tshow=false;
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
$scope.searchfunction=function() {
$scope.tshow=true;
var tf=document.getElementById("tags");
var value=tf.value;
var auth = make_base_auth("gstadmn112","Gstn#123");
var url6 = "http://164.100.148.67:8080/wsgstr3B/rest/payment/gstinsearch?gstin="+value+"&year=201718";
xml = new XMLHttpRequest();
// jQuery
$.ajax({
url : url6,
method : 'GET',
beforeSend : function(req) {
req.setRequestHeader('Authorization', auth);
},
success:function(response) {
console.log(response);
scope.searchdata=response;
},
failure:function() {
window.alert("wrong input data doesn't exist");
}
});
}
});
I need to click twice on the search button for the table to be displayed. I want the table to be hidden initially and once the search is successful the table should be displayed. The table is hidden initially and after clicking twice correct data gets displayed.
Maybe, you try to add $scope.tshow=true; in function success:
success:function(response) {
console.log(response);
$scope.tshow=true;
$scope.searchdata=response;
},
P.S. I advise to use $http instead of $ajax.
This problem is related to the digest loop of angularjs which keeps all changes sync between your view and controller.
When you invoke the searchfunction(), angularjs will know whats happening inside the method and sync the changes made with the view when its completed.
The problem is that your method uses $.ajax which has some async callback methods.
When these methods gets invoked angularjs have already left the party (digest loops is over) and don't know what these methods have done to your controller $scope.
The jQuery success callback will however set the $scope.searchdata=response; and this change gets notified the next time angularjs is in the party (the next time you click).
So basically you need to make sure angularjs is aware of the async methods which makes changes to your $scope.
To solve this I would inject angularjs own $http service (which takes care of async changes to the scope) and use that instead.
var req = {
method: 'GET',
url: url6,
headers: {
'Authorization': auth
}
}
$http(req).then(function(response){
console.log(response);
$scope.searchdata=response;
}, function(){
window.alert("wrong input data doesn't exist");
});
You can use this way.
$scope.searchfunction=function(){
$scope.tshow=true;
var tf=document.getElementById("tags");
var value=tf.value;
$http.get("http://164.100.148.67:8080/wsgstr3B/rest/payment/gstinsearch?gstin="+value+"&year=201718")
.success(function(result) {
$scope.searchdata=response;
$scope.tshow=false;
})
.error(function() {
window.alert("wrong input data doesn't exist");
});
}
Related
I have a modal window for creating new object As you can see there three forms: 1) simple input to create "Name";
2) A dropdown with "Types" ;
3) A dropdown with "Ids";
I need to get data with types and Ids from DB, and as I can see in the browser and logs this part of process is going well, but when I fill all the forms and try to send them, there is always appears an Error, it happens because on server in variables "Types" and "IDs" written data from variable "Name", in short words it sent the data from input to all variables. This is a main question - why do it write data from input to all variables?? This is the html:
<tr>
<td class="collapsing">
<label>{{localization.common[locale].name}}</label>
</td>
<td>
<input type="text" placeholder="" ng-model="Name">
</td>
</tr>
<tr ng-controller="VirtualConnectorAddCtrl">
<td class="collapsing">
<label>{{localization.common[locale].VTypeID}}</label>
</td>
<td>
<select class="ui dropdown VTypeID" ng-model="VTypeID">
<option ng-repeat="item in virtualType" value= "'{{item.Id}}'">{{item.Name}}</option>
</select>
</td>
</tr>
<tr ng-controller="VirtualConnectorAddCtrl">
<td class="collapsing">
<label>{{localization.common[locale].account}}</label>
</td>
<td>
<select class="ui dropdown RunAsId" ng-model="RunAsId">
<option ng-repeat="list in runAccount" value= "'{{list.RunAsAccountId}}'">{{list.RunAsAccountName}}</option>
</select>
</td>
....
<div class="ui button blue" ng-click="createData(modal_id, Name, VTypeID, RunAsId)">{{localization.common[locale].add}}</div>
I had the same problem with creating "Account"(you can see on the image on left side menu: VirtualTypes, Account, VirtualConnectors), and there the issue was about $http params in controller
$scope.createData = function (obj, data, extra) {
....
if ($scope.dictFilter == 'accounts') {
$http({
method: 'GET',
url: appID + '/Insert_Methods.asmx/Insert_RunAsAccount',
params: { name: data, pasword: data}
}).then(function successCallback(response) {
Modals.close(obj)
getData();
}, function errorCallback(response) {
});
return;
} ....
When I replace 'password:data' to 'password:extra' the problem was solved, but with "VirtualConnectors" I couldn't find the way to do this (all creating processes are initializes by function "createData", and I am trying to add new block to that function)
if ($scope.dictFilter == 'virtualConnectors') {
$http({
method: 'GET',
url: appID + '/Insert_Methods.asmx/Insert_VirtualConnector',
params: {name: data, VtypeID: data, RunAsID:data }
}).then(function successCallback(response) {
Modals.close(obj)
getData();
}, function errorCallback(response) {
console.log("function createData doesn't callback");
});
return;
}
maybe it's about params, in this case there three of them (actually 4, including obj "modal"), but function acccept only 2 arguments (actually 3 -including "obj"), the function works for other cases, and in other cases params do not always fit to arguments... In internet not much information about "params", so I can't understand the logic of binding html and controller here. Obviously, in html-file function CreateData take "modal_id", "Name", "VtypeID" and "RunAsId" as arguments so in the controller this function should be set with arguments that can take such types of data (obj, string, string, string), but there is (obj, data, extra), I've tried to change function arguments in the controller: $scope.createData = function (obj, data, extra, string) {... and it doesn't help... I think I should write another function for this case, but what arguments should I put? And one more thing: is it right to use form with options created by ng-repeat for such case (when I need to send a string that is a property of an object)?
why do it write data from input to all variables?
Because, you are assigning data to all properties
params: {name: data, VtypeID: data, RunAsID:data }
you should do,
$scope.createData = function (modal_id, name, vTypeID, runAsId) {
and then:
if ($scope.dictFilter == 'virtualConnectors') {
$http({
method: 'GET',
url: appID + '/Insert_Methods.asmx/Insert_VirtualConnector',
params: {name: name, VtypeID: vTypeID, RunAsID: runAsId } // <-- Check this line
}).then(function successCallback(response) {
Modals.close(obj)
getData();
}, function errorCallback(response) {
console.log("function createData doesn't callback");
});
return;
}
similarly, correct the params for 'accounts'.
There are several bad practices in your code as well:
using ng-controller on each <tr> tag. Use it on table level
Passing modal_id when it's not being used.
passing name in your case data as password. It doesn't make any sense to name variable incorrectly. IF its a name, you shouldn't use password to refer it.
I will suggest you to get your code reviewed by some expert to avoid "bad coding practices" that you are following in your code.
I hope my answer will help you :)
In a form, i have a dropdownlist and a textbox which will get user's input. Then when the user submits the form, i don't want the whole page to refresh. i only wanted the table which is just below it to refresh and load data from database. I tried code below but only alert pops up. The table is still empty.
$('#frmPrescDrugList').submit(function (e) {
e.preventDefault(); // Prevent Default Submission
$.ajax({
url: '/consultation/PrescDrugList',
type: 'POST',
data: $(this).serialize(), // it will serialize the form data
dataType: 'html',
success: function (data) {
var row_data = "";
row_data += "#{ var rowNum = 0; } #foreach (var item in Model.LoadDrugList) {
rowNum += 1;
<tr Class="odd gradeX">
<td>#rowNum</td>
<td>#item.dsubsystem</td>
<td>#item.tradename </td>
<td style="text-align: center; vertical-align: middle;"><form Class="form-horizontal" role="form" action="#Url.Action("Prescription", "consultation", new {dgid = item.dgid})" method="post"><Button type="submit" Class="btn btn-default" name="btnDrugListSelect">Select</Button></form></td>
</tr>
}";
$("#LoadDrugListTable").append(row_data);
alert('Drug list has been loaded.');
},
error: function () {
alert('Ajax Submit Failed ...');
}
}); // end ajax
}); // end form-submit
LoadDrugListTable is the table's id. Why didn't it load the data when the ajax is already successful? Can anyone please help point out my mistake or suggest me a better example reference?
I tried following this example, but i'm stuck at the #Url.Action(). Its underlined red. And when i try to separate them by "" and + it's still underlined red saying
Too many characters in character literal.
UPDATED
This is the form i mentioned that doesn't pass the id properly. I'm passing it to a get method. By the way, this is an entirely different page than the one i mentioned above. When i try to do the same thing in another page, it passes a null paid value. Paid is the id. Maybe because this time i'm passing to another page.
"<td style='text-align: center; vertical-align: middle;'><form Class='form-horizontal' role='form' action='#Url.Action("PatientMedicalInfo", "consultation", new { paid = Html.Raw(" + data[i].paid + ") }) method='get'><Button Class='btn btn-default' name='btnSelectPatient' id='btnSelectPatient'>Select</Button></form></td>"
This is the controller:
[HttpGet()]
public ActionResult PatientMedicalInfo(string paid)
{
PatientLookupVM Patient = new PatientLookupVM();
dynamic CurrentPatientDetailsByPtId = Patient.GetCurrentPatientDetailsByPtId(paid);
Patient.LoadCurrentPatientDetails = CurrentPatientDetailsByPtId;
return View(Patient);
}
Try replacing double " quotes with single ' quotes in action.
Well for consistency you could replace them everywhere.
<td ... action='#Url.Action("Prescription", "consultation", new {dgid = item.dgid})' ... </td>
I have a main View Model for my screen. It consists of 2 child view models.
One handles the registration section.
One handles the login section.
One handles the menu section (If authenticated and what menu items can appear, as well as the "Welcome "Username" type stuff).
$(document).ready(function () {
// Create the main View Model
var vm = {
loginVm: new LoginViewModel(),
registerVm: new RegisterViewModel(),
layoutVm: new LayoutViewModel()
}
// Get the Reference data
var uri = '/api/Reference/GetTimezones';
$.getJSON({ url: uri, contentType: "application/json" })
.done(function (data) {
vm.registerVm.Timezones(data);
});
// Bind.
ko.applyBindings(vm);
});
Once my Login model's "Login" method completes, I want to set the "IsAthenticated" value within the Menu model, as well as some other user info.
So in my login model, I have a SignIn method.
$.post({ url: uri, contentType: "application/json" }, logindata)
.done(function (data) {
toastr[data.StatusText](data.DisplayMessage, data.Heading);
if (data.StatusText == 'success') {
alert($parent.layoutVm.IsAuthenticated());
}
else {
}
})
.fail(function () {
toastr['error']("An unexpected error has occured and has been logged. Sorry about tbis! We'll resolve it as soon as possible.", "Error");
});
The alert code is my testing. I am hoping to access (and set) the IsAuthenticated property of the layoutVm model. That's one of the child models on my main View model.
However, "$parent" is not defined.
How can I update values in the layoutVm, from my loginVm?
$parent is part of the binding context, which is only available during the evaluation of the data-bind (i.e. to the binding handler).
In your viewmodel structure, you'll have to come up with a way to communicate between models yourself. For example, by passing parent view models, or by passing along shared observables. The problem you're describing can be solved by using data-bind="visible: $root.userVM.IsAuthenticated", like I answered in your previous question.
If you'd like to go with the other approach, here's an example on how to share an observable between viewmodels.
var ChildViewModel = function(sharedObs) {
this.myObs = sharedObs;
this.setObs = function() {
this.myObs(!this.myObs());
}.bind(this);
}
var RootViewModel = function() {
this.myObs = ko.observable(false);
this.vm1 = new ChildViewModel(this.myObs);
this.vm2 = new ChildViewModel(this.myObs);
this.vm3 = new ChildViewModel(this.myObs);
}
ko.applyBindings(new RootViewModel());
div { width: 25%; display: inline-block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="with: vm1">
<h4>vm1</h4>
<p data-bind="text: myObs"></p>
<button data-bind="click: setObs">
flip
</button>
</div>
<div data-bind="with: vm2">
<h4>vm2</h4>
<p data-bind="text: myObs"></p>
<button data-bind="click: setObs">
flip
</button>
</div>
<div data-bind="with: vm3">
<h4>vm3</h4>
<p data-bind="text: myObs"></p>
<button data-bind="click: setObs">
flip
</button>
</div>
Note that each of the child view models also have write permission, so you'll have to be careful to not accidentally update the observable
I'm new to angular and would like some help in solving the following issue. This is the code I currently have, simply getting an array of results from the server using a post request and displaying them using the ng-repeat directive:
<div id="mainW" ng-controller="MediaController">
<div id="mediaBodyW" ng-repeat="media in medias">
<div class="mediaW" data-id="{{media.id}}">
<div class="mediaNum">{{media.num}}</div>
<div class="mediaN">{{media.name}}</div>
<div id="loadSubs" ng-click="loadSubMedias(media.id)">load sub medias</div>
<div id="subMediaW"></div>
</div>
</div>
This is my controller:
app.controller("MediaController",['$scope','$http','$httpParamSerializerJQLike',function($scope,$http,$httpParamSerializerJQLike){
$scope.medias = [];
try {
$http({
method: 'POST',
url: 'media.php',
data: $httpParamSerializerJQLike({"request":"getAllMedia"}),
headers: {'Content-Type':'application/x-www-form-urlencoded'}
}).then(function (ans) {
$scope.medias = ans.data;
}, function (error) {
console.error(JSON.stringify(error));
});
}
catch (ex) {
console.error("Error: " + ex.toString());
}
}]);
Now, what I would like to achieve, is: on clicking the div with id of "loadSubs", run another $http post query which will load an array of results into the "subMediaW". Of course the query and appending of html should be unique for each media element, and each time a data is loaded for a particular element all previous results should be cleared, all this while taking into account that the loaded data will be also manipulated in the future.
Can someone please help me understand how can I do this using AngularJS?
Try this,
$scope.prevMedia = null; //use this to store last clicked media object
$scope.loadSubMedias = function(media){
$http.post().... //make api call for subMedia here
.then(function(res){
media.subMediaW = res.data // attach a subMediaW array to the media object
media.showSubMedia = true; // set variable true to make submedia visible for current media object, this will be used with ng-if attribute in html
if($scope.prevMedia != null) $scope.prevMedia.showSubMedia = false; // if last selected media object is not null, hide its submedia
})
}
and html
<div id="mainW" ng-controller="MediaController">
<div id="mediaBodyW" ng-repeat="media in medias">
<div class="mediaW" data-id="{{media.id}}">
<div class="mediaNum">{{media.num}}</div>
<div class="mediaN">{{media.name}}</div>
<div id="loadSubs" ng-click="loadSubMedias(media)">load sub medias</div>
<div id="subMediaW" ng-repeat="submedia in media.subMediaW" ng-if="media.showSubMedia"></div>
</div>
</div>
Firstly you should have a function in your controller with the name loadSubMedias and instead of simply taking media.id you can send whole media object to it (later on we will add new data into this object as an another property).
$scope.loadSubMedias = function (media) {
$http({
method: 'POST',
url: 'media.php',
data: $httpParamSerializerJQLike({"mediaId":media.id}),
headers: {'Content-Type':'application/x-www-form-urlencoded'}
}).then(function (response) {
// now add subDatas into main data Object
media.subMedias = response.data;
});
}
and in your controller just use ng-repeat like this
<div id="mainW" ng-controller="MediaController">
<div id="mediaBodyW" ng-repeat="media in medias">
<div class="mediaW" data-id="{{media.id}}">
<div class="mediaNum">{{media.num}}</div>
<div class="mediaN">{{media.name}}</div>
<div id="loadSubs" ng-click="loadSubMedias(media)">load sub medias</div>
<div id="subMediaW" ng-repeat="subMedia in media.subMedias">
<pre>{{subMedia | json}}</pre>
</div>
</div>
</div>
I have some ng-repeat
<tr ng-repeat="Picture in Pictures">
<td>{{Picture.Id}}</td>
<td>
<div ng-hide="Picture.editorEnabled">
{{Picture.Name}}
</div>
<div ng-show="Picture.editorEnabled">
<input ng-model="Picture.editName" class="form-control" ng-show="Picture.editorEnabled">
</div>
</td>
<td>
<button type="button" name="editButton" class="btn btn-warning" ng-hide="Picture.editorEnabled"
ng-click="enableEditor(Picture)">Edit
</button>
<div ng-show="Picture.editorEnabled">
<button type="button" name="saveEditButton" class="btn btn-success"
ng-click="saveEditor(editName,editImageUrl,Picture)">Save
</button>
<button type="button" name="cancelEditButton" class="btn btn-warning" ng-click="disableEditor(Picture)">
Cancel
</button>
</div>
</td>
</tr>
and here is my controller code:
$scope.enableEditor = function(picture) {
picture.editorEnabled = true;
picture.editName = picture.Name;
picture.editImageUrl = picture.ImageUrl;
};
$scope.disableEditor = function(picture) {
picture.editorEnabled = false;
picture.editName = null;
picture.editImageUrl = null;
};
$scope.saveEditor = function(name, url, picture) {
$.ajax({
url: 'admin/pictures/edit/' + picture.Id,
type: 'POST',
data: {
ImageUrl: url,
Name: name
},
success: function() {
picture.Name = picture.editName;
picture.ImageUrl = picture.editImageUrl;
$scope.disableEditor(picture);
}
});
};
when I click 'Edit' appears editing fields and Save,Cancel buttons.
when I click 'Cancel' it dissapears.
when I click 'Save' fields saves to DB, method disableEditor executes on same object, I checked Id property, and in debugger it showes that editorEnabled is false, but in my browser still exist Save,Cancel buttons and fields for editing. I click again on 'Save' and it dissapears.
You should try using $http instead of $.ajax. jQuery's $.ajax executes outside of Angular's scope. Any code in a callback would not be reflected on your scope because this occurs outside of Angular's digest cycle. Try the following instead:
var url = 'admin/pictures/edit/' + picture.Id;
var data = $httpParamSerializerJQLike({
ImageUrl: url,
Name: name
});
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
$http.post(url, data, config).then(function() {
picture.Name = picture.editName;
picture.ImageUrl = picture.editImageUrl;
$scope.disableEditor(picture);
});
For this to work, you'll need to inject the $http and $httpParamSerializerJQLike services into your controller. I'm assuming you need to submit this as form, since that's how $.ajax works by default. This requires Angular 1.4+.
Note, while it is possible to use $.ajax and then wrap your success code in a $timeout or $scope.$apply(), you're better off using Angular's own $http.
This does not work because everything that you do in $scope.saveEditor happens outside of angular (I assume $ is jQuery). This is simply not the angular way.
You should have a deeper look into https://docs.angularjs.org/api/ng/service/$http the angular HTTP service, and use that instead.
You need to use $apply every time you use something that is not "angular way", like Anid has already mentioned.
For example if you use jQuery's http instead of angular's $http, you will have to add $scope.$apply.