I faced a problem and can't solve it. Maybe somobody would help me with this one.
I have MVC partial view which has table with AJAX pagination.
<div id="mainTable">
#using (Ajax.BeginForm("OrganizationMemberList", "Team", new AjaxOptions()
{
InsertionMode = InsertionMode.ReplaceWith,
UpdateTargetId = "mainTable",
HttpMethod = "POST",
Url = #Url.Action("OrganizationMemberList", "Team")
}))
{
<div class="widget">
<div class="widget-body">
<div class="table-responsive">
<div class="form-group">
<div class="input-group">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">#ResourceMain.Search</button>
</span>
#Html.TextBoxFor(x => x.SearchString, new { #class = "form-control", #autocomplete = "off" })
</div>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>#Html.DisplayNameFor(x => x.Source.FirstOrDefault().Name)</th>
</tr>
</thead>
<tbody>
#foreach (var employee in Model.Source)
{
<tr>
<td class="hidden">
#employee.Upn
</td>
<td>
#employee.Name
</td>
<td>
<i class="pointerSelect glyphicon glyphicon-plus" data-bind="click: function(name, upn){ AddUser('#employee.Name', '#employee.Upn' ) }"></i>
</td>
<td>
<i class="pointerSelect glyphicon glyphicon-king" data-bind="click: function(name, upn){ AddLeader('#employee.Name', '#employee.Upn' ) }"></i>
</td>
</tr>
}
</tbody>
</table>
#Html.Pagination(Model, "OrganizationMemberList", "mainTable")
</div>
</div>
</div>
}
</div>
and I have Click bindings on <i> tag.
When main page loads with this partial everything is working. The problem comes when I change the page after table reload bindings are not working.
My main page:
<div class="row">
<div id="something" class="col-md-3">
#Html.Action("OrganizationMemberList", "Team")
</div>
<div class="col-md-9">
<div class="widget">
<div class="widget-body">
#using (Html.BeginForm(null,null,null,FormMethod.Post, new {#class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.LabelFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
#Html.TextBoxFor(x => x.Name, new {#class = "form-control"})
</div>
<div class="form-group">
#Html.LabelFor(x => x.Leader)
<i class="glyphicon glyphicon-king"></i>
<div data-bind="with: team">
<div style="max-width: 160px;" class="input-group">
<div class="input-group-addon">
<span data-bind="text: Leader().FullName"></span>
</div>
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(x => x.User)
<i class="glyphicon glyphicon-user"></i>
<div data-bind="foreach: users">
<div style="max-width: 160px;" class="input-group">
<div class="input-group-addon">
<span data-bind="text: FullName"></span>
</div>
<div class="input-group-addon">
<i class="pointerSelect glyphicon glyphicon-remove" data-bind="click: $root.RemoveUser"></i>
</div>
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
<script src="~/Scripts/models/TeamViewModel.js"></script>
And my Knock ViewModel looks like this:
$(function() {
var teamModel = function(team) {
var self = this;
self.id = ko.observable(team ? team.Id : 0);
self.User = ko.observableArray(team ? team.User : []);
self.Name = ko.observable(team ? team.Name : "");
self.Leader = ko.observable(team ? team.Leader : "");
};
var userModel = function (user) {
var self = this;
self.Id = ko.observable(user ? user.Id : 0);
self.FullName = ko.observable(user ? user.FullName : "");
self.UserUpn = ko.observable(user ? user.UserUpn : "");
};
var teamManagement = function() {
var self = this;
self.team = ko.observable(new teamModel());
self.users = ko.observableArray([]);
self.AddUser = function (name, upn) {
var user = { FullName: name, UserUpn: upn };
var obj = new userModel(user);
self.users.push(obj);
};
self.RemoveUser = function (user) {
self.users.remove(user);
};
self.AddLeader = function (name, upn) {
var user = { FullName: name, UserUpn: upn };
var obj = new userModel(user);
self.team().Leader(obj);
};
};
ko.applyBindings(new teamManagement());
});
Anybody has a clue?
So I made a research regarding my questuin and I came with this kind of solution.
First I need to split my teamManagement viewModel in to two viewModel.
One that is responsible for PartialView bindings and second one responsible for main page binding.
So created partialViewTable viewModel that has AddUser and AddLeader functionality.
Then I add Id to the container where PartialView is loaded. I called it PartialContainer and also added id to the main page I called it MainPageContainer.
In JS file I applyBindings like this:
ko.applyBindings(new teamManagement(), document.getElementById("MainPageContainer"));
ko.applyBindings(new partialViewTable(), document.getElementById("PartialContainer"));
Also created onSuccess function for the Ajax.Begin form.
function applyBinding() {
ko.cleanNode($("body")[0]);
ko.applyBindings(new partialViewTable(), document.getElementById("something"));
};
Now when you partialView is reloaded via Ajax it will clear old bindings and apply new binding to the new content.
Related
This maybe a simple problem, but I can't find the cure.
When I executes this :-
$('#save_results').on("click", function () {
$('#formSaveQuotationPrepDetail').submit();
});
Everything works fine. But when I do this :-
$('#save_results').on("click", function () {
$('#formSaveQuotationPrepDetail').submit(function (e) {
var result = '#TempData["StatusMsg"]';
e.preventDefault();
if (result == 'Success') {
alert("Saved Successfully.");
}
})
});
This is my code behind :-
[HttpPost]
public ActionResult SaveQuotePreparation(QuotationPreparationEntity quoteP)
{
string result = objManager.SaveQuotePreparation(quoteP);
if (!string.IsNullOrEmpty(result) && (result == GeneralConstants.Inserted || result == GeneralConstants.Updated))
{
//Payment Gateway
data = GeneralConstants.SavedSuccess;
TempData["StatusMsg"] = "Success";
}
return new JsonResult { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
My HTML is a long code , I've made it short just for understanding :-
#using (Html.BeginForm("SaveQuotePreparation", "Technical", FormMethod.Post, new { #id = "formSaveQuotationPrepDetail" }))
{
<div class="row">
<form>
<div class="col-md-12 text-left">
<div class="row text-center">
<div class="col-md-4">
<div class="form-group text-left">
<label class="control-label ">
Quote Number
</label>
#Html.DropDownListFor(m => m.QuoteNo, new SelectList(#ViewBag.ListQuoteNo, "DataStringValueField", "DataTextField", Model.QuoteNo),
new
{
#class = "form-control requiredValidation",
#id = "QuoteNo",
#data_inneraction = "validationCall",
#onfocusout = "return ValidateRequiredFieldsOnFocusOut(this)"
})
<span class="HideValidMsg">Please Select QuoteNo</span>
</div>
</div>
<div class="col-md-4">
<div class="form-group text-left">
<label class="control-label">
Product Line
</label>
#Html.DropDownListFor(m => m.ProductLine, new SelectList(#ViewBag.ListProdGrp, "DataStringValueField", "DataTextField", Model.ProductLine),
new
{
#class = "form-control requiredValidation",
#id = "ProductLine",
#onfocusout = "return ValidateRequiredFieldsOnFocusOut(this)",
ng_model = "ProductLine",
ng_change = "GetProductLineDetails(ProductLine)"
})
<span class="HideValidMsg">Please Select ProductLine</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 pt-4 text-center">
<button type="button" class="btn btn-success btn-sm" data-dismiss="modal" id="save_results">Save</button>
#*<input style="font-size:18px" type="button" class="btn btn-success btn-sm" data-dismiss="modal" id="save_results" value="Save" />*#
<input style="font-size:18px" type="button" class="btn btn-secondary btn-sm" data-dismiss="modal" value="Close" />
</div>
</div>
</form>
The Event don't fire on click. I don't get any error or anything.
I want to return JSON data on submit and show it as an alert on the screen.
You can do form submit in javascript like this..
$(document).on("submit", "form", function (event) {
event.preventDefault();
$.ajax({
url: $(this).attr("action"),
type: $(this).attr("method"),
dataType: "JSON",
data: new FormData(this),
processData: false,
contentType: false,
success: function (data, status) {
successFunction()
}
},
error: function (xhr, desc, err) {
}
});
});
The From will define like this
<form class="form-horizontal" id="frm" name="frm" action="/Controler/Action" method="post" enctype="multipart/form-data"></form>
And Need to create the button as submit
<input type="submit"/>
i dont have your html code but ,
if you want your form to be submitted by on click event :
$('#save_results').on("click", function () {
e.preventDefault();
$('#formSaveQuotationPrepDetail').submit();
if (result == 'Success') {
alert("Saved Successfully.");
}
});
take a look at this example to see difference between your first and second code .
NOTE : in your code result is not defined , i am not sure where have you brought it from
You did mistake in view page. Please remove <form> tag inside view page. It will work.
You only use below code instead of your code:
note: Html.Beginform() method work as tag in HTML
#using (Html.BeginForm("SaveQuotePreparation", "Technical", FormMethod.Post, new { #id = "formSaveQuotationPrepDetail" }))
{
<div class="row">
<div class="col-md-12 text-left">
<div class="row text-center">
<div class="col-md-4">
<div class="form-group text-left">
<label class="control-label ">
Quote Number
</label>
#Html.DropDownListFor(m => m.QuoteNo, new SelectList(#ViewBag.ListQuoteNo, "DataStringValueField", "DataTextField", Model.QuoteNo), new { #class = "form-control requiredValidation", #id = "QuoteNo", #data_inneraction = "validationCall", #onfocusout = "return ValidateRequiredFieldsOnFocusOut(this)" })
<span class="HideValidMsg">Please Select QuoteNo</span>
</div>
</div>
<div class="col-md-4">
<div class="form-group text-left">
<label class="control-label">
Product Line
</label>
#Html.DropDownListFor(m => m.ProductLine, new SelectList(#ViewBag.ListProdGrp, "DataStringValueField", "DataTextField", Model.ProductLine), new { #class = "form-control requiredValidation", #id = "ProductLine", #onfocusout = "return ValidateRequiredFieldsOnFocusOut(this)", ng_model = "ProductLine", ng_change = "GetProductLineDetails(ProductLine)" })
<span class="HideValidMsg">Please Select ProductLine</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 pt-4 text-center">
<button type="button" class="btn btn-success btn-sm" data-dismiss="modal" id="save_results">Save</button>
#*<input style="font-size:18px" type="button" class="btn btn-success btn-sm" data-dismiss="modal" id="save_results" value="Save" />*#
<input style="font-size:18px" type="button" class="btn btn-secondary btn-sm" data-dismiss="modal" value="Close" />
</div>
</div>
</div>
}
This is what I am currently trying to achieve:
I am trying to clone a form and change the input name. How would I pull this off.
JS:
$(document).ready(function() {
var counter = 0;
var MAX_FIELDS = 3;
var MIN_FIELDS = 1;
var form = $('.force-group');
$('#add').on('click', function(e) {
if (counter < MAX_FIELDS) {
//$(".form-group").clone().appendTo("#forceForm");
$.each(form, function(i) {
var clone = $('.force-group').first().clone();
clone.children().val("");
$(this).parent().append(clone);
if (counter == 0) {
$("b:eq(1)").html("<b> Force 2</b>");
};
if (counter == 1) {
$("b:eq(3)").html("<b> Force 3</b>");
};
if (counter == 2) {
$("b:eq(5)").html("<b> Force 4</b>");
};
});
counter++;
};
});
html:
<#import "template.ftl" as layout />
<#layout.template title="Force" js="/js/force.js" css="/css/force.css">
<h3 class = "text-center">Total Force</h3>
<hr>
<div class="col-md-6 col-md-offset-3 col-xs-8 col-xs-offset-2">
<div class="force-selector input_fields_wrap">
<a id = 'add' href="#" class="btn btn-primary col-md-5">Add Force</a>
<a id = 'remove' href="#" class="btn btn-warning col-md-5 col-md-offset-2">Remove Force</a>
</div>
</div>
<div class="col-md-6 col-md-offset-">
<div class="col-xs-12">
<div class = "well" id="force-input">
<form id = "forceForm" class = "form-horizontal" method = "POST" autocomplete="off">
<fieldset>
<h4 class="text-center" id="form-title"><strong>Input</strong></h4>
<div class="form-group force-group">
<label class = "control-label col-md-2"><b>Force 1</b></label>
<label class = "control-label col-md-2">Magnitude</label>
<div class = "col-md-3 force-info">
<input type = "text" class="form-control" name="0force" autocomplete="off">
</div>
<label class = "control-label col-md-2">Angle</label>
<div class = "col-md-3 force-info">
<input type = "text" class="form-control" name="0angle" autocomplete="off">
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
<div class = "col-md-5">
<table id = "forceChart" class="table table-striped table-hover">
<thead>
<tr class="table">
<th class="head-pad">Total Force</th>
</tr>
</thead>
<tbody>
<tr class="table">
<td class="pad">Net Force:</td>
</tr>
</tbody>
</thead>
<tbody>
<tr class="table">
<td class="pad">Direction:</td>
</tr>
</tbody>
<tfoot></tfoot>
</table>
</div>
<div class = "col-md-4 col-md-offset-4 col-xs-6 col-xs-offset-3">
<div class="form-group">
<div class = "col-md-6 col-xs-offset-3">
<input id = "calculate" class = "btn btn-success btn-block" type="submit" value="Calculate!">
</div>
</div>
</div>
This is the jsfiddle link right here:
Click here for the link.
PS: I am using a templater so the jsfiddle will look kinda bad.
I am new to jQuery and I have been searching around a solution but I seem to get stuck with it.
I tried using the .last() with the attr edit but that seems not to do anything.
To change an input name just use attr() method
$('input').attr('name', 'yourNewname');
and I remarque that you have input without Id so you should use one of those methods:
1/ give each input an Id for example #input1 #input2
$('#input1').attr('name', 'yourNewname1'); // change the name of first input
$('#input2').attr('name', 'yourNewname2'); // change the name of first input
or
you can use eq()
$('input').eq(0).attr('name', 'yourNewname1'); // change the name of first input
$('input').eq(1).attr('name', 'yourNewname2'); // change the name of first input
To change any attribute including name you can do this
$('#myInputFieldId').attr('name','new_name')
Hope it helps
Try this: $('input[name="0angle"]').attr('name', 'new-name');
I have the following view
#using System.Linq;
#model MyCompanyNET.Models.ViewModels.AdministratorViewModel
<style type="text/css">
span {
padding-right: 10px;
}
...
</style>
<div class="manage">
<ul class="nav nav-tabs" id="myTab">
<li class="active">
<a data-toggle="tab" href="#invite">
<span class="glyphicon glyphicon-inbox"></span>Invite
</a>
</li>
...
</ul>
<div class="tab-content">
<div id="invite" class="tab-pane active fade in">
#Html.Partial("_InvitePartial", Model)
</div>
...
</div>
</div>
#section scripts {
<script>
$(function () {
$("input[type=button]").click(function () {
var data_email = $('#email').val();
var data_product = $('#product option:selected').text();
$.ajax({
url: '#Url.Action("SendInvite")',
type: 'POST',
data: { email: data_email, product: data_product },
success: function (result) {
$('#fail_message').html(result.result_failure);
$('#success_message').html(result.result_success);
}
});
});
});
</script>
}
The partial view is
#using (Html.BeginForm("SendInvite", "Tools", FormMethod.Post,
new
{
#class = "form-horizontal",
enctype = "multipart/form-data",
role = "form"
}))
{
#Html.AntiForgeryToken()
<h2>Invite Users</h2>
<div class="form-group">
#Html.LabelFor(m => m.EmailAddress, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.EmailAddress, new { #class = "form-control", id = "email" })
#Html.ValidationMessageFor(m => m.EmailAddress)
</div>
</div>
<div class="form-group">
#Html.Label("Access To", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("Product", new SelectList(
new List<Object> {
new { Text = "Product1", Value = "Product1" },
new { Text = "Product2", Value = "Product2" },
new { Text = "All", Value = "All" }},
"Value",
"Text"),
new { #class = "form-control", id = "product" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<span id="fail_message" class="label label-danger"></span>
<span id="success_message" class="label label-success"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="button" value="Invite User" class="btn btn-primary" />
</div>
</div>
}
and my controller is
[AllowAnonymous]
public async Task<JsonResult> SendInvite(string email, string product)
{
ApplicationUser user = null;
string result = String.Empty;
if (ModelState.IsValid)
{
if (String.IsNullOrEmpty(email))
{
return Json(new
{
result_success = String.Empty,
result_failure = "Please enter and email address"
}, JsonRequestBehavior.AllowGet);
}
}
... // Do stuff with email and product.
}
Now when this partial view code was in the view itself, everything worked fine. I'd get both the entered email address and the selected product coming through to my controller method SendInvite, but now the button does not even fire.
Now, I have changed the
<input type="button" value="Invite User" class="btn btn-primary"/>
to use submit
<input type="submit" value="Invite User" class="btn btn-primary"/>
This now calls my controller method but without the email parameter, which is always null(?). To make things worse, the return Json(new ... also does not render my <span id="success_message" class="label label-success"></span>s and it used to when everything was in the main view. Why is this occurring and how an I fix it?
Thank for your time.
the name of the email field is EmailAddress not email.. change your parameter name to string EmailAddress or you can change your markup to
#Html.TextBoxFor(m => m.EmailAddress, new { #class = "form-control", Name="email", id = "email" })
trick is the make the name of the field and your parameter name match, not the id.
Killercam, regarding your form submitting. Did you change it back to type="button" and not type="submit"? If not, reason is when you click the button, it will submit it and will disregard your JavaScript.
try decorating your action method by HttpPost
I have a problem with an AngularJS app I'm making. It shows a list of contacts, and for each contact there is a button whereupon clicking the button a modal pops up with a form. This form should show the existing contact information, and if you want to change something you type the new information and press submit.
The problem, however, is that the existing information is not shown in the form, hence editing it doesn't work. I imagine that the issue is that the modal does not inherit the scope from the parent page, but I don't know what to do in order to fix that. I have tried playing around with the attributes on the input fields (for example prepending ng-model by $parent. and defining an ng-init value), but to no avail, so I hope some of the experts here will be able to point me on the right track.
Thank you in advance.
Now let me show you my code, so you can see the context I'm talking about. Here is the html that displays the list of contacts:
<div class="panel panel-default" ng-controller="contactsController">
<div class="panel-body">
<div id="gridContainer" ng-class="{'': state == 'list', 'none': state != 'list'}">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th scope="col"><spring:message code="contacts.name"/></th>
<th scope="col"><spring:message code="contacts.email"/></th>
<th scope="col"><spring:message code="contacts.phone"/></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in page.source">
<td class="tdContactsCentered">{{contact.name}}</td>
<td class="tdContactsCentered">{{contact.email}}</td>
<td class="tdContactsCentered">{{contact.phoneNumber}}</td>
<td class="width15">
<div class="text-center">
<input type="hidden" value="{{contact.id}}"/>
<a ng-href="#updateContactsModal"
ng-click="selectedContact(contact);"
role="button"
title="<spring:message code="update"/> <spring:message code="contact"/>"
class="btn btn-sm btn-warning" data-toggle="modal">
<i class="icon-pencil"></i>
</a>
<a ng-href="#deleteContactsModal"
ng-click="selectedContact(contact);"
role="button"
title="<spring:message code="delete"/> <spring:message code="contact"/>"
class="btn btn-sm btn-danger" data-toggle="modal">
<em class="fa fa-trash"></em>
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
And the html that defines the modal and the form:
<div id="updateContactsModal"
class="modal fade centering"
role="dialog"
aria-labelledby="updateContactsModalLabel"
aria-hidden="true" style="display: none;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3 id="updateContactsModalLabel" class="modal-title">
<spring:message code="update"/> <spring:message code="contact"/>
</h3>
</div>
<div class="modal-body" data-ng-controller="contactsController">
<form name="updateContactForm" novalidate>
<input type="hidden"
required
data-ng-model="contact.id"
name="id"
value="{{contact.id}}"/>
<div>
<div class="form-group">
<label>* <spring:message code="contacts.name"/>:</label>
<input type="text"
autofocus
required
class="form-control"
data-ng-model="contact.name"
name="name"
placeholder="<spring:message code='contact'/> <spring:message code='contacts.name'/> "/>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.name.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
<div class="form-group">
<label>* <spring:message code="contacts.email"/>:</label>
<div class="input-append">
<input type="text"
required
class="form-control"
ng-model="contact.email"
name="email"
placeholder="<spring:message code='sample.email'/> "/>
</div>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.email.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
<div class="form-group">
<label>* <spring:message code="contacts.phone"/>:</label>
<div class="input-append">
<input type="text"
required
class="form-control"
ng-model="contact.phoneNumber"
name="phoneNumber"
placeholder="<spring:message code='sample.phone'/> "/>
</div>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.phoneNumber.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
</div>
</form>
<div class="modal-footer">
<input type="submit"
class="btn btn-primary"
ng-click="updateContact(updateContactForm);"
value='<spring:message code="update"/>'/>
<button class="btn btn-default"
data-dismiss="modal"
ng-click="exit('#updateContactsModal');"
aria-hidden="true">
<spring:message code="cancel"/></button>
</div>
</div>
<span class="alert alert-error dialogErrorMessage"
ng-show="errorOnSubmit">
<spring:message code="request.error"/>
</span>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
And finally the relevant parts of the controller:
App.controller('contactsController', ["$scope", "$http", function($scope,$http) {
$scope.pageToGet = 0;
$scope.state = 'busy';
$scope.lastAction = '';
$scope.url = "/uaiContacts/protected/contacts/";
$scope.errorOnSubmit = true;
$scope.errorIllegalAccess = false;
$scope.displayMessageToUser = false;
$scope.displayValidationError = false;
$scope.displaySearchMessage = false;
$scope.displaySearchButton = false;
$scope.displayCreateContactButton = false;
$scope.contact = {};
$scope.searchFor = "";
$scope.getContactList = function () {
var url = $scope.url;
$scope.lastAction = 'list';
$scope.startDialogAjaxRequest();
var config = {params: {page: $scope.pageToGet}};
$http.get(url, config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, null, false);
})
.error(function () {
$scope.state = 'error';
$scope.displayCreateContactButton = false;
});
};
$scope.populateTable = function (data) {
if (data.pagesCount > 0) {
$scope.state = 'list';
$scope.page = {source: data.contacts, currentPage: $scope.pageToGet, pagesCount: data.pagesCount, totalContacts : data.totalContacts};
if($scope.page.pagesCount <= $scope.page.currentPage){
$scope.pageToGet = $scope.page.pagesCount - 1;
$scope.page.currentPage = $scope.page.pagesCount - 1;
}
$scope.displayCreateContactButton = true;
$scope.displaySearchButton = true;
} else {
$scope.state = 'noresult';
$scope.displayCreateContactButton = true;
if(!$scope.searchFor){
$scope.displaySearchButton = false;
}
}
if (data.actionMessage || data.searchMessage) {
$scope.displayMessageToUser = $scope.lastAction != 'search';
$scope.page.actionMessage = data.actionMessage;
$scope.page.searchMessage = data.searchMessage;
} else {
$scope.displayMessageToUser = false;
}
};
$scope.exit = function (modalId) {
$(modalId).modal('hide');
$scope.contact = {};
$scope.errorOnSubmit = false;
$scope.errorIllegalAccess = false;
$scope.displayValidationError = false;
};
$scope.finishAjaxCallOnSuccess = function (data, modalId, isPagination) {
$scope.populateTable(data);
$("#loadingModal").modal('hide');
if(!isPagination){
if(modalId){
$scope.exit(modalId);
}
}
$scope.lastAction = '';
};
$scope.startDialogAjaxRequest = function () {
$scope.displayValidationError = false;
$("#loadingModal").modal('show');
$scope.previousState = $scope.state;
$scope.state = 'busy';
};
$scope.handleErrorInDialogs = function (status) {
$("#loadingModal").modal('hide');
$scope.state = $scope.previousState;
// illegal access
if(status == 403){
$scope.errorIllegalAccess = true;
return;
}
$scope.errorOnSubmit = true;
$scope.lastAction = '';
};
$scope.addSearchParametersIfNeeded = function(config, isPagination) {
if(!config.params){
config.params = {};
}
config.params.page = $scope.pageToGet;
if($scope.searchFor){
config.params.searchFor = $scope.searchFor;
}
};
$scope.selectedContact = function (contact) {
$scope.contact = angular.copy(contact);
debugger;
};
$scope.updateContact = function (updateContactForm) {
if (!updateContactForm.$valid) {
debugger;
$scope.displayValidationError = true;
return;
}
$scope.lastAction = 'update';
var url = $scope.url + $scope.contact.id;
$scope.startDialogAjaxRequest();
var config = {};
$scope.addSearchParametersIfNeeded(config, false);
$http.put(url, $scope.contact, config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, "#updateContactsModal", false);
})
.error(function(data, status, headers, config) {
$scope.handleErrorInDialogs(status);
});
};
$scope.getContactList();
}]);
The modal doesn't share the same scope as the contacts table because each time Angular finds another ng-controller directive, it creates a new scope.
You're declaring ng-scope in both the contacts table and the modal, which causes angular to create different scopes.
See this answer for more details:
https://stackoverflow.com/a/14462341/4938335
There are a few ways to solve this...
1) Put the modal HTML inside the parent element where you're already declaring ng-controller the first time - that way it will be part of the same scope
2) Use UI Bootstrap's modal directive to generate the modal with its own controller and pass in $scope.contact from your contactsController. See example here https://angular-ui.github.io/bootstrap/#/modal
3) Create a service that stores $scope.contact and inject that into a separate controller that you create for the modal. Here's someone else's fiddle that shows this:
http://jsfiddle.net/whnSs/
I'm using knockoutjs for the first time in an attempt to have a list of tasks and then to be able to click on one to show it in a popup form. The issue I'm having is with data-bind="click: $parent.edit", which calls ToDoListViewModel/edit.
When I first started writing the list code, it would pass in the current row's todo object as the first parameter into the edit function. After adding the second view model for my add/edit popup form, it no longer behaves this way. Calling $parent.edit still calls the edit function, however now it passes in an empty object { }.
It seems like I'm running into some sort of conflicting bindings issue, any ideas?
Here is the to do list html:
<table class="table table-striped table-hover" data-bind="visible: isVisible">
<thead>
<tr>
<th>To-Do</th>
<th>Estimate</th>
<th>Deadline</th>
#if (Model == null) { <th>Property</th> }
<th>Tenant</th>
<th style="display: none;">Assigned To</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: todos">
<tr data-bind="visible: isVisible()">
<td><a class="pointer" title="Edit To-Do" data-bind="click: $parent.edit, text: Task"></a></td>
<td data-bind="text: FormattedAmount"></td>
<td data-bind="text: FormattedDueDate"></td>
<td data-bind="visible: $parent.showPropertyColumn, text: PropertyName"></td>
<td data-bind="text: TenantName"></td>
<td>
<div class="btn-group pull-right">
<a class="btn btn-primary pointer default-click-action" data-bind="click: $parent.markComplete">Mark Complete</a>
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a class="pointer" data-bind="click: $parent.edit">Edit To-Do</a></li>
<li><a class="pointer" data-bind="click: $parent.remove">Delete To-Do</a></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
And here is the popup html:
#model Housters.Schemas.Models.Property.Listing
<div id="popup" class="modal fade" style="display: none;" data-bind="with: form">
#using (Html.BeginForm("SaveTodo", "Properties", FormMethod.Post, new { Id = "form", #class = "form-horizontal" }))
{
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>To-Do Information</h3>
</div>
<div class="modal-body">
<input id="id" name="id" type="hidden" data-bind="value: todo().Id" />
<input id="originalListingId" type="hidden" value="#(Model != null ? Model.IdString : "")" />
<div class="control-group #(Model != null ? " hide" : "")">
<label class="control-label" for="listingId">Property</label>
<div class="controls">
#Html.DropDownList("listingId", ViewBag.Listings as SelectList, "None",
new Dictionary<string, Object> { { "data-bind", "value: todo().ListingId" } })
</div>
</div>
<div class="control-group">
<label class="control-label" for="tenantId">Tenant</label>
<div class="controls">
<select id="tenantId" name="tenantId" class="span11" data-bind="value: todo().TenantId">
<option value="">None</option>
</select>
<p class="help-block">Is this task related to a certain tenant?</p>
</div>
</div>
<div class="control-group">
<label class="control-label" for="amount">Estimate to Complete</label>
<div class="controls">
<div class="input-prepend"><span class="add-on">$</span><input type="text" id="amount" name="todo.Amount" maxlength="10"
class="span11 number" data-bind="value: todo().Amount" /></div>
</div>
</div>
<div class="control-group">
<label class="control-label" for="dueDate">Due Date</label>
<div class="controls">
<input type="text" id="dueDate" name="todo.DueDate" maxlength="10"
class="span11 date" data-bind="value: todo().DueDate" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="task">Task<span class="required-asterisk">*</span></label>
<div class="controls">
<textarea id="task" name="todo.Task" rows="4" class="span11 required" data-bind="value: todo().Task"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn pointer" data-dismiss="modal">Close</a>
<a id="mark-complete-popup" class="btn btn-primary" data-dismiss="modal" data-bind="visible: (todo().Id && !todo().IsComplete), click: markComplete">Mark Complete</a>
<button type="button" class="btn btn-primary" data-bind="click: save">Save</button>
</div>
}
</div>
And lastly, here is the javascript defining the view models:
function ToDoList () {
if(!(this instanceof arguments.callee))
return new arguments.callee();
var parent = this;
this.showCompleted = ko.observable(false);
this.tenantFilter = new PropertyTenantFilter();
this.viewModel = {
list: new ToDoListViewModel(),
form: new ToDoFormViewModel()
};
this.init = function () {
//get all tenants.
utils.block($("#grid-content"), "Loading");
this.tenantFilter.init(function () {
//initialize view model.
ko.applyBindings(this.viewModel);
//setup controls & events.
$("#dueDate").datepicker();
$("#listingId").change(this.tenantFilter.getByListing.bind(this.tenantFilter)).change();
} .bind(this));
};
function ToDoListViewModel() {
//init.
var self = this;
self.todos = ko.observableArray([]);
//computed.
self.showPropertyColumn = ko.computed(function () {
return $("#originalListingId").val().length == 0;
});
self.isVisible = ko.computed(function () {
return _.find(self.todos(), function (todo) { return todo.isVisible(); }) != null;
});
//operations.
self.add = function () {
//set form field values.
parent.viewModel.form.fill(new schemas.ToDo({}, parent));
//show popup.
$("#popup").modal("show");
};
self.edit = function (todo) {
console.debug("edit: " + JSON.stringify(todo));
//set form field values.
parent.viewModel.form.fill(todo);
//update tenants dropdown for selected listing.
parent.tenantFilter.getByListing();
//show popup.
$("#popup").modal("show");
};
self.markComplete = function (todo) {
parent.markComplete(todo);
};
self.remove = function (todo) {
var result = confirm("Are you sure that you want to delete this To-Do?");
if (result) {
//save changes.
utils.ajax(basePath + "properties/deletetodo",
{ id: todo.Id },
function (success) {
//refresh results.
self.todos.remove(todo);
//show result.
utils.showSuccess('The To-Do has been deleted successfully');
}
);
}
};
self.toggleShowCompleted = function () {
parent.showCompleted(!parent.showCompleted());
$("#showCompletedTodos").text(parent.showCompleted() ? "Show Active" : "Show Completed");
};
self.update = function (todo) {
var existingToDo = _.find(self.todos(), function (item) { return item.Id() == todo.Id(); });
existingToDo = todo;
};
//load todos from server.
utils.ajax(basePath + "properties/gettodos",
{ id: $("#originalListingId").val(), showCompleted: parent.showCompleted() },
function (results) {
var mappedTodos = $.map(results, function (item) { return new schemas.ToDo(item, parent); });
self.todos(mappedTodos);
$("#grid-content").unblock();
}
);
}
function ToDoFormViewModel() {
//init.
var self = this;
self.todo = ko.observable({});
utils.setupPopupForm(self.saved);
//operations.
self.fill = function (todo, isEdit) {
//set form field values.
self.todo = todo;
if (todo.Id()) {
//update tenants dropdown for selected listing.
parent.tenantFilter.getByListing();
}
//show popup.
$("#popup").modal("show");
};
self.save = function (todo) {
self.todo = todo;
$("#form").submit();
};
self.saved = function (result) {
var todo = new schemas.ToDo(result.todo, parent);
if (result.isInsert)
parent.viewModel.list.todos().push(todo);
else
parent.viewModel.list.update(todo);
utils.showSuccess("Your To-Do has been saved successfully.");
};
self.markComplete = function (todo) {
parent.markComplete(todo);
};
}
this.markComplete = function (todo) {
var result = confirm("Are you sure that you want to mark this To-Do complete?");
if (result) {
//save changes.
utils.ajax(basePath + "properties/marktodocomplete", {
id: todo.Id()
},
function () {
todo.IsComplete(true);
//show success.
utils.showSuccess('Your To-Do has been marked completed');
} .bind(this)
);
}
}
this.init();
}
One possible solution is to replace:
data-bind="click: $parent.edit"
with
data-bind="click:$root.viewModel.list.edit"