please help me with this issue that I have.
Embedded in CRM on Demand I have a view that needs to take values from CRM input fields to perform a search against CRM through web service and to show a view if duplicate records are found.
Here I have the code into some libraries against CRM {crm.context.ts}:
/*
* Context Helpers
*/
declare var epmcrm: any;
class context {
private getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
config: { objects: { [key: string]: any } } = {
objects: {
"Homepage": {
name: "Homepage"
},
"Task": {
name: "Task",
idParam: "TaskDetailForm.Id",
screens: {
"/OnDemand/user/TaskDetail": "Detail",
"/OnDemand/user/TaskEdit": "Edit"
}
},
"Account": {
name: "Account",
idParam: "AccountDetailForm.Id",
screens: {
"/OnDemand/user/TaskDetail": "Detail",
"/OnDemand/user/TaskEdit": "Edit"
}
},
"User": {
name: "User",
idParam: "UserDetailForm.Id",
screens: {
"/OnDemand/user/UserDetail": "Detail",
"/OnDemand/user/UserEdit": "Edit"
}
},
"Opportunity": {
name: "Opportunity",
idParam: "OpportunityDetailForm.Id",
screens: {
"/OnDemand/user/OpportunityDetail": "Detail",
"/OnDemand/user/OpportunityEdit": "Edit"
}
},
"Contact": {
name: "Contact",
idParam: "ContactDetailForm.Id",
screens: {
"/OnDemand/user/ContactDetail": "Detail",
"/OnDemand/user/ContactEdit": "Edit",
// "/OnDemand/user/ContactInsert": "Create"
}
}
}
};
private knownPaths: { [key: string]: any } = {
"/OnDemand/user/Homepage": this.config.objects["Homepage"],
"/OnDemand/user/TaskDetail": this.config.objects["Task"],
"/OnDemand/user/TaskEdit": this.config.objects["Task"],
"/OnDemand/user/AccountDetail": this.config.objects["Account"],
"/OnDemand/user/AccountEdit": this.config.objects["Account"],
"/OnDemand/user/ContactDetail": this.config.objects["Contact"],
"/OnDemand/user/ContactEdit": this.config.objects["Contact"],
// "/OnDemand/user/ContactInsert": this.config.objects["Contact"],
"/OnDemand/user/UserDetail": this.config.objects["User"],
"/OnDemand/user/UserEdit": this.config.objects["User"],
"/OnDemand/user/OpportunityEdit": this.config.objects["Opportunity"]
//"/OnDemand/user/CustomObj2": mapping to custom objects here is important!
};
webServiceUrl: string = null;
screen: string = null;
objectType: string = null;
objectId: string = null;
ssoToken: string = null;
moduleRoot: string = null;
rowId: string = null;
commentsAction: string = null;
status: string = null;
//crm parameters to built the task link;
account: string = null;
accountId: string = null;
contact: string = null;
contactId: string = null;
quote: string = null;
quoteId: string = null;
order: string = null;
orderId: string = null;
oppty: string = null;
opptyId: string = null;
lead: string = null;
leadId: string = null;
step: string = null;
//crm contact
lastName: string = null;
firstName: string = null;
email: string = null;
constructor() {
// pick out the info from the url
this.webServiceUrl = "https://" + window.location.hostname + "/Services/Integration";
// get the SSO token from the global variable defined in the web applet
this.ssoToken = epmcrm.ssoToken;
// get the module root from the global variable defined in the web applet
this.moduleRoot = epmcrm.moduleRoot;
this.rowId = epmcrm.rowId;
this.commentsAction = epmcrm.commentsAction;
this.status = epmcrm.status;
this.step = epmcrm.step;
//crm parameters to built the task link;
this.account = epmcrm.account;
this.accountId = epmcrm.accountId;
this.contact = epmcrm.contact;
this.contactId = epmcrm.contactId;
this.quote = epmcrm.quote;
this.quoteId = epmcrm.quoteId;
this.order = epmcrm.order;
this.orderId = epmcrm.orderId;
this.oppty = epmcrm.oppty;
this.opptyId = epmcrm.opptyId;
this.lead = epmcrm.lead;
this.leadId = epmcrm.leadId;
//crm Contact
$("#ContactEditForm\\.First\\ Name").on("change", function () {
this.firstName = (<HTMLInputElement>document.getElementById("ContactEditForm.First Name")).value;
});
$("#ContactEditForm\\.Email\\ Address").on("change", function () {
this.email = (<HTMLInputElement>document.getElementById("ContactEditForm.Email Address")).value;
});
$("#ContactEditForm\\.Last\\ Name").on("change", function () {
this.lastName = (<HTMLInputElement>document.getElementById("ContactEditForm.Last Name")).value;
});
// attempt to discover contextual information
var pathname = window.location.pathname;
if (this.knownPaths[pathname]) {
var obj = this.knownPaths[pathname];
this.objectType = obj.name;
if (obj.idParam) {
this.objectId = this.getParameterByName(obj.idParam);
}
if (obj.screens) {
this.screen = obj.screens[pathname];
}
}
}
}
export = context;
In the view models I have what should give me the results into knockout observables which should than mirror CRM field and with this results I would perform a search and return or not some results:
`contactSearch.ts`
import ko = require("knockout");
import context = require("libs/crm.context");
import contacts = require("libs/crm.contacts");
$("#BTN_TB_ContactNewForm_Save").hide();
$("#BTN_TB_ContactNewForm_Save_idx_1").hide();
//$("#ContactEditForm\\.First\\ Name").on("change", assignFName);
//$("#ContactEditForm\\.Last\\ Name").on("change", assignLName);
//function assignFName() {
// var firstName = (<HTMLInputElement>document.getElementById("ContactEditForm.First Name")).value;
// alert(firstName);
//}
//function assignLName() {
// var lastName = (<HTMLInputElement>document.getElementById("ContactEditForm.Last Name")).value;
// alert(lastName);
//}
//function assignEmail() {
// var Email = (<HTMLInputElement>document.getElementById("ContactEditForm.Email Address")).value
// alert(Email);
//}
//var contactViewModel = function () {
// var self = this;
// self.validContacts = ko.observableArray([]);
// self.addContact = function (validContact) {
// self.validContacts.puch(validContact);
// $.ajax({
// data: ko.toJSON(this),
// contentType: 'application/json',
// success: function (result) {
// validContact.fName(result.
// }
// });
// }
//}
class contactSearch {
private _context: context = new context();
private _contacts: contacts = new contacts(this._context.webServiceUrl);
private _firstName = this._context.firstName;
private _lastName = this._context.lastName;
private _email = this._context.email;
vFName = ko.observable(this._firstName);
vLName = ko.observable(this._lastName);
vEmail = ko.observable(this._email);
//email = ko.computed({
// read: () => $("#ContactEditForm\\.Email\\ Address").on("change", function () {
// })
//})
////})
//lName = ko.observable("");
////email = ko.computed(function () {
//// assignEmail();
//})
isSearching: KnockoutObservable<boolean> = ko.observable(false);
searchValue = ko.computed({
read: () => ("[ContactEmail]~=" + "'" + "" + "'" + " AND [ContactFirstName]~=" + "'" + this.vFName() + "'" + " AND [ContactLastName]~=" + "'" + this.vLName() + "'")
});
contactSearchResults: KnockoutObservableArray<IContact> = ko.observableArray([]);
doValidation() {
$("#ContactEditForm\\.Email\\ Address").on("change", function () {
})
}
doContactSearch() {
this.isSearching(true);
this.contactSearchResults([]);
this._contacts
.find(this.searchValue(), ["Id", "ContactFirstName", "ContactLastName", "ContactEmail", "AccountId", "AccountName"])
.done((results: IContact[]) => {
if (results.length > 0) {
this.contactSearchResults(results);
this.isSearching(false);
}
else {
$("#BTN_TB_ContactNewForm_Save").show();
$("#BTN_TB_ContactNewForm_Save_idx_1").show();
alert("# of matching results= " + results.length);
}
});
}
bindTR(element): void {
/*
* Replicate the CRMOD method of hover styles
*/
var $element = $(element);
$element.hover(
() => {
$element.attr("_savedBGColor", $element.css("background-color"));
$element.css("background-color", "#d3dde6");
},
() => {
$element.css("background-color", $element.attr("_savedBGColor"));
$element.attr("_savedBGColor", "");
}
);
}
bindLink(element): void {
var $element = $(element);
$element.click(
() => {
window["doNavigate"]('ContactDetail?ocTitle=' + encodeURIComponent(this.vLName()) + '&ContactDetailForm.Id=' + this.contactSearchResults["Id"] + '&OCTYPE=', true, this, null)
},
() => {
$element.css("text-decoration", "underline");
}
);
}
}
export = contactSearch;
David,
I have created the observables and I'm binding those here in the HTML view model, but the source for my data are those HTMLInputElement and I don't know how pass the values to the observables.
<p>Email <input data-bind="value: vEmail" />
<span data-bind="text: vEmail"></span>
<span data-bind="text: vFName"></span>
<span data-bind="text: vLName"></span>
<p>Enter the search spec: <textarea data-bind="value: searchValue" />
<button type="button" data-bind="click: validation, click: doContactSearch">Go</button></p>
<table class="list clist" cellspacing="0">
<thead>
<tr>
<th class="m">
<th class="m">
<th>Id</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Account Id</th>
<th>Account</th>
</tr>
</thead>
<tbody data-bind="visible: isSearching">
<tr>
<td colspan="99" style="text-align:center;">Searching, please wait...</td>
</tr>
</tbody>
<tbody data-bind="foreach: contactSearchResults, afterRender: bindTR, click: bindLink">
<tr>
<td class="lrml"> </td>
<td> </td>
<td data-bind="text: Id"></td>
<td data-bind="text: ContactFirstName"></td>
<td data-bind="text: ContactLastName"></td>
<td data-bind="text: ContactEmail"></td>
<td data-bind="text: AccountId"></td>
<td data-bind="text: AccountName"></td>
</tr>
</tbody>
</table>
I have also this file that create the dependency with CRM:
var epmcrm;
((epmcrm) => {
if (!epmcrm["moduleRoot"])
throw new Error("epmcrm global variable not configured");
require.config({
baseUrl: epmcrm.moduleRoot + "/scripts/app",
paths: {
// define the libs here
// 1. External
"jquery": "//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min",
"jquery-ui.theme": "//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.theme.css",// recently added
"knockout": "//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min",
"text": "//cdnjs.cloudflare.com/ajax/libs/require-text/2.0.10/text",
"json2": "//cdnjs.cloudflare.com/ajax/libs/json2/20130526/json2.min",
"knockout.mapping": "//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping", // added by daniel
// 2. Internal
"koExtensions": "../libs/knockout-extensions",
"libs/crm.tasks": "../libs/crm.tasks",
"libs/crm.accounts": "../libs/crm.accounts",
"libs/crm.contacts": "../libs/crm.contacts",
"libs/crm.users": "../libs/crm.users",
"libs/crm.session": "../libs/crm.session",
"libs/crm.context": "../libs/crm.context",
"libs/crm.objects": "../libs/crm.objects",
"libs/crm.utilities": "../libs/crm.utilities",
"libs/crm.viewEngine": "../libs/crm.viewEngine",
"libs/crm.viewmodelEngine": "../libs/crm.viewmodelEngine"
},
shim: {
"knockout": {
deps: ["json2"]
}
}
});
require(["knockout", "knockout.mapping", "koExtensions"],
(ko: KnockoutStatic, komap: KnockoutMapping) => {
ko.mapping = komap;
ko.applyBindings({}, document.getElementById("QuoteWebAppletContainer"));
});
})(epmcrm || (epmcrm = {}));
I'm coming with the solution eventually, because I got what I need.
This were changed like this:
$("#ContactEditForm\\.First\\ Name").on("change", () => {
this.firstName((<HTMLInputElement>document.getElementById("ContactEditForm.First Name")).value);
});
$("#ContactEditForm\\.Email\\ Address").on("change", () => {
this.email((<HTMLInputElement>document.getElementById("ContactEditForm.Email Address")).value);
});
$("#ContactEditForm\\.Last\\ Name").on("change", () => {
this.lastName((<HTMLInputElement>document.getElementById("ContactEditForm.Last Name")).value);
});
this three variables I changed to be observable properties:
lastName: KnockoutObservable<string> = ko.observable("");
firstName: KnockoutObservable<string> = ko.observable("");
email: KnockoutObservable<string> = ko.observable("");
And here I've made some other changes:
vFName = this._context.firstName;
vLName = this._context.lastName;
vEmail = this._context.email;
isSearching: KnockoutObservable<boolean> = ko.observable(false);
searchValue = ko.computed(() => {
return ("[ContactEmail]~=" + "'" + ko.unwrap(this._context.email) + "'" + " AND [ContactFirstName]~=" + "'" + ko.unwrap(this._context.firstName) + "'" + " AND [ContactLastName]~=" + "'" + ko.unwrap(this._context.lastName) + "'")
});
Related
All I am trying to do is that I want to refresh my DataTable when the page is relaoded/refreshed. Right now on refreshing the page it retains its data. My application is using ASP.Net MVC tech.
Here is my Data Table and relevant functions:
$(document).ready(function () {
//debugger;
var table = $('#user_session_center_grid').DataTable({
"searching": false,
"colReorder": true,
"lengthChange": false,
"processing": true,
"serverSide": true,
"autoWidth": false,
"ajax": {
cache: false,
url: "#Url.Action("GetUserSessionHistory", "LoggedIn")",
type: 'POST',
data: function (data) {
data.SortBy = 'Month';
data.FromDate = "";
data.ToDate = "";
data.userSearch = $('#userSearch').val();
if (event) {
var objDtFilter = event.target;
if ($(objDtFilter).hasClass('dtFilter')) {
data.FromDate = event.target.getAttribute('fromdt');
data.ToDate = event.target.getAttribute('todt');
}
if ($(objDtFilter).hasClass('dtSort'))
data.SortBy = event.target.getAttribute('sort');
if ($(objDtFilter).hasClass('UserTypeFilter'))
data.value1 = event.target.getAttribute('value');
console.log(data.value1);
}
},
},
"language": {
oPaginate: {
sNext: '<i class="fa fa-chevron-right"></i>',
sPrevious: '<i class="fa fa-chevron-left"></i>',
sFirst: '<i class="fa fa-chevron-right"></i>',
sLast: '<i class="fa fa fa-chevron-left"></i>'
}
},
"columns": [
{
data: null,
class: "userName",
render: function (data, type, row) {
return "<div>" + data.FirstName + " " + data.LastName + "</div></td>";
}
},
{
data: null,
class: "startDate",
render: function (data, type, row) {
var parsedDate = JSON.parse(JSON.stringify(data.StartTime), ToJavaScriptDate);
return "<div>" + parsedDate + "</div></td>";
}
},
//{ 'data': 'User_ID' },
//{ 'data': 'FirstName' },
//{ 'data': 'LastName' },
//{ 'data': 'StartTime' },
],
});
table.on('draw', function () {
var widthofPagination = $('.dataTables_paginate.paging_simple_numbers').width() + 25;
$('#user_session_center_grid_info').css({ width: 'calc(100% - ' + widthofPagination + 'px)' });
});
$("#date_filter_ul.dropdown-menu li a").click(function () {
//debugger;
$('#date_filter_ul').removeClass('show');
$(this).closest('#CategoryFilter').find('.csp-select > span').text("Duration:" + $(this).text());
$('#user_session_center_grid').DataTable().ajax.reload();
});
$("#user_session_center_status_ul.dropdown-menu li a").click(function () {
$('#user_session_center_status_ul').removeClass('open');
$(this).closest('#StatusFilter').find('.csp-select > span').text($(this).text());
$('#user_session_center_grid').DataTable().ajax.reload();
});
});
Here are my controller functions:
public ActionResult UserSessionCenter()
{
if (Session["selectedCustomer"] == null)
{
Session["selectedCustomer"] = 9;
}
int customerId = (int)Session["selectedCustomer"];
var model = new UserSessionHistory();
var daccess = new ApplicationCommon.Ado.SqlDataAccess();
var customerNamesDt = daccess.GetUserNames(customerId);
var customerList = new List<UserSessionData>();
for (var i = 0; i < customerNamesDt.Rows.Count; i++)
{
var userinfo = new UserSessionData();
userinfo.CustomerId = customerNamesDt?.Rows[i]["Customer_Id"].ToString() ?? "";
userinfo.CustomerName = customerNamesDt?.Rows[i]["Customer_Name"].ToString() ?? "";
userinfo.UserId = customerNamesDt?.Rows[i]["User_ID"].ToString() ?? "";
userinfo.UserName = customerNamesDt?.Rows[i]["UserName"].ToString() ?? "";
customerList.Add(userinfo);
}
model.UserInfoList = customerList;
return View(model);
}
[HttpPost]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public JsonResult GetUserSessionHistory()
{
var draw = Request.Form.GetValues("draw").FirstOrDefault();
var start = Request.Form.GetValues("start").FirstOrDefault();
var length = Request.Form.GetValues("length").FirstOrDefault();
if (Request["value1"] != null)
Session["userId"] = Request["value1"];
int pageSize = length != null ? Convert.ToInt32(length) : 0;
try
{
var UserID = Convert.ToInt32(Session["userId"]);
var filterModel = new FilterSessionHistoryModel();
filterModel.UserID = UserID;
filterModel.Skip = int.Parse(start);
filterModel.Take = int.Parse(length);
var fromDate = Request["FromDate"];
var toDate = Request["ToDate"];
filterModel.userSearch = Request["userSearch"];
if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate))
{
filterModel.DateFrom = fromDate;
filterModel.DateTo = toDate;
}
UserService userService = new UserService();
List<ADM_User_Session_History_Result> SessionList = userService.ADM_User_Session_History(filterModel);
int? numberOfRecords = 0;
if(SessionList.Count>0) {
numberOfRecords=SessionList[0].NumberOfRecords;
}
return Json(new { draw = draw, recordsFiltered = (int)numberOfRecords, recordsTotal = (int)numberOfRecords, data = SessionList }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
CustomLogging.LogMessage(ex.Message + "/r/n" + ex.Source + "/r/n" + ex.StackTrace, LogType.Error);
return this.GetJSONObject(false, ex.Message.ToString());
}
}
The hierarchy is like this.
1. The Admin user can use any customer
2. One customer can have many users
3. The table shows the time of log in of the user selected.
The problem is that the list through which the Admin can change the Customer is in the top nav present in _Layout.cshtml where as this data table is in another view. The data changes fine while cahanging the user but I need it to reload or get empty when the customer is changed.
old browsers like 'explorer' keep caching the info from a first ajax call,
you can try to set the cash flag to false and the table will be updated each time
function ReLoadTable() {
$.ajax({
cache: false,
url: 'Your URL',
type: 'POST',
in controller put this attribute
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class HomeController : Controller
{
finally in your Global.asax file add this function
protected void Application_PreSendRequestHeaders(Object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(false);
}
How can I override the vendor/magento/module-configurable-product/view/adminhtml/web/js/variations/steps/summary.js to local. I have tried https://magento.stackexchange.com/questions/60276/extending-overriding-js-in-magento-2 this but no luck
Finally I got the answer
I have written a module, which contain a requirejs-config.js under the path Vendor/Module/view/adminhtml/requirejs-config.js and having
var config = {
"map": {
"*": {
"Magento_ConfigurableProduct/js/variations/steps/summary": "Vendor_Module/js/variations/steps/summary-custom",
}
}
}
and in Vendor/Module/view/adminhtml/web/js/variations/steps/summary-custom.js , I have
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
// jscs:disable jsDoc
define([
'uiComponent',
'jquery',
'ko',
'underscore',
'mage/translate',
'Magento_ConfigurableProduct/js/variations/steps/summary'
], function (Component, $, ko, _, $t, summary) {
'use strict';
return Component.extend({
defaults: {
modules: {
variationsComponent: '${ $.variationsComponent }'
},
notificationMessage: {
text: null,
error: null
},
gridExisting: [],
gridNew: [],
gridDeleted: [],
attributes: [],
attributesName: [$.mage.__('Images'), $.mage.__('SKU'), $.mage.__('Quantity'), $.mage.__('Cost'), $.mage.__('Msrp'), $.mage.__('Price')],
sections: [],
gridTemplate: 'Magento_ConfigurableProduct/variations/steps/summary-grid'
},
initObservable: function () {
this._super().observe('gridExisting gridNew gridDeleted attributes sections');
this.gridExisting.columns = ko.observableArray();
this.gridNew.columns = ko.observableArray();
this.gridDeleted.columns = ko.observableArray();
return this;
},
nextLabelText: $.mage.__('Generate Products'),
variations: [],
generateGrid: function (variations, getSectionValue) {
var productSku = this.variationsComponent().getProductValue('sku'),
productPrice = this.variationsComponent().getProductValue('price'),
productWeight = this.variationsComponent().getProductValue('weight'),
variationsKeys = [],
gridExisting = [],
gridNew = [],
gridDeleted = [];
this.variations = [];
_.each(variations, function (options) {
var product, images, sku, quantity, cost, msrp, price, variation,
productId = this.variationsComponent().getProductIdByOptions(options);
if (productId) {
product = _.findWhere(this.variationsComponent().variations, {
productId: productId
});
}
images = getSectionValue('images', options);
sku = productSku + _.reduce(options, function (memo, option) {
return memo + '-' + option.label;
}, '');
quantity = getSectionValue('quantity', options);
if (!quantity && productId) {
quantity = product.quantity;
}
msrp = product.msrp;
cost = product.cost;
price = getSectionValue('price', options);
if (!price) {
price = productId ? product.price : productPrice;
}
if (productId && !images.file) {
images = product.images;
}
variation = {
options: options,
images: images,
sku: sku,
quantity: quantity,
cost: cost,
msrp: msrp,
price: price,
productId: productId,
weight: productWeight,
editable: true
};
if (productId) {
variation.sku = product.sku;
variation.weight = product.weight;
gridExisting.push(this.prepareRowForGrid(variation));
} else {
gridNew.push(this.prepareRowForGrid(variation));
}
this.variations.push(variation);
variationsKeys.push(this.variationsComponent().getVariationKey(options));
}, this);
this.gridExisting(gridExisting);
this.gridExisting.columns(this.getColumnsName(this.wizard.data.attributes));
if (gridNew.length > 0) {
this.gridNew(gridNew);
this.gridNew.columns(this.getColumnsName(this.wizard.data.attributes));
}
_.each(_.omit(this.variationsComponent().productAttributesMap, variationsKeys), function (productId) {
gridDeleted.push(this.prepareRowForGrid(
_.findWhere(this.variationsComponent().variations, {
productId: productId
})
));
}.bind(this));
if (gridDeleted.length > 0) {
this.gridDeleted(gridDeleted);
this.gridDeleted.columns(this.getColumnsName(this.variationsComponent().productAttributes));
}
},
prepareRowForGrid: function (variation) {
var row = [];
row.push(_.extend({
images: []
}, variation.images));
row.push(variation.sku);
row.push(variation.quantity);
row.push(variation.cost);
row.push(variation.msrp);
_.each(variation.options, function (option) {
row.push(option.label);
});
row.push(this.variationsComponent().getCurrencySymbol() + ' ' + variation.price);
return row;
},
getGridTemplate: function () {
return this.gridTemplate;
},
getGridId: function () {
return _.uniqueId('grid_');
},
getColumnsName: function (attributes) {
var columns = this.attributesName.slice(0);
attributes.each(function (attribute, index) {
columns.splice(5 + index, 0, attribute.label);
}, this);
return columns;
},
render: function (wizard) {
this.wizard = wizard;
this.sections(wizard.data.sections());
this.attributes(wizard.data.attributes());
this.gridNew([]);
this.gridExisting([]);
this.gridDeleted([]);
this.generateGrid(wizard.data.variations, wizard.data.sectionHelper);
},
force: function () {
this.variationsComponent().render(this.variations, this.attributes());
$('[data-role=step-wizard-dialog]').trigger('closeModal');
},
back: function () {
}
});
});
What I need here was to show the "Msrp" and the "Cost" while editing the variations
I'm starting to learn and azure phonejs.
Todo list get through a standard example:
$(function() {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
// Read current data and rebuild UI.
// If you plan to generate complex UIs like this, consider using a JavaScript templating library.
function refreshTodoItems() {
var query = todoItemTable.where({ complete: false });
query.read().then(function(todoItems) {
var listItems = $.map(todoItems, function(item) {
return $('<li>')
.attr('data-todoitem-id', item.id)
.append($('<button class="item-delete">Delete</button>'))
.append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
.append($('<div>').append($('<input class="item-text">').val(item.text)));
});
$('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
$('#summary').html('<strong>' + todoItems.length + '</strong> item(s)');
}, handleError);
}
function handleError(error) {
var text = error + (error.request ? ' - ' + error.request.status : '');
$('#errorlog').append($('<li>').text(text));
}
function getTodoItemId(formElement) {
return $(formElement).closest('li').attr('data-todoitem-id');
}
// Handle insert
$('#add-item').submit(function(evt) {
var textbox = $('#new-item-text'),
itemText = textbox.val();
if (itemText !== '') {
todoItemTable.insert({ text: itemText, complete: false }).then(refreshTodoItems, handleError);
}
textbox.val('').focus();
evt.preventDefault();
});
// Handle update
$(document.body).on('change', '.item-text', function() {
var newText = $(this).val();
todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
});
$(document.body).on('change', '.item-complete', function() {
var isComplete = $(this).prop('checked');
todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
});
// Handle delete
$(document.body).on('click', '.item-delete', function () {
todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
});
// On initial load, start by fetching the current data
refreshTodoItems();
});
and it works!
Changed for the use of phonejs and the program stops working, even mistakes does not issue!
This my View:
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<button data-bind="click: incrementClickCounter">Click me</button>
<span data-bind="text: listData"></span>
<div data-bind="dxList:{
dataSource: listData,
itemTemplate:'toDoItemTemplate'}">
<div data-options="dxTemplate:{ name:'toDoItemTemplate' }">
<div style="float:left; width:100%;">
<h1 data-bind="text: name"></h1>
</div>
</div>
</div>
</div>
This my ViewModel:
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = ko.observableArray([
{ name: "111", type: "111" },
{ name: "222", type: "222" }]);
var query = todoItemTable.where({ complete: false });
query.read().then(function (todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
});
var viewModel = {
listData: toDoArray,
incrementClickCounter: function () {
todoItemTable = client.getTable('todoitem');
toDoArray.push({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
};
I can easily add items to the list of programs, but from the server list does not come:-(
I am driven to exhaustion and can not solve the problem for 3 days, which is critical for me!
Specify where my mistake! Thank U!
I suggest you use a DevExpress.data.DataSource and a DevExpress.data.CustomStore instead of ko.observableArray.
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = [];
var store = new DevExpress.data.CustomStore({
load: function(loadOptions) {
var d = $.Deferred();
if(toDoArray.length) {
d.resolve(toDoArray);
} else {
todoItemTable
.where({ complete: false })
.read()
.then(function(todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
d.resolve(toDoArray);
});
}
return d.promise();
},
insert: function(values) {
return toDoArray.push(values) - 1;
},
remove: function(key) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray.splice(key, 1);
},
update: function(key, values) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray[key] = $.extend(true, toDoArray[key], values);
}
});
var source = new DevExpress.data.DataSource(store);
// older version
store.modified.add(function() { source.load(); });
// starting from 14.2:
// store.on("modified", function() { source.load(); });
var viewModel = {
listData: source,
incrementClickCounter: function () {
store.insert({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
}
You can read more about it here and here.
I'm using Twitter Bootstrap together with knockout.js.
I have an orders page where the cashier can choose a customer and products that the customer wants to buy. However, I get some very strange behavior. When I add one product to the cart the correct function addToCart is called, but also the function removeFromCart is called without me telling the program to call it. I guess something is happening because I use Bootstrap modals.
Please help. Here is the fiddle http://jsfiddle.net/rY59d/4/ .
HTML Code:
<div id="create-order-main">
<h2>Create new order</h2>
<a class="btn btn-primary" data-toggle="modal" data-target="#select-products2"><b>+</b> Add products</a>
<div>
<div id="create-order-select-products" data-bind="with: productVM">
<div class="modal fade" id="select-products2" tabindex="-1" role="dialog" aria-labelledby="selectProducts2Label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title">Add products</h4>
</div>
<div class="modal-body">
<table class="table table-bordered table-with-records" data-bind="triggerUpdate: Products, visible: Products().length > 0">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: filteredProducts2">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td data-bind="text: price"></td>
<td><input type="number" min="0" step="1" value="1"></td>
<td data-bind="attr: { value: $index }, click: $parent.selectedProduct2"><a class="btn btn-primary" data-bind="click: $parent.addToCart">Add to cart</a></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
<button type="submit" class="btn btn-primary" data-dismiss="modal">
Choose
</button>
</form>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.modal -->
</div>
<div data-bind="with: productVM">
<table class="table table-bordered table-with-records" data-bind="triggerUpdate: cart, visible: cart().length > 0">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: cart">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td data-bind="text: price"></td>
<td data-bind="attr: { value: $index }, click: $parent.selectedProductInOrder"><a class="btn btn-primary" data-bind="click: $parent.removeFromCart($data)">Remove</a></td>
</tr>
</tbody>
</table>
</div>
</div>
JavaScript Code:
/**
* -----------
* Viewmodels.js
* -----------
*
* Contains Knockout.js Viewmodels
*
*/
// CustomerViewModel starts here
var CustomerViewModel = function () {
var self = this;
var stringStartsWith = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
self.name = ko.observable("");
self.surname = ko.observable("");
self.email = ko.observable("");
self.query = ko.observable();
self.Customers = ko.observableArray();
self.filterId = ko.observable("");
self.filterName = ko.observable("");
self.filterSurname = ko.observable("");
// Used for search in "Create Order" view
self.filterId2 = ko.observable("");
self.filterName2 = ko.observable("");
self.filterSurname2 = ko.observable("");
function Customer(id, name, surname, email) {
this.id = id;
this.name = name;
this.surname = surname;
this.email = email;
}
self.selectedCustomer = ko.observable(null);
// Used for search in "Create Order" view
self.selectedCustomer2 = ko.observable(null);
self.getId = function () {
var idCounter;
if (self.Customers().length === 0) {
idCounter = 0;
} else {
idCounter = self.Customers()[self.Customers().length - 1]['id'];
}
return (++idCounter);
};
$.getJSON("api/customers", function (data) {
self.Customers(data);
});
self.Customers.push(new Customer(1,"John","Smith","john#smith.com"));
self.Customers.push(new Customer(2,"Maria","Jones","maria#jones.com"));
self.Customers.push(new Customer(3,"Alexander","Stevenson","alexander#stevenson.com"));
self.clearSearchCustomers = function () {
self.filterId("");
self.filterName("");
self.filterSurname("");
};
// Used in the "Create new Order" view
self.clearSearchCustomers2 = function () {
self.filterId2("");
self.filterName2("");
self.filterSurname2("");
self.selectedCustomer2("");
};
self.selectCustomer = function () {
self.selectedCustomer(this);
};
self.chooseCustomerInSearch = function () {
$('#select-customer2').modal('toggle');
};
self.createNewCustomer = function () {
var customer = new Customer(self.getId(), self.name(), self.surname(), self.email());
$.ajax({
type: "POST",
url: 'api/customers',
data: ko.toJSON({
data: customer
}),
success: function (result) {
self.Customers.push(customer);
self.name("");
self.surname("");
self.email("");
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
$('#create-customer').modal('toggle');
};
self.deleteItem = function ($this) {
$.ajax({
type: "DELETE",
url: 'api/customers/' + this.id,
success: function (result) {
self.Customers.remove($this);
$('#delete-customer').modal('toggle');
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
};
self.callEditCustomerFromViewCustomer = function () {
$('#display-customer').modal('toggle');
$('#edit-customer').modal('toggle');
};
self.editCustomer = function ($this) {
var customer = self.selectedCustomer();
$.ajax({
type: "PUT",
url: 'api/customers/' + this.id,
contentType: 'application/json',
data: ko.toJSON({
data: customer
}),
success: function (result) {
self.Customers.remove($this);
self.Customers.push($this);
$('#edit-customer').modal('toggle');
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
};
self.filteredCustomer = ko.computed(function () {
var filterTextId = self.filterId().toLowerCase(),
filterTextName = self.filterName().toLowerCase(),
filterTextSurname = self.filterSurname().toLowerCase();
if (!filterTextId && !filterTextName && !filterTextSurname) {
return self.Customers();
} else {
if (self.Customers() != 'undefined' && self.Customers() !== null && self.Customers().length > 0) {
return ko.utils.arrayFilter(self.Customers(), function (item) {
return (stringStartsWith(item.id.toLowerCase(), filterTextId) && stringStartsWith(item.name.toLowerCase(), filterTextName) && stringStartsWith(item.surname.toLowerCase(), filterTextSurname));
});
}
}
});
// Used for the "Create New Order" view
self.filteredCustomer2 = ko.computed(function () {
var filterTextId2 = self.filterId2().toLowerCase();
var filterTextName2 = self.filterName2().toLowerCase();
var filterTextSurname2 = self.filterSurname2().toLowerCase();
if (!filterTextId2 && !filterTextName2 && !filterTextSurname2) {
return self.Customers();
} else {
if (self.Customers() != 'undefined' && self.Customers() !== null && self.Customers().length > 0) {
return ko.utils.arrayFilter(self.Customers(), function (item) {
return (stringStartsWith(item.id.toLowerCase(), filterTextId2) && stringStartsWith(item.name.toLowerCase(), filterTextName2) && stringStartsWith(item.surname.toLowerCase(), filterTextSurname2));
});
}
}
});
};
// Product View Model starts here
var ProductViewModel = function () {
var stringStartsWith = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
function Product(id, name, price) {
this.id = id;
this.name = name;
this.price = price;
}
var self = this;
self.name = ko.observable("");
self.price = ko.observable("");
self.filterId = ko.observable("");
self.filterName = ko.observable("");
self.filterPrice = ko.observable("");
self.selectedProduct = ko.observable(null);
self.Products = ko.observableArray();
// used for "create new order - add items" view
self.filterProductId2 = ko.observable("");
self.filterProductName2 = ko.observable("");
self.filterProductPrice2 = ko.observable("");
self.selectedProduct2 = ko.observable(null);
self.selectedProductInOrder = ko.observable("");
self.cart = ko.observableArray("");
self.addToCart = function () {
alert("Item added to cart");
self.cart.push(this);
};
self.removeFromCart = function ($this) {
alert("this is a test");
// self.cart.remove($this);
};
self.getId = function () {
var idCounter;
if (self.Products().length === 0) {
idCounter = 0;
} else {
idCounter = self.Products()[self.Products().length - 1]['id'];
}
return (++idCounter);
};
self.clearSearchProducts = function () {
self.filterId("");
self.filterName("");
self.filterPrice("");
};
self.clearSearchProducts2 = function () {
self.filterProductId2("");
self.filterProductName2("");
self.filterProductPrice2("");
};
$.getJSON("api/products", function (data) {
self.Products(data);
});
self.Products.push(new Product(1,"product 1", "300"));
self.Products.push(new Product(2,"product 2", "400"));
self.Products.push(new Product(3,"product 3", "500"));
self.Products.push(new Product(4,"product 4", "600"));
self.createNewProduct = function () {
var product = new Product(self.getId(), self.name(), self.price());
$.ajax({
type: "POST",
url: 'api/products',
data: ko.toJSON({
data: product
}),
success: function (result) {
self.Products.push(product);
self.name("");
self.price("");
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
$('#create-product').modal('toggle');
};
self.deleteItem = function ($this) {
$.ajax({
type: "DELETE",
url: 'api/products/' + this.id,
success: function (result) {
self.Products.remove($this);
$('#delete-product').modal('toggle');
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
};
self.callEditProductFromViewProduct = function () {
$('#display-product').modal('toggle');
$('#edit-product').modal('toggle');
};
self.editProduct = function ($this) {
var product = self.selectedProduct();
$.ajax({
type: "PUT",
url: 'api/products/' + this.id,
contentType: 'application/json',
data: ko.toJSON({
data: product
}),
success: function (result) {
self.Products.remove($this);
self.Products.push($this);
$('#edit-product').modal('toggle');
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
};
self.filteredProducts = ko.computed(function () {
var filterTextId = self.filterId().toLowerCase(),
filterTextName = self.filterName().toLowerCase(),
filterTextPrice = self.filterPrice().toLowerCase();
if (!filterTextId && !filterTextName && !filterTextPrice) {
return self.Products();
} else {
if (self.Products() !== 'undefined' && self.Products() !== null && self.Products().length > 0) {
return ko.utils.arrayFilter(self.Products(), function (item) {
return (stringStartsWith(item.id.toLowerCase(), filterTextId) && stringStartsWith(item.name.toLowerCase(), filterTextName) && stringStartsWith(item.price.toLowerCase(), filterTextPrice));
});
}
}
});
// used for "create new order - add item" view
self.filteredProducts2 = ko.computed(function () {
var filterProductTextId2 = self.filterProductId2().toLowerCase(),
filterProductTextName2 = self.filterProductName2().toLowerCase(),
filterProductTextPrice2 = self.filterProductPrice2().toLowerCase();
if (!filterProductTextId2 && !filterProductTextName2 && !filterProductTextPrice2) {
return self.Products();
} else {
if (self.Products() !== 'undefined' && self.Products() !== null && self.Products().length > 0) {
return ko.utils.arrayFilter(self.Products(), function (item) {
return (stringStartsWith(item.id.toLowerCase(), filterProductTextId2) && stringStartsWith(item.name.toLowerCase(), filterProductTextName2) && stringStartsWith(item.price.toLowerCase(), filterProductTextPrice2));
});
}
}
});
};
// CustomerOrderViewModel starts here
var CustomerOrderViewModel = function () {
function CustomerOrder(id, date, customer, details) {
this.id = id;
this.date = name;
this.customer = customer;
this.details = details;
}
var self = this;
self.id = ko.observable("");
self.date = ko.observable();
self.customer = ko.observable("");
self.details = ko.observable("");
self.selectedOrder = ko.observable(null);
self.CustomerOrders = ko.observableArray("");
var newOrder = {
id: 1,
date: "10/10/20",
customer: "ThecUstomeRhere",
details: "sajdasdj"
};
self.createOrder = function () {
alert("Order is created!")
};
self.CustomerOrders.push(newOrder);
self.callEditOrderFromViewOrder = function () {
$('#display-order').modal('toggle');
$('#edit-order').modal('toggle');
};
self.deleteItem = function ($this) {
$.ajax({
type: "DELETE",
url: 'api/orders/' + this.id,
success: function (result) {
self.CustomerOrders.remove($this);
$('#delete-order').modal('toggle');
},
error: function (err) {
$('#delete-order').modal('toggle');
alert(err.status + " - " + err.statusText);
}
});
};
self.editOrderItem = function ($this) {
var selectedCustomerOrder = self.selectedOrder();
$.ajax({
type: "PUT",
url: 'api/orders/' + this.id,
contentType: 'application/json',
data: ko.toJSON({
data: selectedCustomerOrder
}),
success: function (result) {
self.CustomerOrders.remove($this);
self.CustomerOrders.push($this);
$('#edit-order').modal('toggle');
},
error: function (err) {
alert(err.status + " - " + err.statusText);
}
});
};
};
var masterVM = {
customerVM: new CustomerViewModel(),
productVM: new ProductViewModel(),
customerOrderVM: new CustomerOrderViewModel()
};
ko.applyBindings(masterVM);
Your binding for the removeFromCart is wrong. It calls the function when you bind, which happens when the cart observable array changes as it is in a foreach binding.
Replace click: $parent.removeFromCart($data)
With click: $parent.removeFromCart
Demo
I am looking to run the following controller but im having trouble with scope.
I have a service that calls two functions that retrieve meta data to populate scope variables.
The issue is that using the service to call back the data interferes with other actions happening in the code. I have a directive on a tag that shows/hides an error on the span element once the rule is validated. This is now not functioning correctly. I run the code without asynchronous functions then everything works correctly.
My Plunker code is here
and the plunker of the desired behaviour is here
Plunker working example without dynamic data loading
<form class="form-horizontal">
<div class="control-group" ng-repeat="field in viewModel.Fields">
<label class="control-label">{{field.label}}</label>
<div class="controls">
<input type="text" id="{{field.Name}}" ng-model="field.data" validator="viewModel.validator" ruleSetName="{{field.ruleSet}}"/>
<span validation-Message-For="{{field.Name}}"></span>
</div>
</div>
<button ng-click="save()">Submit</button>
</form>
How do I get all bindings to update so everything is sync and loaded correctly?
angular.module('dataApp', ['servicesModule', 'directivesModule'])
.controller('dataCtrl', ['$scope', 'ProcessService', 'ValidationRuleFactory', 'Validator',
function($scope, ValidationRuleFactory, Validator, ProcessService) {
$scope.viewModel = {};
var FormFields = {};
// we would get this from the meta api
ProcessService.getProcessMetaData().then(function(data) {
alert("here");
FormFields = {
Name: "Course",
Fields: [{
type: "text",
Name: "name",
label: "Name",
data: "",
required: true,
ruleSet: "personFirstNameRules"
}, {
type: "text",
Name: "description",
label: "Description",
data: "",
required: true,
ruleSet: "personEmailRules"
}]
};
$scope.viewModel.Fields = FormFields;
ProcessService.getProcessRuleData().then(function(data) {
var genericErrorMessages = {
required: 'Required',
minlength: 'value length must be at least %s characters',
maxlength: 'value length must be less than %s characters'
};
var rules = new ValidationRuleFactory(genericErrorMessages);
$scope.viewModel.validationRules = {
personFirstNameRules: [rules.isRequired(), rules.minLength(3)],
personEmailRules: [rules.isRequired(), rules.minLength(3), rules.maxLength(7)]
};
$scope.viewModel.validator = new Validator($scope.viewModel.validationRules);
});
});
var getRuleSetValuesMap = function() {
return {
personFirstNameRules: $scope.viewModel.Fields[0].data,
personEmailRules: $scope.viewModel.Fields[1].data
};
};
$scope.save = function() {
$scope.viewModel.validator.validateAllRules(getRuleSetValuesMap());
if ($scope.viewModel.validator.hasErrors()) {
$scope.viewModel.validator.triggerValidationChanged();
return;
} else {
alert('person saved in!');
}
};
}
]);
The validation message directive is here
(function(angular, $) {
angular.module('directivesModule')
.directive('validationMessageFor', [function() {
return {
restrict: 'A',
scope: {eID: '#val'},
link: function(scope, element, attributes) {
//var errorElementId = attributes.validationMessageFor;
attributes.$observe('validationMessageFor', function(value) {
errorElementId = value;
//alert("called");
if (!errorElementId) {
return;
}
var areCustomErrorsWatched = false;
var watchRuleChange = function(validationInfo, rule) {
scope.$watch(function() {
return validationInfo.validator.ruleSetHasErrors(validationInfo.ruleSetName, rule.errorCode);
}, showErrorInfoIfNeeded);
};
var watchCustomErrors = function(validationInfo) {
if (!areCustomErrorsWatched && validationInfo && validationInfo.validator) {
areCustomErrorsWatched = true;
var validator = validationInfo.validator;
var rules = validator.validationRules[validationInfo.ruleSetName];
for (var i = 0; i < rules.length; i++) {
watchRuleChange(validationInfo, rules[i]);
}
}
};
// get element for which we are showing error information by id
var errorElement = $("#" + errorElementId);
var errorElementController = angular.element(errorElement).controller('ngModel');
var validatorsController = angular.element(errorElement).controller('validator');
var getValidationInfo = function() {
return validatorsController && validatorsController.validationInfoIsDefined() ? validatorsController.validationInfo : null;
};
var validationChanged = false;
var subscribeToValidationChanged = function() {
if (validatorsController.validationInfoIsDefined()) {
validatorsController.validationInfo.validator.watchValidationChanged(function() {
validationChanged = true;
showErrorInfoIfNeeded();
});
// setup a watch on rule errors if it's not already set
watchCustomErrors(validatorsController.validationInfo);
}
};
var getErrorMessage = function(value) {
var validationInfo = getValidationInfo();
if (!validationInfo) {
return '';
}
var errorMessage = "";
var errors = validationInfo.validator.errors[validationInfo.ruleSetName];
var rules = validationInfo.validator.validationRules[validationInfo.ruleSetName];
for (var errorCode in errors) {
if (errors[errorCode]) {
var errorCodeRule = _.findWhere(rules, {errorCode: errorCode});
if (errorCodeRule) {
errorMessage += errorCodeRule.validate(value).errorMessage;
break;
}
}
}
return errorMessage;
};
var showErrorInfoIfNeeded = function() {
var validationInfo = getValidationInfo();
if (!validationInfo) {
return;
}
var needsAttention = validatorsController.ruleSetHasErrors() && (errorElementController && errorElementController.$dirty || validationChanged);
if (needsAttention) {
// compose and show error message
var errorMessage = getErrorMessage(element.val());
// set and show error message
element.text(errorMessage);
element.show();
} else {
element.hide();
}
};
subscribeToValidationChanged();
if (errorElementController)
{
scope.$watch(function() {
return errorElementController.$dirty;
}, showErrorInfoIfNeeded);
}
scope.$watch(function() {
return validatorsController.validationInfoIsDefined();
}, subscribeToValidationChanged());
});
}
};
}]);
})(angular, $);