angularjs bindings won't show unless i make a GET call - javascript

here is my situation:
I have an "edit" screen in my app. To load this screen, i am passing in an object through a window.postMessage event. I am listening for this postMessage event in my controller for the edit page and assigning the passed-in object to a property on my scope called "patient". Then, in the edit page markup, i am using ng-repeat to bind to an array on my "patient" object. The problem is, the page does not update to show the received data.
Here is what my event handler code looks like in my controller:
window.addEventListener("message", function(event) {
if (event.data.view === 'edit') {
$scope.patient = event.data.patient;
}
});
Here is what the markup looks like to bind to the patient object:
<div ng-repeat="dataItem in patient.fields | filter:{display:true}">
<div class="row" >
<div class="col-xs-12">
{{dataItem.displayName}}:
</div>
</div>
<div class="row" >
<div class="col-xs-12">
<input type="text" style="width: 100%;" ng-model="dataItem.value" /><br />
</div>
</div>
</div>
The data loads correctly and the $scope.patient property is getting fully populated, but the screen does not update. However, if I add a simple 'GET' call using RESTAngular, and don't even use the result of the call, the page updates correctly. Why is that and what can I do to get the page to update without the meaningless RESTAngular call? Here is the code that works with the RESTAngular call:
window.addEventListener("message", function(event) {
if (event.data.view === 'edit') {
patient = PatientRestangular.one('patients', event.data.patientId);
//this is a hack
//the data will not load on the edit screen without a call to 'get()'
patient.get().then(function(){
$scope.patient = event.data.patient;
});
}
});

Try $apply:
$scope.$apply(function(){
$scope.patient = event.data.patient;
});

Related

Why aren't my JS functions not being executed if the model of the page is populated with data?

I am developping a web app, using ASP .NET Core 2.0
I have a partial view that contains a form.
Then I have an action method inside a controller that is executed through a JS function, that uses AJAX.
This action method populates a model and returns the form partial view and corresponding model.
The function works well, as long as the model is not populated.
I need to fetch a record from the database when a determined field is filled and then loses focus. This works for the first time, when the model used in the form is empty. However, after finding a record for the first time and filling the form, the function is not executed anymore.
Also, the validations of the field also don't work after a record is loaded.
Here's the form partial view:
<form id="myform">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group col-lg-2">
<label asp-for="Client.Name">Client</label>
<input asp-for="Client.Name" id="ClientName" />
</div>
<div class="form-group col-lg-1">
<label asp-for="Cliente.Phone"></label>
<input asp-for="Cliente.Phone" id="Phone"/>
</div>
</form>
The JS/AJAX function:
<script>
$("form").on("blur", "#Phone", function () {
var phone = document.querySelector('#Phone').value;
alert('select client by phone=' + phone);
//Send the JSON array to Controller using AJAX.
$.ajax({
type: "POST",
url: "/Home/GetClienteByPhone",
data: "phone=" + phone,
success: function (res) {
$('#myform').html(res);
}
})
});
</script>
Finally, the controller action method (Controller=Home):
public IActionResult GetClienteByPhone(string phone)
{
if (!string.IsNullOrEmpty(phone))
{
var model = new FormViewModel();
model.Client = _db.Clients.Where(c => c.Phone == phone).FirstOrDefault();
return PartialView("_Form", model);
}
return PartialView("_Form");
}
On my View, I render the partial view into a div with id="myform"
Anyone knows why?
Thanks
It seems you're replacing your entire form in the AJAX callback. When you do that, you're also killing any attached event handlers. If you need the event handler to persist, you need to delegate it to a parent element that remains on the page after the replacement. You are using jQuery's delegate handler function, but your delegate itself is being replaced. You either need to not replace the entire form and just replace some child inside, or use a higher parent element than your form.

Multiple data objects in Ractive template

