injecting $http into directive controller and minification - asp.net-mvc

I'm getting one of these in my prod environment
[$injector:unpr] http://errors.angularjs.org/1.2.2/$injector/unpr?p0=nProvider
My directive is something along these lines
var app = angular.module('myDir', []);
function link(scope, elm, attrs) {
scope.$watch('Potato', function (nVal) {
if (nVal) {
elm.html(Potato);
}
}
function someDir(http, compile) {
function controller($scope, http) {
http.get('/some/service/' + $scope.someThing).success(function (result) {
scope.Potato = result;
console.log('hooray');
});
};
var dirObject = {
restrict: 'A',
scope: {
'someThing': '='
},
link: link,
controller: controller
};
return dirObject;
};
app.directive('someDir', ['$http', '$compile', someDir]);
Is this how I should be handling injecting $http into a directive controller? Solution is hosted on azure, and minified via ASP.NET MVC bundles.
If I render all my script files individually instead of letting them get minified, everything works, but when they get minified I get the unknown provider error.
Adding controller that is running outside of the directive.
angular.element(document).ready(function () {
"use strict";
var profileApp = angular.module('profileApp', ['myDir']);
function ProfileCtrl(scope) {
scope.companyId = angular.element("#Id").val();
};
profileApp.controller('ProfileCtrl', ['$scope', ProfileCtrl]);
angular.bootstrap(document, ['profileApp']);
});

You should return the dirObject from someDir(). I guess this could be a typo.
Also the controller definition is wrong. Use the array injection, if you want the minified version to run:
function controller($scope, http) { ... }
var dirObject = {
...
controller: ["$scope", "$http", controller]
}
return dirObject;

Related

MVC Partial View Returns fine in dev but fails in live with 404 error

I am sending an ajax call on on a button click to load a partial view in Kendo Modal window. In dev mode it is doing everything fine as expected but giving me a 404 error in live.
Following is my Ajax call
$('.add_page').click(function (e) {
e.preventDefault();
$.ajax({
url: '../Page/AddEditPage',
type: 'Get',
accepts: 'text/html',
context: self,
success: self.addEditPageWindowCallBack,
error: function () { toastr.error('Error: Something went wrong'); },
complete: function () { }
});
});
Following is the Ajax success method
addEditPageWindowCallBack: function (html, textStatus, jqXHR) {
var model = $('#AddEditPageModelWindow').data('kendoWindow');
if (model === undefined) {
model = $('#AddEditPageModelWindowLayout').data('kendoWindow');
}
model.content(html);
model.center();
model.open();
},
My Controller action method is as follow
public ActionResult AddEditPage(int Id = 0)
{
var pageId = Id;
var pageViewModel = new PageViewModel();
if(pageId == 0)
{
pageViewModel = new PageViewModel
{
Page = new Page(),
};
pageViewModel.Page.CreatedBy = User.Identity.GetUserId();
}
else
{
var pageData = pageService.GetPageByPageId(pageId);
pageViewModel = new PageViewModel
{
Page = pageData,
};
}
return PartialView("~/Views/Shared/Page/_AddEditPage.cshtml", pageViewModel);
}
Initially, I was returning partial view as follow
return PartialView("Page/_AddEditPage", pageViewModel);
That i have changed with the following but still no effect
return PartialView("~/Views/Shared/Page/_AddEditPage.cshtml", pageViewModel);
I am trying to find a resolution to this problem. Any idea either of resolving this issue or if there is any way of catching the exact error in live environment will really help.
Following is the Network screen shot of error
It seems that the error is probably in following line
url: '../Page/AddEditPage',
try it with url: '~/../Page/AddEditPage',
you can use html helper in your javascript code like
url: '#Html.UrlAction("actionname","controllername")'

Knockout.js 3.3.0 options binding for jQuery mobile 1.4 select

I'm using knockout.js 3.3.0 with jQuery mobile 1.4.
The problem using knockout.js together with jQuery mobile is that the programmatically changes to the underlying viewmodel are not always reflected to the graphical user interface, due to the JQM refactoring of the html elements, or widgets. So, for example, the JQM selectmenu is synchronized from the user interface to the viewmodel, but not in the other way.
I'm trying to stick together the 3.3.0 KO 'options' binding with a custombinding to the current JQM actual version.
There are already two possible solutions for the 'refresh' problem already proposed at SO for 2.x versions of KO: jqmSelect and jqmValue, as custombindings.
I try this suggestions for a more recent KO+JQM combination, putting together all the answers/comments found at SO regarding this topic.
This is the js that i'm using to test:
$(document).ready(function () {
var jsonResultData = [{
"id": 6,
"name": "First item",
"pattern": "Cheetah"
}, {
"id": 2,
"name": "Second item",
"pattern": "Viper"
}, {
"id": 1,
"name": "Third item",
"pattern": "Scorpion"
}];
ko.applyBindings(new AdminViewModel(jsonResultData));
});
function Match(data) {
this.id = ko.observable(data.id);
this.pattern = ko.observable(data.pattern);
this.name = ko.observable(data.name);
}
function AdminViewModel(allData) {
var self = this;
self.matches = ko.observableArray([]);
self.matchesFromDb = $.map(allData, function (item) {
return new Match(item);
});
self.matches = self.matchesFromDb;
self.selectedMatchId = ko.observable(self.matches[0].id());
self.selectedMatch = ko.observable(self.matches[0]);
self.setSelectedMatchId = function (match) {
if (match.id() != self.selectedMatchId()) {
self.selectedMatchId(match.id());
self.selectedMatch(match);
}
};
self.patternValues = ko.observableArray(["Shark", "Viper", "Chameleon", "Cheetah", "Scorpion"]);
}
I made a fiddle to test the jqmValue custom binding, which is one of the latest soultions found at SO, but but i'm not able to get it to work:
ko.bindingHandlers.jqmValue = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
if (typeof ko.bindingHandlers.value.init !== 'undefined') {
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel);
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var instance;
if (typeof ko.bindingHandlers.value.update !== 'undefined') {
ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel);
}
instance = $.data(element, 'mobile-selectmenu');
if (instance) {
$(element).selectmenu('refresh', true);
}
}
};
(the original code for AdminViewModel as reference for KO 2.x thanks to pablo is working with the suggested change to invert value/options in the markup thanks to JohnEarles at google groups)
Here is the fiddle with actual KO and JQM versions where i try to include the best of all suggestions found regarding this topic:
http://jsfiddle.net/nHNzL/42/
but i'm still streching my hairs without success. Why in my test fiddle the changes to the viewmodel are not reflected to the JQM selectmenu? What is my error?
UPDATE: two-page fiddle to test also initialization: http://jsfiddle.net/nHNzL/50/
FINAL VERSION:
I made 2 small fixes and 1 change:
1) isInstance shall be checked every time
2) removed the if (currentValue == value)
3) inverted the disabled attribute
Moreover: i tested this custombinding in a ko foreach-loop, each select element needs to be child of an own container div.
ko.bindingHandlers.jqmSelectMenu = {
init: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
valueObservable = allBindings.get("value"), valueSubscription,
optionsObservable = allBindings.get("options"), optionsSubscription;
var refresh = function () {
var $el = $(element);
var isInstance = !!$.data(element, 'mobile-selectmenu');
if (isInstance) {
$el.selectmenu('refresh', true);
} else {
/* instantiate the widget unless jqm has already done so */
$(element).selectmenu(options);
}
};
refresh();
/* hook up to the observables that make up the underlying <select> */
if (ko.isSubscribable(valueObservable)) {
valueSubscription = valueObservable.subscribe(refresh);
}
if (ko.isSubscribable(optionsObservable)) {
optionsSubscription = optionsObservable.subscribe(refresh);
}
/* properly dispose of widgets & subscriptions when element is removed */
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).selectmenu("destroy");
if (valueSubscription) valueSubscription.dispose();
if (optionsSubscription) optionsSubscription.dispose();
});
},
update: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
$el = $(element);
/* update any widget options if necessary */
ko.utils.objectForEach(options, function (key, value) {
if (key === "enabled") {
$el.selectmenu(value ? "enable" : "disable");
} else {
$el.selectmenu("option", key, value);
}
});
}
};
In fact you are mixing two separate things.
There is the underlying <select> element. It holds the available options as well as the selected one.
Then there is the jQuery Mobile SelectMenu widget. It is concerned with the UX part. A look at the API options it provides reveals that it really has not a lot to do with the underlying select box, mostly using it for self-initialization.
Of course the widget communicates a value change to the underlying select box, but when you change the value programmatically you must communicate that to the widget yourself, by calling refresh on it. So even without knockout this is not a two-way communication.
We do have set of knockout built-in bindings that work very well with select boxes, but what we don't have is a binding that communicates changes in those values to the widget. What we also don't have is a way of initializing/updating any of the widget's API options.
So instead of re-inventing the value binding we need one that deals with the widget itself and otherwise simply complements the existing bindings:
ko.bindingHandlers.jqmSelectMenu = {
init: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
valueObservable = allBindings.get("value"), valueSubscription,
optionsObservable = allBindings.get("options"), optionsSubscription,
isInstance = !!$.data(element, 'mobile-selectmenu'),
refresh = function () { $(element).selectmenu('refresh', true); };
// instantiate the widget unless jqm has already done so
if (!isInstance) $(element).selectmenu(options);
refresh();
// hook up to the observables that make up the underlying <select>
if (ko.isSubscribable(valueObservable)) {
valueSubscription = valueObservable.subscribe(refresh);
}
if (ko.isSubscribable(optionsObservable)) {
optionsSubscription = optionsObservable.subscribe(refresh);
}
// properly dispose of widgets & subscriptions when element is removed
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).selectmenu("destroy");
if (valueSubscription) valueSubscription.dispose();
if (optionsSubscription) optionsSubscription.dispose();
});
},
update: function (element, valueAccessor, allBindings) {
var options = ko.toJS(valueAccessor()),
$elem = $(element);
// update any widget options if necessary
ko.utils.objectForEach(options, function (key, value) {
var currentValue = $elem.selectmenu("option", key);
if (currentValue !== value) {
if (key === "disabled") {
$elem.selectmenu(value ? "disable" : "enable");
} else {
$elem.selectmenu("option", key, value);
}
}
});
}
};
I have written this in the spirit of the knockout-jQueryUI. I recommend taking a look at that library.
Here's an updated fiddle, too. http://jsfiddle.net/nHNzL/46/

