View Renders before Knockout Binds Data - asp.net-mvc

I am sure this has been asked before but alas I can't find it - any help to point me in the right direction would be greatly appreciated.
I am loading a view / view model with Durandal and during the Activate method I am calling for a single record to be fetched and then displayed. I am getting the record back (checked the XHR response and all data comes back ok) but I can only assume that the view is loading before the data is ready to display. No JS errors, no errors at all for that matter, but just a big whitespace in the middle of my screen, even where I know data should show...
ViewModel :
define(['durandal/system', 'services/logger', 'services/datacontext'], function (system, logger, datacontext) {
var aForm = ko.observable();
var initialized = false;
function activate(routeData) {
var id = parseInt(routeData.id);
return refresh(id);
};
function refresh(id) {
return datacontext.getaFormById(id, aForm);
}
var vm = {
activate: activate,
aForm: aForm,
title: "THE TITLE DISPLAYS JUST FINE"
}
return vm;
});
View :
<h3 class="page-title" data-bind="text: title"></h3>
<div class="container-fluid" data-bind="with: aForm">
<div class="row-fluid">
<div class="span12">
<strong>Testing</strong>
<strong data-bind="text: id"></strong>
</div>
</div>
<h2 data-bind="text: description"></h2>
<h2 data-bind="text: checkType().description"></h2>
</div>
I threw in the Testing strong and it doesn't even render, only the title does. Can anyone point me in the right direction? I would create a fiddle but it works when I hardcode the data in the ViewModel.
EDIT :
Ok after digging into it a bit more it is possible I am not returning the object properly, specifically in the data.entity return type. Would this cause the issue?
var getaFormById = function (aFormId, aFormObservable) {
var query = EntityQuery.from('aForms')
.where('id', '==', aFormId);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
if (aObservable) {
aObservable(data.entity);
}
logger.log('Retrieved selected [aForm] from remote data source', data, system.getModuleId(datacontext), true);
}
}

You are not correctly filling your observable in your executeQuery success callback that is why your UI is not rendered correctly.
From the executeQuery documenation the successFunction called with the following argument
successFunction([data])
data Object
results Array of Entity
...
So you need to fill your observable from the data.results array and not form data.entity (which just returns undefined):
if (aObservable) {
aObservable(data.results[0]);
}

You can tell Durandal to hold off binding by returning a jQuery promise() from the activate function.
See this video by Ryan Keeter for the explanation: http://www.youtube.com/watch?v=w_XjWg6xL8I

Related

Angular 7 - Copying a List returned from SQL into an Array