I am in the middle of the development of a standalone widget in jQuery and RactiveJS template. Initially the widget is opened with dynamic input form elements and these elements are populated with AJAX call. Here the email is a static field. This is the initial view for the user. When a user clicks on a button in the widget, it will validate the form and send another AJAX-POST call with validated data and show the response of the request on a div inside the widget template itself. If the POST call fails, some error message should be displayed in the same div. I have successfully implemented the initial view of the widget and validation. Below is my code:
Template
<div> <!-- all the mustache {{}} variables are coming from the loadData() ajax call -->
{{#partial widget-header}}
<header>
<div >
<img alt="logo"><span>{{clientID}}</span>
</div>
</header>
{{/partial}} {{>widget-header}}
<section>
<div>
<div>
<form>
<span>Complete required fields </span> {{#partial mandatory}}
<em>*</em> {{/partial}}
{{#each items}}
<div>
<div>
<label>{{dynamicField}}</label>{{>mandatory}}</div>
<div>
<input type="text" name="{{dynamicField}}" maxlength="20">
<div>{{dynamicFieldHelp}}</div>
</div>
</div>
{{/each}}
<div >
<div>
<label>Your Email Id</label>{{>mandatory}}
</div>
<div >
<input type="text" name="email">
<div>enter your email</div>
</div>
</div>
<div >
<input type="button" value="Submit Form" on-click="formValidate">
</div>
</form>
</div>
</div>
</section>
</div>
JavaScript
this.ractive = new Ractive({
el: 'widgetContent',
template: mainTemplate,
data: function loadData() {
$.ajax({
async: false,
url: "https://example.com/xyz/" +employeeNo,
success: function (response) {
initialData = response.payload[0];
}
});
return initialData; // return the dynamic form elements in data field.
},
oncomplete: function () {
self.center();
}
});
this.ractive.on({
formValidate: function (ev) {
validate the form and send AJAX-POST call then display the response data in a div -
this is where I am stuck..
},
});
But the problem I am facing here in the second AJAX-POST call. I am not able to use a second data field in the ractive initialization. How can I implement this part ? If I use a second data field in the ractive, it will not call the first AJAX call for displaying the form elements. So my understanding is like only one data field can be added in the ractive initialization.
Do I need to use any advanced ractive concepts such as components to implement this part? Can someone help me on this.
note:- I haven't added divs for result handling in the template since I am stuck
You don't want two data members; what you want is for your data to contain a separate field for the response data. Based on what you have, data should initially be something like:
{
items: [],
response: 0
}
I would add an oninit function where you do the initial AJAX call, then when you get the response do ractive.set("items", items) to set it. Using set will cause Ractive to automatically update the view with your new items.
Next, in your formValidate function, make your AJAX call. When the response comes back, again use set to notify Ractive of the change: ractive.set("response", response). Add your div to the template:
{{#response}}
<div>{{response}}</div>
{{/}}

Display alert message after page reload

I have to call $route.reload(); in controller 2 addData API call so that I can get the added data in my UI. But then the text 'Data added successfully' goes away due to page refresh.
controller 1:
$rootScope.$on('reloaded', function(event, data) {
$scope.$parent.alerts.length=0;
$scope.$parent.alerts.push({type: 'success',msg: 'Data added successfully'});
});
controller 2:
$scope.add = function() {
someAPIService.addData(JSON.stringify($scope.rows)).success(function(response) {
ngDialog.close('ngdialog1');
$route.reload();
$rootScope.$emit('reloaded', "true");
});
}
HTML Part:
<section>
<div ng-controller="controller3">
<alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</alert>
</div>
<div class="row form-inline" ng-controller="controller2 as vm">
<!-- Table data with text fields-->
</div>
<script type="text/ng-template" id="addDataDialog">
<div id="frm" ng-controller="controller1" class="col-xm-6">
<div class="form-group">
<!-- Labels with text fields-->
</div>
<button class="ngdialog-button" ng-click="add()">Save</button>
</div>
</script>
</section>
NOTE: Both controllers are in the same JS file and they are used for the same HTML file.
As you want to only load the latest record list which is on server side. Then there is not need to use $route.reload(); after making post call. You need to only make ajax call to get the latest records list that will solve your problem. For making ajax you need to refer $http
$route.reload() is getting used only when you need to load
controller again with specified template in your $routeProvider when
condition
just a simple hint of an answer:
add a (specific) cookie (with javascript) before calling reload (use e.g Cookies lib),
read it on reload,
and then delete it
Also you may use local storage if it is available instead of a cookie using same steps:
add a specific entry in local storage, before calling reload
read it on reload
and then delete it
Another option to maintain state client-side is to use url parameters but these will not do for page reload

jQuery Mobile navigate - Why is the state empty?

I'm using $.mobile.navigate("#test-page", {id:123}) to navigate to a secondary page.
The navigation from page to page works fine.... but the state is empty!
The docs clearly show that the state should contain all information I need when the navigation is performed.
This is the code I'm using:
$(window).on('navigate', function(event, data) {
console.log("navigated", data);
console.log(data.state.info);
console.log(data.state.direction);
console.log(data.state.url);
console.log(data.state.hash);
if (data.state.hash === "test-page") {
console.log("Test page", data.state.id);
}
});
Unfortunately data is passed as empty:
{
state:{}
}
The HTML is the following:
<div id="test-home" data-role="page">
<div data-role="header">
<h1>Test Home</h1>
</div>
<div data-role="content">
<div id="test-btn">
Click DIV for TEST page
</div>
</div>
<div data-role="footer">
</div>
</div>
<div id="test-page" data-role="page">
<div data-role="header">
<h1>Test Page</h1>
</div>
<div data-role="content">
Test page
</div>
</div>
Hope that someone can help. Thanks!
$.mobile.navigate and navigate event, are used to track URL history and pass/fetch data from URL. They work with browser's navigation (back / forward).
To pass data between pages dynamically within a webapp using internal navigation, use $.mobile.changePage.
Resources:
$.mobile.navigate()
Navigate
$.mobile.changePage()
Use the below code to pass data from page to another.
$.mobile.changePage('store.html', {
dataUrl: "store.html?id=123",
data: {
'id': '123'
},
reloadPage: true // force page to reload
});
To retrieve data
$('.selector').on('pagebeforeshow', function () {
var values = $(this).data("url").split("?")[1];
id = values.replace("id=", "");
console.log(id);
});
I know it is an old question, but #Omar's answer can be improved.
In fact, it is possible to use pagecontainerbeforehide, pagecontainerbeforeshow, pagecontainerhide, pagecontainershow, pagecontainertransition and pagecontainerchange (they are fired in this order) and inside the handler you can read the property history.state, that at that point is updated with the new state.
So, for example, you can write (to initialize things that need the page already formatted, e.g. Google Maps):
$(document).on("pagecontainershow", function(e, data) {
console.log("pagecontainershow: " + JSON.stringify(history.state));
});
This works in all cases: if you click on a link (with a hash, e.g. #user), if you navigate with the back and forward buttons, if you use $.mobile.navigate and also for window.history.back().
Moreover, you can pass complex data, not limited to the query string constraints.
Resources:
Pagecontainer
History

How to auto focus a field?

I'm writing a small chat client/server app with KnockoutJS and Node.js, everything is good, except for the fact, that after I send a message, I lose focus on the message field, and users have to reclick it everytime they want to type (very annoying). Do you guys know what I can do? Here is the template:
<script type="text/html" id="chatRoom">
<div id="chatContainer" class="chatContainer">
<div class="chatFrom">
<i id="chatClose" class="chatSprite chatClose" data-bind='click: function() { server.removeChat(this) }'></i>
</div>
<div class="chatMessages">
<ul id="chatHolder">
{{each messages()}}
<li><div class="chatFromText">From: ${ from }</div>
<div class="chatTime">${ time }</div><div class="chatMsg">${ text }</div></li>
{{/each}}
</ul>
</div>
<div class="chatControls">
<form data-bind="submit: function() { send($('#'+channel).val()); $('#'+channel).focus(); }">
<input type="text" id="${ channel }" name="message" class="chatText" style="color: #999;" value="Message Here" data-bind='click: function() {
$("#"+channel).val("").css("color", "#000");
}' />
<i class="chatSprite chatSend" data-bind="click: function() { $('.chatSend').parent().submit() }"></i>
</form>
</div>
</div>
</script>
As you can see I have tried every possible way of focusing the field, but none seem to work. Any suggestions?
I think that your issue is likely that your "send" method does an asynchronous post back to the server, in the success callback it probably pushes the message to your messages observableArray. When this happens your template is re-rendered and your focus is lost. So, this happens after your $('#'+channel).focus() call, because the send completes asynchronously.
Can't be sure without seeing your send function.
One option would be to pass "channel" as another parameter to your "send" function, then in the success callback for your AJAX request after pushing the message to your messages observableArray set the focus based on channel.
Sample here: http://jsfiddle.net/rniemeyer/h2A6p/
Knockout now has a hasfocus binding.
Combining Aran's and Arun's answers, the easiest way that works for me is:
<input id="channel" type="text" data-bind="hasfocus: true" />
The reason is that your send function being asynchronous,
Either set async = false, if you are using ajax
Alternately,
you can use a view model property to hold a boolean value and use the hasfocus binding
function chatVM()
{
this.focus = ko.observable(true);
}
var vm = new chatVM();
And then inside your submit function
set vm.focus(true);
OR
set hasfocus of your message box to true always.
div class="msgbox" data-bind="hasfocus: ko.observable(true)"></div>

Categories