Angularjs Rails Resource save() function not working from controller

I am using angularjs-rails-resource , in my Rails Angular App.
Account Resources
myApp.factory('Account', ['railsResourceFactory','railsSerializer', function
(railsResourceFactory,railsSerializer) {
return railsResourceFactory({
url: '/accounts',
name: 'account',
serializer: railsSerializer(function () {
this.nestedAttribute('address');
})
});
}]);
UserController.js
function userController($scope,$location,Auth,$rootScope,$http,Useraccount,Account) {
$scope.profileUpdate = function() {
//Useraccount.save(); // THIS WORKS
$scope.account = {}
$scope.account.save() // Throwing error : undefined function save
}
}
UserAccount Service
myApp.service('Useraccount',function(Auth,$location,$rootScope,Account){
var account;
var query = function(){
var promise = Account.query().then(function (results) {
account = results;
}, function (error) {
alert("Went Wrong while fetching User Account!!")
});
return promise;
}
var save = function() {
account.save().then(function (results) {
console.log(results);
}, function (error) {
alert("Went Wrong!!")
});
}
return {
query:query,
save:save
}
})
});
I am not sure why the save function from UserController is not working though I have imported Account resources as dependency. I did same in service , but it was working there. Any clue will be helpful.
You are actually calling the save() method for an empty javascript object. I don't see the point here.
Anyway you need an Angular object to do so. So either load account data from server.
$scope.accounts = Account.query(); // Will be an Array of accounts
Or create new instance of Account
$scope.account = new Account(); // An empty object