I would like to convert results from an SQL method to an array so that I can use the filter option on the array in Angular 7.
I am still getting my feet wet in Angular (7) and I'm trying to set up a nested dropdown/select list where the first select list is for "Departments" and the selected value will return a result set for my "DeptTypes" dropdown/select list.
I am currently returning the data by sending the selected value (id) call my "changeData(id: number)" event in the ...component.ts. It successfully returns data, but there is sometimes a problem with the dropdown not always populating. It appears that it is a performance or speed issue due to always making the call to the SQL backend. So, I would like to return the data as an array so that I can use the .filter command, but there seem to be no option for that. So, what would be the best solution to my lil' problem? I was thinking about pushing the returned list into an [] array variable that would then allow me to use "push" but I cannot figure out the coding to do that. If there is a better way, by all means, enlighten me. Thanks.
// the calls in my component.ts
opportunityList() { // this is the for the first <select ...>
this.dashboardService.getOpportunities()
.subscribe((opportunities: Opportunities) => {
this.opportunities = opportunities;
},
error => {
// this.notificationService.printErrorMessage(error);
});
}
typeList(id: number) { // nested <select ...> that needs to populate
this.dashboardService.getTypesByOpportunityId(id)
.subscribe((types: Types) => {
this.types = types;
},
error => {
// this.notificationService.printErrorMessage(error);
});
}
changedata($event) { // called after selecting a value in the 1st <select ...>
// to remove previous selected items from second dropdown
//this.finaldata.splice(0);
// filter items and pass into finaldata
//this.finaldata = this.type2.filter(x => x.areaId == $event.target.value);
this.typeList($event.target.value);
this.finaldata = this.types;
}
// the HTML
<label for="areaId">Departments</label>
<select id="areaId" (change)="changedata($event)" [(ngModel)]="opportunityList" class="form-control">
<option *ngFor="let opp of opportunities" [value]="opp.areaId">{{ opp.areaName }}</option>
</select><br />
<label for="typeId">Department Area Types</label>
<select id="typeId" class="form-control">
<option *ngFor="let typeobj of finaldata" [value]="typeobj.typeId">{{ typeobj.typeName}}</option>
</select>
my ...service.ts
getTypesByDepartmentId(id: number): Observable<Types> {
const headers = new Headers();
headers.append('content-Type', 'application/json');
const authToken = localStorage.getItem('auth_token');
headers.append('Authorization', `Bearer ${authToken}`);
return this.http.get(this.baseUrl + '/dashboard/GetTypesByDepartmentId/' + id, { headers })
.map(response => response.json())
.catch(this.handleError);
}
the controller action
#region api/dashboard/GetTypesByDepartmentId
[HttpGet("{id}")]
public async Task <IActionResult> GetTypesByDepartmentId([FromRoute]int id)
{
// retrieve all revenue types
var model = await (from t in _appDbContext.Type where t.AreaId == id
select new
{
t.TypeId,
t.TypeName
}).ToArrayAsync();
return Ok(model);
}
#endregion
The code above, thru the service.ts returns the results, it is not always populating the "typeId". It's a "hit or miss." Sometimes the data is there and sometimes it's just blank.
I would like to go with returning all of the department area types and using an array and the "filter" command. for example:
this.finaldata = this.typeList.filter(x => x.areaId == event.target.value);
in the component.ts itself or a more proper way to handle this issue since "filter" does not work on this call.
Looks like I get to answer my own question. I can actually filter my returned observable ... the issue was that I was defining a variable that used an interface, reflecting my model. If I remove that and make it "any", I can then push it to an array:
types: any;
type2: Types[] = [];
public finaldata: any[] = [];
typeList() {
this.dashboardService.getTypes()
.subscribe((types: Types) => {
this.types = types;
this.type2.push({ areaId: this.types.areaId, typeId: this.types.typeId, typeName: this.types.typeName, areaName: this.types.areaName });
},
error => {
// this.notificationService.printErrorMessage(error);
});
}
changedata($event) {
// to remove previous selected items from second dropdown
this.finaldata.splice(0);
// filter items and pass into finaldata
this.finaldata = this.types.filter(x => x.areaId == $event.target.value);
}
So, I just call my typeList() in my "ngInit()" and it is taken care of.

Passing data between angular controllers

