I've got a view that needs to be populated conditionally.
Scenario:
A manager will select a users name on screen B, then will be navigated to the same form the user filled in EG. screen A, except that the said manager will have the option to approve or deny the request of the user.
I've seen that in my VM on screen A I can do the following.
var vm = {
activate: function (data) {
console.log(data);
var id = data.id || -1;
if (id !== -1) {
router.isNavigating(true);
http.json('/api/user/'+ id )
.done(function (response) {
ko.viewmodel.updateFromModel(vm.userInfo, response);
router.isNavigating(false);
});
}
}
};
And then B (view & view model)
view
<table>
<thead>
<tr>
<td>User</td>
<td>Date Requested</td>
<td>Action</td>
</tr>
</thead>
<tbody>
<tr>
<td>Mike</td>
<td>19 Jun 2013
</td>
<td>
<input type="button" value="Go" data-bind="click: function() { buttons.goTo(6) }" />
</td>
</tr>
</tbody>
</table>
viewmodel
define(['durandal/plugins/router'], function (router) {
var buttons = {
goTo: function (id) {
console.log('goTo clicked');
//this does work in conjunction with my code on B
router.navigateTo('#/userinfo?id=' + id);
}
};
var vm = {
buttons: buttons
};
return vm;
});
My issue is that I'm not sure what the best way/or how to for that matter to get Durandal to navigate to page A from B... Is what I'm doing right? As it feels a little bit "hacky"
The navigation, at least to me, is designed to mimic standard MVC web navigation. In this case, since you already know that you want to go to 6, why not use an anchor like so
<a href="#/userinfo?id=6"/>
A better way would be to register your route with an id splat like so your route would become
routes.map({route: 'userinfo/:id, ...
<a href="#/userinfo/6" />
This way you can access the splat on the activate method..there are several examples out there but I don't have links to them. Basically the activate method of your userinfo viewmodel will accept a parameter and from there you can load an entity or whatever you like. Hope this helps.
Brad
Related
I am using x.PagedList to use pagination in my ASP.NET MVC page. The only problem I have with the plugin is , it used a page refresh when I navigate between pages.
To avoid that I am using jQuery calls to replace page contents as explained in this article.
My View and javascript looks like this.
<div id="circuitsContent">
<table class="table">
<tr>
--Header
</tr>
#foreach (var item in Model)
{
<tr>
--Loop through and create content
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
#Html.ActionLink("Details", "Details", new { id = item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
</tr>
}
</table>
</div>
<div id="circuitContentPager">
#Html.PagedListPager((IPagedList)Model, page => Url.Action("Circuits", new { page }))
</div>
#section scripts
{
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$(document).on("click", "#circuitContentPager a[href]", function () {
$.ajax({
url: $(this).attr("href"),
type: 'GET',
cache: false,
success: function (result) {
$('#circuitsContent').html(result);
}
});
return false;
});
});
</script>
And this is my controller code:
public ActionResult Circuits(int? page)
{
var pageNumber = page ?? 1;
var circuits = _repo.GetAllCircuits().OrderBy(circ=>circ.ID).ToList();
var pagedCircuits = circuits.ToPagedList(pageNumber, 25);
return View(pagedCircuits);
}
What am I missing here?
Your ajax call returns the html from Circuits() method which is the same view you have used to render the page initially, which includes all the initial html, but you only replacing part of of the existing page, so elements such as the paging buttons generated by the #Html.PagedListPager() method are going to be repeated. Your also generating invalid html because of duplicate id attributes (you will have multiple <div id="circuitsContent"> elements
There are 2 ways you could solve this.
Create a separate controller method that returns a partial view of just the <table> and call that method, however you would need to extract the value of the page number for the href attribute of you pager buttons to pass that as well.
Using your current Circuits() method, test if the request is ajax, and if so, return a partial view of just the <table>.
public ActionResult Circuits(int? page)
{
var pageNumber = page ?? 1;
var circuits = _repo.GetAllCircuits().OrderBy(circ=>circ.ID);
var pagedCircuits = circuits.ToPagedList(pageNumber, 25);
if (Request.IsAjaxRequest)
{
return PartialView("_Circuits", pagedCircuits);
}
return View(pagedCircuits);
}
Note: Do not use .ToList() in your query. That is defeating the whole purpose of using server side paging because .ToList() immediately downloads all the records fro the database.
Where _Circuits.cshtml would be
#model IEnumerable<yourModel>
<table class="table">
<thead>
<tr>
// <th> elements
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
.... // Loop through and create content
</tr>
}
</tbody>
</table>
Note that your header elements should be in a <thead> element and the records in a <tbody> element.
I have question about requests - their count.
Im using Ajax.BeginForm and onSuccess option.
But when I click that form my JS handler for OnSuccess option fires up many times.
I looked up for my request and its looks like this:
Image with number of request
So my question is: why if I click on AjaxForm it makes many request?
Thanks
View with Ajax action link:
#foreach (var item in Model)
{
if (item.Accepted == false)
{
<text>
<tr>
<td>
#Html.DisplayFor(modelItem => item.Accepted)
</td>
<td>
#Html.DisplayFor(modelItem => item.IsOrganizer)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlayerRating)
</td>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Ajax.ActionLink("Akceptuj", // <-- Text to display
"AcceptPlayer", // <-- Action Method Name
new { id = item.PlayerId },
new AjaxOptions
{
HttpMethod = "POST",
})
</td>
</tr>
</text>
}
}
Controller action:
[Authorize]
public ActionResult AcceptPlayer(long id)
{
using (var Players = new DbMigrationExample2Entities())
{
Player playerToAccept = Players.Player.Find(id);
if (playerToAccept == null)
{
return HttpNotFound();
}
playerToAccept.Accepted = true;
Players.SaveChanges(); return View();
}
If you have included jquery.unobtrusive-ajax.min.js twice once in the layout once in the partial. So your browser executes the js inside twice which will subscribe twice on the form click event that is why doing two POST instead of one.
So you need to remove the jquery.unobtrusive-ajax.min.js from the partial.
Note: If your are using a partial with a layout you don't need to duplicate the js included in the partial because it's already done by the layout. There are some good articles about layouts and partials.
Second think if problem persist then the solution here :
First ajax request is sent multiple times
If nigher this all solution is not working in your case then definitely you need to change your Ajax.ActionLink into Html.ActionLink Like :
#Html.ActionLink(article.Title, new { controller = "Akceptuj", action = "AcceptPlayer", id = item.PlayerId }, new AjaxOptions { HttpMethod = "POST" })
Cheers !!
i have a table which is having the data retrieved from an api call from my memberController which is displayed inside ng-repeat and its working fine.
I need each Business Name of the member list to link to a separate page(edit_form.html) and display the id value, so that i can pass this along with the api call to get only this particular member detail. So i have added ng-init in my edit form page which calls the function test_funct when the page loads and retrieve each persons id there. unfortunately i am unable to retrieve the id value inside the function.
HTML Template
<div class="page" data-ng-controller="memberController">
<table>
<thead >
<tr>
<th>Business Name</th>
<th>Contact Name</th>
<th>Trade Balance</th>
<th>Cash Balance</th>
<th>Telephone</th>
<th>Account Number </th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="member in details | filter:search">
<td>{{member.businessname}}</td>
<td>{{member.person}}</td>
<td>{{member.balance_trade}}</td>
<td>{{member.balance_cash}}</td>
<td>{{member.telephone}}</td>
<td>{{member.accountnumber}}</td>
</tr>
</tbody>
</table>
</div>
I have the following controller
function memberController($scope, $http, $cookieStore) {
var token = $cookieStore.get('token');
var conId = $cookieStore.get('Cont_Id');
var exId = $cookieStore.get('ex_Id');
var member_list = "http://www.vb.com/functions/member_list.html?exchangeid=" + exId +
"&contactid=" + conId + "&token=" + token;
$http.get(member_list)
.success(function(response) {
$scope.details = response;
});
$scope.test_funct = function(id) {
$scope.myid = id;
alert($scope.myid); // getting undefined in alert, i expect the id(eg:1123)
}
}
edit_form.html
<div class="page" data-ng-controller="memberController">
<div class="panel-body" ng-init="test_funct()"></div>
</div>
Please assist me on this. Thanks in advance.
There are 2 things going on here.
First, you should separate controllers for the different views, so you end up with something like this:
<div class="page" data-ng-controller="memberController">
<table>
<!-- your whole big table here -->
</table>
</div>
And your editing form as follows:
<div class="page" data-ng-controller="editController">
<div class="panel-body"></div>
</div>
Notice that you now have two distinct controllers - your "editController" and your "memberController".
The second question then becomes, how do you transfer the selected ID from the list view ("memberController") to the edit view ("editController").
There are 2 ways of doing that.
First, you could use a service shared between the controller:
.factory('SelectedId',function() {
return {};
});
And then in your "member" view, you would set it upon clicking:
{{member.businessname}}
Notice the ng-click, which then needs a function in the memberController and the injected service:
.controller('memberController',function($scope,SelectedId) {
$scope.setId = function(id) {
SelectedId.id = id;
};
});
While the editController retrieves it:
.controller('editController',function($scope,SelectedId) {
$scope.id = SelectedId.id;
});
The above option works well, especially for complex things like shopping carts. If all you are doing is passing an ID, I would just stick it in the URL:
{{member.businessname}}
So that the ID is part of the URL. You then can retrieve it in the "editController":
.controller('editController',function($scope,$routeParams) {
$scope.id = $routeParams.member;
});
assuming you are using ng-route, and your route would look like:
$routeProvider.when('/pages/edit_form/:member',{templateUrl:'/route/to/template.html',controller:'editController'});
In html do that
<td>{{member.businessname}}</td>
...
In app.js or where you define route do that
.when('/edit/:memberid',
{
templateUrl:'partials/edit.html',
controller:'editController'
})
In controller you have to take this id by doing that
app.controller("editController",function($routeParams,$scope){
$scope.memberid= $routeParams.memberid;
//Now use this member id to fetch all data
});
I'm new to AngularJS and I don't know how to accomplish a task. In my system I have a database with two tables: "Customers" and "Offices". This is a 1:N relation. In my HTML page I have a list (actually is a table) that contains info about the customers (info extracted from the "Customers" table). For each row of the table, you can find a button. If you click on that button some infos about the offices of THAT customer are loaded from the database.
The first part is quite simple. This is the Angular service that loads customers info:
angular.module("app").factory("Customer", ["$resource",
function($resource){
return $resource("/customers/list",
{format:'json'},
{
find: {
method:'GET'
}
});
}]);
This is the Angular controller that exposes an array of customers:
angular.module("app").controller("customersController", ["Customer", "$scope",
function (Customer, $scope){
var customers = new Array();
var metacustomers = Customer.find(function(){
for(var i=0; i<metacustomers.results.length; i++){
customers[i] = new Object();
customers[i].customerName = metacustomers.results[i].customer_name;
customers[i].customerID = metacustomers.results[i].customer_ID;
}
$scope.customers = customers;
});
}]);
And this is the part of the view that shows info:
<table>
<thead>
<th></th>
<th></th>
<th></th>
</thead>
<tr data-ng-repeat="customer in customers | filter:query">
<td>
+
</td>
<td>
<strong>{{customer.customerName}}</strong>
</td>
<td>
<div>
<a title="Modify customer" href="/customers/post/{{customer.customerID}}">
</a>
<a title="Delete customer" href="/customers/delete/{{customer.customerID}}">
</a>
</div>
</td>
</tr>
</table>
All of this looks like:
The problem arrives with the second task. When I click on the "+" button I must load the offices infos about THAT customer (the customer on the same row of the button). I need the customerID to retrieve these infos and I have it. But, how can I say to Angular to load exactly the info belongs to the right customer? I don't know how to model this situation. I need your help. Thank you in advance.
You can pass the customer to the ng-click directive like so:
+
And then create an expand function in your controller which takes a customer as its input. Something like this:
$scope.expand = function(customer){
//fetch the customer
};
I have a list of attachments on a page which is generated using a jQuery $.ajax call and Knockout JS.
My HTML looks like (this is stripped back):
<tbody data-bind="foreach: attachments">
<tr>
<td data-bind="text: Filename" />
</tr>
</tbody>
I have a function that gets the list of attachments which is returned as a JSON response:
$(function () {
getFormAttachments();
});
function getAttachments() {
var request = $.ajax({
type: "GET",
datatype: "json",
url: "/Attachment/GetAttachments"
});
request.done(function (response) {
ko.applyBindings(new vm(response));
});
}
My view model looks like:
function vm(response) {
this.attachments = ko.observableArray(response);
};
There is a refresh button that the use can click to refresh this list because over time attachments may have been added/removed:
$(function () {
$("#refresh").on("click", getAttachments);
});
The initial rendering of the list of attachments is fine, however when I call getAttachments again via the refresh button click the list is added to (in fact each item is duplicated several times).
I've created a jsFiddle to demonstrate this problem here:
http://jsfiddle.net/CpdbJ/137
What am I doing wrong?
Here is a fiddle that fixes your sample. Your biggest issue was that you were calling 'applyBindings' multiple times. In general you will call applyBindings on page load and then the page will interact with the View Model to cause Knockout to refresh portions of your page.
http://jsfiddle.net/CpdbJ/136
html
<table>
<thead>
<tr><th>File Name</th></tr>
</thead>
<tbody data-bind="foreach: attachments">
<tr><td data-bind="text: Filename" /></tr>
</tbody>
</table>
<button data-bind="click: refresh">Refresh</button>
javascript
$(function () {
var ViewModel = function() {
var self = this;
self.count = 0;
self.getAttachments = function() {
var data = [{ Filename: "f"+(self.count*2+1)+".doc" },
{ Filename: "f"+(self.count*2+2)+".doc"}];
self.count = self.count + 1;
return data;
}
self.attachments = ko.observableArray(self.getAttachments());
self.refresh = function() {
self.attachments(self.getAttachments());
}
};
ko.applyBindings(new ViewModel());
});
--
You may also want to look at the mapping plugin - http://knockoutjs.com/documentation/plugins-mapping.html. It can help you transform JSON into View Models. Additionally it is able to assign a property to be the "key" for an object... this will be used to determine old vs new objects on subsequent mappings.
Here is a fiddle I wrote a while back to demonstrate a similar idea:
http://jsfiddle.net/wgZ59/276
NOTE: I use 'update' as part of my mapping rules, but ONLY so I can log to the console. You would only need to add this if you wanted to customize how the mapping plugin updated objects.