SignalR and MVC bundle

I'm trying to use SignalR with MVC bundle, but having problem finding out how to include the /signalr/hubs script into the bundle. For now I have to insert the path in between jquery.signalR and my code. That will result in three javascript file requests.
Is there any way to include /signalr/hubs into my mvc bundle?
A bit late, but here is my contribution:
Create a javascript file with the following contents:
(function ($) {
$.ajax({
url: "/signalr/hubs",
dataType: "script",
async: false
});
}(jQuery));
Then add the file to the bundles collection.
This will load the "/signalr/hubs" code for you.
The default /signalr/hubs script is generated dynamically by the runtime on the first request and then cached.
You can use hubify.exe (see http://weblogs.asp.net/davidfowler/archive/2012/06/10/signalr-0-5-1-released.aspx for details) to pre-generate the file yourself, so you can add it into the MVC bundle.
I know this is an old thread but I would like to add the following for SignalR 2.x. I really wanted to bundle the proxy using SquishIt and by trial and error I managed to come up with the following:
using Microsoft.AspNet.SignalR
using Microsoft.AspNet.SignalR.Hubs
var resolver = new DefaultHubManager(new DefaultDependencyResolver());
var proxy = new DefaultJavaScriptProxyGenerator(resolver, new NullJavaScriptMinifier());
string iCanHazScriptNao = proxy.GenerateProxy("/signalr");
From asp.net, using the SignalR.Utils NuGet package, I found that I needed to be in the directory with the DLL that has the hub in it:
(assuming you have a standard solution structure and are using 2.2.0 of SignalR.Utils)
cd C:\YourSolution\YourProjectWithTheHub\bin\Debug
..\..\..\packages\Microsoft.AspNet.SignalR.Utils.2.2.0\tools\signalr.exe ghp
After running the tool, there will be a server.js file in the directory you ran it from (in this case, Debug).
(Note: I couldn't get it to work when specifying the path with the /p flag, and for some reason even when it does work, it creates a temp directory with the signalr.exe file in it)
I used #KTW response mentioned on this Thread and here is the complete change
BundleConfig
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/modernizr-2.6.2.js",
"~/Scripts/jquery-2.2.3.js",
"~/Scripts/jquery-ui-1.11.4.js",
"~/Scripts/jquery.multiselect.js",
"~/Scripts/jquery.dataTables.js",
"~/Scripts/jquery.jstepper.min.js",
"~/Scripts/underscore.min.js"
));
bundles.Add(new ScriptBundle("~/bundles/SignalRScripts").Include(
"~/Scripts/jquery.signalR-2.2.2.min.js",
"~/Scripts/signalRBundle.js",
"~/Scripts/Views/Search/SignalRFunctions.js"));
}
}
SignalRFunctions.js
$(function() {
// Declare a proxy to reference the hub.
var usersHub = $.connection.currentUsersHub;
//Create a function that the hub can call to broadcast messages.
usersHub.client.broadcastMessage = function(reservationNumber, usrName) {
//Message broadcast from server
//now find the id with reservationNumber on the page and to that append the user name
var id = '#' + reservationNumber;
if ($(id).length) {
if (usrName.length) {
itemsOpened($(id), usrName);
} else {
itemsClosed($(id));
}
}
//else {
// //is it possible that server broad casted for a reservationNumber and is not available at the client?
//}
};
//Accepts dictionary from hub and goes through search results
//https://stackoverflow.com/questions/7776337/reading-c-sharp-dictionary-in-javascript
usersHub.client.broadcastCollection = function (reservationNumberAndUsers) {
for (var resNumKey in reservationNumberAndUsers) {
if (reservationNumberAndUsers.hasOwnProperty(resNumKey)) {
//Message broadcast from server
//now find the id with ReservationNumber on the page and to that append the user name
var id = '#' + resNumKey;
if ($(id).length) {
if (reservationNumberAndUsers[resNumKey].length) {
itemsOpened($(id), reservationNumberAndUsers[resNumKey]);
} else {
itemsClosed($(id));
}
}
}
}
};
$.connection.hub.start().done(function() {
var searchedReservationNumbers = [];
if (typeof jsData !== 'undefined') {
if (jsData && jsData.length) {
for (var i = 0; i < jsData.length; i++) {
searchedReservationNumbers.push(jsData[i].UReservationNumber);
}
if (searcheduReservationNumbers.length !== 0) {
usersHub.server.getWorkingUsersOnUReservationNumber(searcheduReservationNumbers);
}
}
}
}).fail(function () { console.log('Could not Connect To SignalrHub!'); });
/*In case we would decide to continuously reconnect making connection to server.
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
},
5000); // Restart connection after 5 seconds.
});*/
function itemsOpened(elem, id) {
var item = "Opened By - " + id;
elem.prop('title', item);
elem.css('background-color', 'chocolate');
};
function itemsClosed(elem) {
elem.prop('title', "");
elem.css('background-color', '');
};
});
signalRBundle.js
(function ($) {
$.ajax({
url: "/signalr/hubs",
dataType: "script",
async: false
});
}(jQuery));
/* Source https://stackoverflow.com/questions/11556110/signalr-and-mvc-bundle */
SomePartialView.cshtml
Instead of writing below in above partial view
#using Localization
#using Newtonsoft.Json
#model NameSpace.ViewModels.FilterVM
#{
ViewBag.Title = Strings.Filter;
}
#using (Html.BeginForm())
{
<div class="large-12 columns">
---SOME CODE HERE
</div>
}
#section scripts
{
<script type="text/javascript" language="javascript">
var jsData = #Html.Raw(JsonConvert.SerializeObject(Model));
</script>
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Scripts/Views/Search/SignalRFunctions.js"></script>
}
This changed to
#using Localization
#using Newtonsoft.Json
#model NameSpace.ViewModels.FilterVM
#{
ViewBag.Title = Strings.Filter;
}
#using (Html.BeginForm())
{
<div class="large-12 columns">
---SOME CODE HERE
</div>
}
#section scripts
{
<script type="text/javascript" language="javascript">
var jsData = #Html.Raw(JsonConvert.SerializeObject(Model));
</script>
#Scripts.Render("~/bundles/SignalRScripts")
}
Notice
#Scripts.Render("~/bundles/SignalRScripts")
in partial view above.Without #KTW file above(ajax request to /signalr/hubs)
var usersHub = $.connection.currentUsersHub;
was always coming as null.
You can generate the code with the Microsoft.AspNet.SignalR.Utils NuGet package. Add that to your project, then you can add the below as a post-build script (Project -> {project name} Properties -> Build Events). It should be a post-build script and not pre- since you want it to build against your updated hub code after it's compiled.
It will find whatever version of the Microsoft.AspNet.SignalR.Utils package that you have installed and put the server.js file in the Scripts folder.
You must also have a version redirect for Newtonsoft.Json in your web.config file (assuming your project uses Newtonsoft.Json). This is because signalr.exe is built against version 6.0.0 and you're likely using a newer version. The /configFile switch is to tell it to use your project's config file so that it uses the redirect.
Post-build script:
cd $(ProjectDir)\Scripts
FOR /F "usebackq delims=" %%p IN (`dir ..\..\packages /b /ad ^| find "Microsoft.AspNet.SignalR.Utils"`) DO (
set "UTILSPATH=%%p"
)
$(SolutionDir)\packages\%UTILSPATH%\tools\net40\signalr.exe ghp /path:$(TargetDir) /configFile:$(TargetPath).config
Then include ~/Scripts/server.js in your bundle.