Below is the sample code where I am trying to pass/sync the value between the two controllers. For the same in view I have a textbox and two place holders for showing the value of the textbox.
I have somehow achieved the behavior but still could not figure out why the code is not working in one place and doing well in another. I have mentioned 2 ways, one is working(second way) and one is not(first way).
I have used following:
1 service
2 controllers
1 view
Service
mcApp.factory('mcService', function($rootScope){
var service = {};
//variable 1
service.message = 'Default';
//function 1
service.getMessage = function(){
return this.message;
}
//function 2
service.setMessage = function(msg){
this.message = msg;
}
return service;
});
Controller - First way - Not working
mcApp.controller('mcController1', function ($scope, mcService) {
$scope.message = mcService.getMessage();
$scope.setmsg = function(msg){
mcService.setMessage(msg);
}
});
mcApp.controller('mcController2', function($scope, mcService){
$scope.message = mcService.getMessage();
});
View - First way - Not working
<div ng-app="mcApp">
<div ng-controller="mcController1">
<input type="text" ng-model="message" ng-change="setmsg(message)">
<p ng-bind="message"></p>
</div>
<div ng-controller="mcController2">
<p ng-bind="message"></p>
</div>
</div>
In above code I am updating the value of scope variable "message" by calling the service method "getMessage()" in "mcController2". However it is not getting updated in the view.
Below is the code where instead of directly using the service method "getMessage()" in "mcController2" I have assigned the service to the scope variable.
Controller - Second way - Working
mcApp.controller('mcController1', function ($scope, mcService) {
$scope.message = mcService.getMessage();
$scope.setmsg = function (msg) {
mcService.setMessage(msg);
}
});
mcApp.controller('mcController2', function ($scope, mcService) {
$scope.service = mcService;
});
View - Second way - Working
<div ng-app="mcApp">
<div ng-controller="mcController1">
<input type="text" ng-model="message" ng-change="setmsg(message)">
<p ng-bind="message"></p>
</div>
<div ng-controller="mcController2">
<p ng-bind="service.message"></p>
</div>
</div>
Please NOTE : Using the $rootScope.$broadcast in service and $scope.$on in controller in First way also gets the work done. But I am not able to figure out why Fisrt way is not working.
the first way not work because you pass the primitive object --- pass by value
-- pass by value if the old value has change the variable will not be reflect.
the second way worked well because you pass the complex object --- pass by reference .
-- pass by reference if the old value has change the variable will be reflect.
ex :
var x = {
name: "BeSaRa",
country: "Egypt",
like: [
'PHP',
'Javascript',
'MYSQL'
]
};
var v = x.name; // pass by value becouse the string is primitve object
var r = x.like; // pass by reference because the array is Complex Object
// try to change the name below this line
x.name = "Ahmed Mostafa";
console.log(v) // output --> "BeSaRa" it is not reflected by the change
// try to change the like property below this line
x.like.push("AngularJS"); // add AngularJS to the Array
console.log(r) // output ['PHP','Javascript','MYSQL','AngularJS'];
i hope you understand now :D
the primitives types in JavaScript:
Boolean
Null
Undefined
Number
String
Symbol (new in ECMAScript 6)
any thing else Complex :)

How/when to query 'extended' entities in Breeze

I have a ViewModel that loads up 'child' entities and I want to also display 'grandchild' entities based off of each child that is loaded. To simplify things, I need help identifying how to query these objects and display them under the proper 'tree' in the browser (apologies for my butchering of coding language : ))
I am using Knockout to bind the data and loading up the entities with Breeze. This question is an extension of When to add extend additional complex types onto a Breeze entity
Also - my model is EF code first and I have a configuration defining a one to many relationship between children and grandchildren, and I think Breeze knows this already but I am trying to figure out how to take advantage of this.
childs.js (view model)
var childs = ko.observableArray();
var grandChilds = ko.observableArray();
var parentId = ko.observable();
function refresh() {
var parentId = (parent).parentId; // << for ex. don't worry about this line : )
return Q.all([getChildren(), getGrandChildren()]);
}
function getChildren() {
return datacontext.getChildren(childs, parentId);
}
function getGrandChildren() {
return datacontext.getGrandChildren(grandChilds);
}
and in the view (childs.html)
<div data-bind="foreach: childs">
<div title="Go to Child Details">
<div><strong data-bind="text: name"></strong></div>
<div><strong data-bind="text: gender().description"></strong></div>
</div>
<div>
<!-- ko.compose { view: grandChilds} --><!--/ko-->
</div>
</div>
And my current datacontext for reference
var getChilds= function (childsObservable, parentId) {
var query = EntityQuery.from('Childs')
.where('parentId', '==', parentId)
.expand('grandChildren')
.orderBy('id');
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
if (childsObservable) {
childsObservable(data.results);
}
logger.log('Retrieved [Childs] and [Grandchilds] from remote data source', data, system.getModuleId(datacontext), true);
}
};
I want to load up only grandchildren of the children, and since there are many children I want to only display grandchildren under the correct child, not all grandchildren in one list. Any help would be appreciated.
You have three basic approaches to querying and "linking" related entities.
1) Use EntityQuery.expand
2) Use EntityAspect.loadNavigationProperty
3) Create a "broad" EntityQuery that just happens to encompass the entire graph and all of the parent/child relations will be automatically linked.

ASP.NET / MVC HTML ActionLink - how to pass returned Javascript value to ActionLink routeValue parm?

(target framework is 4.0)
I am implementing a feature to delete multiple records in a simple table. In order to do this, I am using simple HTML checkboxes with a Javascript function to loop through each one to find which are selected. The Javascript is returning an array of int values representing the record IDs to delete.
I am using an ActionLink to call all of this, and to ultimately return to the Delete function (with an int array as a parm) in my controller file to actually submit the deletions to the database.
My problem is, I don't know how to take the array I get back from the Javascript to place into the ActionLink, to pass it into the controller.
Here is the code with which I am working:
<p>
#Html.ActionLink("Add New", "Add") | #Html.ActionLink("Delete Selected", "Delete", new { id = "???" }, new { onclick = "return getProjectsToDelete()" })
</p>
<script type="text/javascript">
function getProjectsToDelete() {
var answer = confirm("Are you sure you wish to delete the selected projects?");
if (answer) {
var listToDelete = new Array();
var arrayCount = 0;
for (i = 0; i < document.projectList.deleteProject.length; i++) {
if (document.projectList.deleteProject[i].checked) {
listToDelete[arrayCount] = document.projectList.deleteProject[i].value;
arrayCount++;
}
}
return listToDelete;
}
}
</script>
If I understand you correctly, you want to know how to push back a collection of IDs to a controller?
In general, this would be done as a FORM and POST the form to the back end. I myself prefer to do this via AJAX but thats just me.
While you can do this using a GET and stuffing all of the IDs in the query string, its dirty!
It would look something like /controller/action?id=7&id=8&id=9
The Model Binder in .NET will convert that in to a List<int> or int[].
I would suggest using ".push" in Javascript to manage you array of IDs. You can also use the ".join" method on your array to create a clean querystring.

MVC update Dropdownlistfor with jquery

I have a Dropdownlistfor connected to my model. When the page is loaded the model is empty, but I have a button on the page that updates the model. My problem is that my dropdownlist doesn't update.
Markup:
#Html.DropDownList("ddlRaces", new SelectList(ViewBag.Races, "RaceId", "Name"))
<input type="button" id="btnChangeRace" class="btnGo" value=" " />
#Html.DropDownListFor(m => m.Timers, new SelectList(Model.Timers, "TimerId", "StartTime"), "Velg timer:")
Script:
btnChangeRace.click(function () {
url = "/TimeMerger/GetTimers/?raceId=" + ddlRaces.val();
$.get(url);
});
Codebehind:
[HttpGet]
public ActionResult GetTimers(int raceId)
{
var timeMergeModel = new TimeMergerModel();
timeMergeModel.Timers = TimerModel.GetTimers(raceId);
return View(timeMergeModel);
}
$.get(url);
Here you are sending an AJAX request but you are doing nothing in the success callback so nothing will update. There is not even a success callback. So you need to define a success callback:
$.get(url, function(result) {
// result here will represent the value returned by the server
// which in your case is HTML. So here you might need to update
// the relevant portion of your page.
});
Also your DropDownListFor definition is wrong. You are using the same model variable as first and second argument (Model.Timers). The first first argument of this helper should be a scalar property to which you are binding and not a list. It should be:
#Html.DropDownListFor(
m => m.SelectedTimerValue,
new SelectList(Model.Timers, "TimerId", "StartTime"),
"Velg timer:"
)
where SelectedTimerValue would be a property on your view model that will hold the selected value. Also why you are using DropDownList instead of DropDownListFor for the first drop down? The whole idea of a view model is that it will contain all the necessary information so that the view would simply display it.

Resources