ASP.NET MVC 3 Treeview

I need to display a Treeview in my MVC3 application. There will be a self referencing hierarchical table (Folders) and another table linked to it (Documents.) (So Folders can have N-subFolders and any folder/sub folder can have many documents.)
I have looked into using third party vendors such as Telerik, DJME and MVC Controls Toolkit. While all nice packages, I'm uneasy about the licences, and since i'm new to MVC (and programming in general,) I find their documentation lacking to get the right display working.
I've also looked at the heavily referenced blogs on TreeViews:
TreeViewHelper
and the Recursive Partial View
In addition to the other less referenced articles (The top 3 are also very informative):
http://tpeczek.com/2010/01/asynchronous-treeview-in-aspnet-mvc.html
http://mikehadlow.blogspot.com/2008/10/rendering-tree-view-using-mvc-framework.html
http://www.tek-tips.com/viewthread.cfm?qid=1637392&page=4
http://weblogs.asp.net/jigardesai/archive/2008/02/04/display-hierarchical-data-in-asp-net-mvc-framework.aspx
http://www.jigar.net/articles/viewhtmlcontent311.aspx
http://help.syncfusion.com/ug_82/ASP.NETMVCUI_Tools/CreatingATreeViewControl.html
I would like to use either the TreeViewHelper or the Recursive Partial View Method.
However, in the TreeViewHelper, I can't make it pull data from the second table (ie. I can only make it list the Files, but I'm not sure how to have it list the Documents for each File.)
For the Recursive Partial View, I'm still at a loss in how to convert this to MVC3 and also general implementation. I did find a post (forums.asp.net/t/1652809.aspx/1?treeview+with+mvc+3) that gives an explanation of how to convert a bit of it to MVC3, but i'm still unclear of what to do with it. I keep getting the error for the Partial view: Cannot implicitly Convert type 'void' to type 'object'
Like I said before I'm new to MVC3 and would like insight in which method would work best for my scenario and how to implement it.
In case anyone is wondering, the way I solved this problem was to use a recursive partial view. The problem I has having with it was that I didn't have the self referencing relationship set up in SQL/EF (I just had the ParentID field which wasn't linked to the Primary Key.) I also integrated jsTree as this has a lot of slick functionality such as search.
Like I said in the comment above, #Html.Action and #Html.Partial work instead of #Html.RenderAction and #Html.RenderPartial.
give a look to the edit/add/delete/node move templated TreeView of my Mvc Controls Toolkit here: http://mvccontrolstoolkit.codeplex.com/wikipage?title=TreeView
$(document).ready(function () {
BindChart();
});
function BindChart() {
$("#org").jOrgChart({
chartElement: '#chart',
dragAndDrop: true
});
}
$(".cardadd").live("click", function ()
{
var data = { id: 0 , ParentId:$(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardedit").live("click", function () {
var data = { id: $(this).parent().data('cardid')};
OpenForminWindow('divfrmChartMember', 'divChartMember', 'frmChartMember', chart.ChartMember, data, '', 400, 1000);
});
$(".cardremove").live("click", function () {
});
function OpenForminWindow(popupId, targetDivId, formid, url, data, callbackfunc, heigth, width) {
$.ajax({
type: "GET",
url: url,
data: data,
cache: false,
success: function (data) {
$('#' + targetDivId).html(data);
$('#' + formid).removeData('validator');
$('#' + formid).removeData('unobtrusiveValidation');
$('#' + formid).each(function () { $.data($(this)[0], 'validator', false); }); //enable to display the error messages
$.validator.unobtrusive.parse('#' + formid);
if (callbackfunc)
return callbackfunc();
}
});
$("#" + popupId).dialog({
modal: true,
height: heigth,
width: width,
beforeClose: function (event, ui) {
if (typeof refresh !== 'undefined' && refresh == true)
ReloadCurrentPage();
}
});
}
$('#frmChartMember').live('submit', function (e) {
SubmitAjaxForm($(this).attr('id'), chart.AddMember, ReloadChart);
e.preventDefault();
});
function SubmitAjaxForm(formId, url, callBack) {
$.ajax({
url: url,
type: 'post',
cache: false,
data: $('#' + formId).serialize(),
success: function (data) {
return callBack(data);
},
});
}
function ReloadChart(result) {
ClosePopup('divfrmChartMember');
$.ajax({
type: 'GET',
url: chart.ChartList,
cache: false,
success: function (result) {
$("#orgChart").html(result);
BindChart();
}
});
}
function ClosePopup(divid) {
$("#" + divid).dialog("close");
}
public class ChartController : Controller
{
//
// GET: /Chart/
ChartContext ctx = new ChartContext();
public ActionResult Index()
{
return View();
}
public ActionResult OrgChart()
{
return PartialView("_OrgChart", ctx.Cards.ToList());
}
public ActionResult ChartMember(int id, int? ParentId = null)
{
Card card = new Card();
if (id > 0)
card = ctx.Cards.Find(id);
else
card.ParentId = ParentId;
return PartialView("_ChartMember", card);
}
public ActionResult SaveMember(Card card)
{
if (card.id == 0)
ctx.Cards.Add(card);
else
ctx.Entry(card).State = System.Data.EntityState.Modified;
ctx.SaveChanges();
return Json(true, JsonRequestBehavior.AllowGet);
}
}

Resources