i am trying to use Angular-Formly to build dynamically forms starting from a set of .NET classes.
I serialize the class properties information in json and return that to Formly, but no fields is shown.
I follow suggestions find in: How to load angular-formly vm.fields object from remotely-generated json?
but not seems to work for me.
My form code is:
<form ng-submit="vm.onSubmit()" novalidate>
<formly-form model="vm.model" fields="vm.formFields">
<button type="submit" class="btn btn-primary submit-button">Submit</button>
</formly-form>
</form>
The angular code is:
<script>
/* global angular */
(function () {
'use strict';
var app = angular.module('formlyExample', ['formly', 'formlyBootstrap'], function config(formlyConfigProvider) {
// set templates here
//formlyConfigProvider.setType({
// name: 'custom',
// templateUrl: 'custom.html'
//});
});
app.factory('User', function ($http) {
return {
getFields: getFields
};
function getFields() {
return $http.post('TestFormly.aspx/LoadData', { headers: { 'Cache-Control': 'no-cache' } });
}
});
app.controller('MainCtrl', function MainCtrl($scope, $http, User) {
var vm = this;
// funcation assignment
vm.onSubmit = onSubmit;
vm.loadingData = User.getFields().then(function (result) {
vm.fields = JSON.parse(result.data.d);
vm.originalFields = angular.copy(vm.fields);
});
vm.model = {
};
// function definition
function onSubmit() {
alert(JSON.stringify(vm.model), null, 2);
}
});
})();
</script>
The CSharp.Net code is:
[WebMethod]
public static string LoadData()
{
string retValue = null;
List<FieldItem> m_fields = new List<FieldItem>();
FieldItem item1 = new FieldItem();
item1.key = "text";
item1.type = "input";
item1.templateOptions = new TemplateOptions() { label = "Text", placeholder = "Formly is terrific!" };
FieldItem item2 = new FieldItem();
item2.key = "story";
item2.type = "textarea";
item2.templateOptions = new TemplateOptions() { label = "Some sweet story", placeholder = "It allows you to build and maintain your forms with the ease of JavaScript :-)" };
m_fields.Add(item1);
m_fields.Add(item2);
retValue = JsonConvert.SerializeObject(m_fields);
return retValue;
}
The JSON result is:
[
{
"key":"text",
"type":"input",
"templateOptions":{
"label":"Text",
"placeholder":"Formly is terrific!"
}
},
{
"key":"story",
"type":"textarea",
"templateOptions":{
"label":"Some sweet story",
"placeholder":"It allows you to build and maintain your forms with the ease of JavaScript :-)"
}
}
]
Debugging with firebug i see the JSON passed correctly to the vm.fields but no input box is shown, only the Sumbit button.
I noticed that nor the Formly example shows the fields.
Can you help ?
Thanks in advance,
Giuseppe.
Here is a solution I've quickly hacked as a proof of concept for myself.
Basically the FormlyModelBuilder does scan a ViewModel class and builds a formly fields model.
Sample usage
public IActionResult Index()
{
return Ok(new ViewModels.Account.FormlyModelBuilder<ViewModels.Account.RegisterViewModel>().JsonStringify(new ViewModels.Account.RegisterViewModel { Email = "test#test.com" }));
}
Transforms this
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare ("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
into this
{
"fields": [{
"key": "Email",
"type": "email",
"templateOptions": {
"isRequired": false,
"label": "Email"
},
"expressionProperties": {
"templateOptions.focus": "Email"
}
}, {
"key": "Password",
"type": "password",
"templateOptions": {
"isRequired": false,
"label": "Password"
},
"expressionProperties": {}
}, {
"key": "ConfirmPassword",
"type": "password",
"templateOptions": {
"label": "Confirm password"
},
"expressionProperties": {}
}],
"model": {
"email": "test#test.com"
},
"expressionProperties": {}
}
Source Code
public class FormlyModelBuilder<T>
{
internal T GetAttributeFrom<T>(object instance, string propertyName) where T : Attribute
{
var property = instance.GetType().GetProperty(propertyName);
return GetAttributeFrom<T>(property);
}
internal T GetAttributeFrom<T>(PropertyInfo property) where T : Attribute
{
var attrType = typeof(T);
T t = (T)property.GetCustomAttributes(attrType, false).FirstOrDefault();
if (t == null)
{
var metaAttr = (MetadataTypeAttribute[])property.ReflectedType.GetCustomAttributes(typeof(MetadataTypeAttribute), true);
if (metaAttr.Length > 0)
{
foreach (MetadataTypeAttribute attr in metaAttr)
{
var subType = attr.MetadataClassType;
var pi = subType.GetField(property.Name);
if (pi != null)
{
t = (T)pi.GetCustomAttributes(attrType, false).FirstOrDefault();
return t;
}
}
}
}
else
{
return t;
}
return null;
}
internal FormlyModel<T> Build(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
var modelType = typeof(T);
var model = new FormlyModel<T>(dataModel);
foreach (var property in modelType.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Public))
{
var type = GetAttributeFrom<DataTypeAttribute>(property);
var field = new FormlyField(property.Name, GetInputTypeFromDataType(type?.DataType, property.PropertyType));
model.AddField(field);
//
var display = GetAttributeFrom<DisplayAttribute>(property);
field.TemplateOptions.Label = display?.Name;
//
var required = GetAttributeFrom<RequiredAttribute>(property);
field.TemplateOptions.IsRequired = required?.AllowEmptyStrings;
//
}
var focusField = model.Fields.First();
focusField.ExpressionProperties["templateOptions.focus"] = focusField.Key;
return model;
}
internal string JsonStringify(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
var dcr = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
//
return Newtonsoft.Json.JsonConvert.SerializeObject(Build(dataModel),
new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = dcr
});
}
private string GetInputTypeFromDataType(DataType? dataType, Type propertyType)
{
if (dataType != null)
{
//
switch (dataType)
{
case DataType.Text:
return "input";
case DataType.Password:
return "password";
case DataType.EmailAddress:
return "email";
case DataType.Html:
case DataType.MultilineText:
case DataType.Custom:
case DataType.DateTime:
case DataType.Date:
case DataType.Time:
case DataType.Duration:
case DataType.PhoneNumber:
case DataType.Currency:
case DataType.Url:
case DataType.ImageUrl:
case DataType.CreditCard:
case DataType.PostalCode:
case DataType.Upload:
default:
break;
}
}
switch (propertyType.Name)
{
case nameof(System.Boolean):
return "checkbox";
default:
return "input";
}
}
}
internal class FormlyModel<T>
{
internal FormlyModel(T dataModel)
{
if (dataModel == null) throw new ArgumentNullException(nameof(dataModel));
//
this.Fields = new List<FormlyField>();
this.Model = dataModel;
this.ExpressionProperties = new Dictionary<string, string>();
}
internal IEnumerable<FormlyField> Fields { get; }
internal T Model { get; }
internal Dictionary<string, string> ExpressionProperties { get; }
internal void AddField(FormlyField field)
{
if (field == null) new ArgumentNullException(nameof(field));
//
((List<FormlyField>)this.Fields).Add(field);
}
}
internal class FormlyField
{
internal FormlyField(string key, string type)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentNullException(nameof(type));
//
TemplateOptions = new TemplateOptions();
ExpressionProperties = new Dictionary<string, string>();
Key = key;
Type = type;
}
internal string Key { get; }
internal string Type { get; }
internal string HideExpression { get; set; }
internal TemplateOptions TemplateOptions { get; }
internal Dictionary<string, string> ExpressionProperties { get; }
}
internal class TemplateOptions
{
public bool? IsRequired { get; set; }
public string Label { get; set; }
public string Placeholder { get; set; }
}
Apparently, the version of Angular-Formly (6.0.0-beta.1) being used in the example is throwing exception. I remember this was working before. Any way I reverted it to a stable version and its working again.
Here is the jsbin with your formly json that is working as it should:
http://jsbin.com/towozegiqu/edit
Related
I have a list of items and in each item I have a Ajax.ActionLink what I want to do is to set id of each action link dynamically (Item id).
#Ajax.ActionLink("Join","ajaxview",new{ id = tour.TourId},newAjaxOption
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "currentaction"},new{
#class= "tm-tours-box-1-link-right",
#id="currentaction"})
my model is
HolidayPlanners.Models.Tour
what I want to do is something like this
#class= "tm-tours-box-1-link-right",
#id=#Tour.id
but it is giving me errors because I am using razon syntax(server side) inside jquery(client side) is there any way around to this?
Dynamic action link ajax, as I posted prior:
#car.CarMake,
"getUpdate",
new { carId = car.CarId },
new AjaxOptions
{
UpdateTargetId = "result" + car.CarId, //use car.ID here? not sure
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
}, new { #class = "getClick" })
Model:
#model IEnumerable<Testy20161006.Controllers.CarModel>
More about my model:
public class CarModel
{
public int CarId { get; set; }
public string CarMake { get; set; }
public string theCarModel { get; set; }
}
public class HomeController : Controller
{
public PartialViewResult getUpdate(int carId)
{
CarModel carModel = new CarModel();
switch (carId)
{
case 1:
carModel.CarId = 1;
carModel.CarMake = "updated11111Make";
carModel.theCarModel = "updated11111Model";
break;
case 2:
carModel.CarId = 2;
carModel.CarMake = "updated2Make";
carModel.theCarModel = "updated22222Model";
break;
case 3:
carModel.CarId = 3;
carModel.CarMake = "updated3Make";
carModel.theCarModel = "updated33333Model";
break;
default:
break;
}
return PartialView("_PartialView", carModel);
}
public ActionResult Index700()
{
IList<CarModel> carModelList = Setup();
return View(carModelList);
}
private static IList<CarModel> Setup()
{
IList<CarModel> carModelList = new List<CarModel>();
carModelList.Add(new CarModel { CarId = 1, CarMake = "VW", theCarModel = "model1" });
carModelList.Add(new CarModel { CarId = 2, CarMake = "BMW", theCarModel = "model2" });
carModelList.Add(new CarModel { CarId = 3, CarMake = "Ford", theCarModel = "model3" });
return carModelList;
}
I have a form that has two sections. 3 input fields and another section with 10 checkboxes.
public class Customerproductdto
{
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
public string CustomerPhone { get; set; }
List<ProductDetails> GetAllChecked {get;set;}
}
public class ProductDetails
{
public string ProductName{ get; set; }
}
Here is jquery code I am using to get all the values of the checkboxes that were
checked on my form. They are about 10 and users could check everything.
var yourArray[]
$("input:checkbox[name=type]:checked").each(function(){
yourArray.push($(this).val());
});
Here is javascript that I use to collect the data and pass to my controller.
How can I pass in my array here all in one shot?
var objdata =
({
CustomerNumber: txtcustnumber,
CustomerName: txtcustname,
CustomerPhone: txtphone
//How do I pass the yourArray here?
});
var url = "#Url.Action("WriteToDb", "Home")";
var completeData = JSON.stringify({ 'Information': objdata });
$.get(url, { 'objdata': completeData }, function (data) {
$('#mainListContent').html(data);
});
Please note that I will like to deserialize this once I get to the controller.
Here is the method.
public ActionResult WriteToDb(string objdata)
{
Customerproductdto getAllTaskSaved = null;
try
{
var stripOffObjectName = JObject.Parse(objdata)["Information"];
var cleanedData = JsonConvert.DeserializeObject<Customerproductdto>(stripOffObjectName.ToString());
getAllTaskSaved = _dtcDataService.WriteTaskToDb(cleanedData, "Add");
}
catch (Exception ex)
{
logger.Error(ex);
}
return PartialView("_Taskdisplay", getAllTaskSaved);
}
I am working on a project which includes MVC and KnockoutJS. As this is the first time i'm working with KnockoutJS i'm running into a lot of issues.
I want to put my MVC Viewmodel on the webpage in a tabbed interface. Then from that interface i want to call functions depending on which button i press.
Currently i am getting the model to load and show itself fine, but i cannot call any functions i am trying to create.
Also i have no clue that the way i am creating this at the moment is the correct one.
I have the following model:
public partial class SSoSearchModel
{
public string SearchParameter { get; set; }
public string SearchValue { get; set; }
public string SearchLabel { get; set; }
public IQueryable<SsoRecordResultViewModel> searchResults { get; set; }
public List<SelectListItem> ParameterList { get; set; }
public List<SelectListItem> LabelList { get; set; }
}
The SearchResults look like this
public partial class SsoRecordResultViewModel
{
[JsonProperty(PropertyName = "first_name")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "last_name")]
public string LastName { get; set; }
[JsonProperty(PropertyName = "email_address"), DefaultValue(PropertyName = "email_addresses")]
public string EmailAddress { get; set; }
[JsonProperty(PropertyName = "phone_number"), DefaultValue(PropertyName = "phone_numbers")]
public string PhoneNumber { get; set; }
[JsonProperty(PropertyName = "Klantnummer"), CustomAttribute(PropertyName = "Klantnummer")]
public string Klantnummer { get; set; }
[JsonProperty(PropertyName = "verzekerdenummer"), CustomAttribute(PropertyName = "Verzekerdennummer")]
public string Verzekerdenummer { get; set; }
[JsonProperty(PropertyName = "person_id")]
public string SSOID { get; set; }
[JsonProperty(PropertyName = "initials")]
public string Initialen { get; set; }
}
And this is all loaded in the Index function of the controller with dummy data
public ActionResult Index()
{
List<SsoRecordResultViewModel> temp = new List<SsoRecordResultViewModel>();
temp.Add(new SsoRecordResultViewModel { SSOID = "ea373d27-142d-48f6-86c9-dcdb15e316d2", EmailAddress = "A", FirstName = "a", Klantnummer = "1", Verzekerdenummer = "1", PhoneNumber = "1", Initialen = "A", LastName = "a" });
temp.Add(new SsoRecordResultViewModel { SSOID = "2d613aba-3f89-43b0-919a-1aa615086ff3", EmailAddress = "b", FirstName = "b", Klantnummer = "2", Verzekerdenummer = "2", PhoneNumber = "2", Initialen = "b", LastName = "b" });
temp.Add(new SsoRecordResultViewModel { SSOID = "c142f22e-7664-4a9c-9303-293b48acbf65", EmailAddress = "c", FirstName = "c", Klantnummer = "3", Verzekerdenummer = "3", PhoneNumber = "3", Initialen = "c", LastName = "c" });
SSoSearchModel model = new SSoSearchModel();
model.searchResults = temp.AsQueryable(); //Enumerable.Empty<SsoRecordResultViewModel>().AsQueryable();
//zoekopties
model.ParameterList = new List<SelectListItem>();
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Klantnummer", Value = "Klantnummer" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Verzekerdenummer", Value = "Verzekerdenummer" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op E-mail adres", Value = "Email" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op SSO ID", Value = "SSOID" });
model.ParameterList.Add(new SelectListItem { Text = "Zoek op Telefoonnummer", Value = "Telefoonnummer" });
//Labels
model.LabelList = new List<SelectListItem>();
model.LabelList.Add(new SelectListItem { Text = "Rel1", Value = "Rel1" });
model.LabelList.Add(new SelectListItem { Text = "Rel2", Value = "Rel2" });
return View(model);
}
Now on the Index.cshtml i offcourse have my markup which shows the data great at the moment, but the KnockoutJS is the issue.
The javascript looks the following:
<script>
function ViewModel() {
var self = this;
self.Search = ko.observableArray(searchResults);
self.save = function(ssoid){
//ajax hier
};
};
var rawData = #Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model));
var Data = ko.mapping.fromJS(rawData,ViewModel);
ko.applyBindings(Data);
</script>
This currently shows me the data, but i do not know how i can call the save function. I have this but this does not work:
<button type="button" class="btn btn-default" aria-label="Bewaren" data-toggle="tooltip" data-placement="top" title="Bewaar de gegevens" data-bind="click: function() { $data.save(SSOID()); }"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span></button>
Also when i swap the values in ko.mapping.fromJS(rawData,ViewModel); the data does not get shown anymore.
Anyone here who can help me with this as it is driving me crazy.
I have a page called bookprogram and below is its model!
public class BookViewModel
{
[Required(ErrorMessage = "Field cannot be blank!", AllowEmptyStrings = false)]
[Display(Name="Name *")]
public string name { get; set; }
[Required(ErrorMessage = "Field cannot be blank!", AllowEmptyStrings = false)]
[DataType(DataType.PhoneNumber)]
public string contact { get; set; }
[Required(ErrorMessage = "Field cannot be blank!", AllowEmptyStrings = false)]
[RegularExpression("[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+.[A-Za-z]{2,4}", ErrorMessage = "Invalid Email Id")]
public string email { get; set; }
[Required(ErrorMessage = "Please select a category")]
public string category { get; set; }
[Required(ErrorMessage = "Field cannot be blank!", AllowEmptyStrings = false)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime dateofprogram { get; set; }
[Required(ErrorMessage = "Field cannot be blank!", AllowEmptyStrings = false)]
[StringLength(200,ErrorMessage="Max length exceeded! Should be less than 200 characters")]
public string Message { get; set; }
}
Here is my js for performing AJAX Post
function ValidateBookProgramAndPost(form)
{
$(form).on("submit", function (e) {
e.preventDefault();
ValidateForm(form);
var selectedVal = $(form).find('select').children(":selected").val();
if(selectedVal=="")
{
$(form).find('div.bootstrap-select').children(":first").addClass('alert-danger');
$(form).find('div.bootstrap-select').next('.text-danger').html('Please select a category!');
}
var dateofprog = moment().format($('#txtDate').val());
console.log(dateofprog);
$.ajax({
url: '/BookProgram/',
type: "POST",
dataType: 'json',
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ model:{
name: $('#txtName').val(),
contact: $('#txtPhone').val(),
email: $('#txtEmail').val(),
category: $("#hdnSelectedCategory").val(),
dateofprogram: dateofprog,
Message: $("#txtMessage").val()
}
}),
success: function (data) {
if (data.result) {
$(form).find('input[type=text], textarea').val('').removeClass("alert-success");
$('.selectpicker').selectpicker('refresh');
}
else {
if (data.result == "Email Validation failed on server side!") {
$("#txtEmail").addClass("alert-danger");
}
else {
//display error message
}
return false;
}
return true;
}
}
$(form).unbind('submit');
return false;
});
and here is my controller:
[HttpPost]
public ActionResult BookProgram(BookViewModel model)
{
bool valid = false;
bool val = false;
if (ModelState.IsValid)
{
if (model.name != "" && model.category != "" && model.contact != "" && model.dateofprogram.ToShortDateString() != "" && model.email != "" && model.Message != "")
{
if (v.validateEmail(model.email) && v.validatePhone(model.contact))
{
valid = true;
}
else
{
return Json(new { result = "Email/Phone Validation failed on server side!" });
}
}
else
{
return Json(new { result = "One of the field has been modified and has been sent empty!!" });
}
if (valid)
{
using (var context = new MConnectionString())
{
tbl_programs book = new tbl_programs();
book.cont = model.contact;
book.date = model.dateofprogram;
book.email = model.email;
book.msg = model.Message;
book.category = model.category;
book.status = "";
book.name = model.name;
context.tbl_programs.Add(book);
context.SaveChanges();
val = true;
}
if (val)
{
return Json(new { result = true });
}
else
{
return Json(new { result = "Could not book the program. Please try again!" });
}
}
return Json(new { result = "Could not book the program. Please try again!" });
}
return Json(new { success = false });
}
But when I check in the controller the date value is coming null and Model.IsValid fails. how I should pass date value from ajax then? Console.log shows selected date[dateofprog] as "14/02/2015"[example] but it will not be assigned to model. Where is the problem actually I cannot make it out. Can anyone help me on this??
You are posting a invalid format for dateofprogram. It cases a failure at the binding process. You must specify the culture in at system.web section of the web.config to get the right date parsed from the json, for exemple:
<globalization uiCulture="pt-BR" culture="pt-BR" />
The code above informs the culture of the application, then if I inform a DateTime in BR format like dd/MM/yyyy it will bind correctly.
I'm trying to make my call to the server with BreezeJS but can't get it to work. It says tblMovie is not recognized. I can't find the problem :S
When I want to add a new movie it says so.
show.js
self.viewAddMovieModal = function () {
self.app.showModal(new self.addmovie()).then(function (result) {
if (result != undefined) {
var movie = dataservice.createMovie({
Title: result[0].title,
Director: result[0].director
});
if (movie.entityAspect.validateEntity()) {
self.movies.push(new movie(result[0].title, result[0].director));
dataservice.saveChanges();
} else {
alert("Error");
}
}
});
};
My dataservice.js layer
/// <reference path="../../Scripts/breeze.debug.js"/>
define(["require"], function (require) {
var Dataservice = (function () {
function Dataservice(service) {
this.serviceName = '';
this._isSaving = false;
this.serviceName = service;
this.Manager = new breeze.EntityManager(this.serviceName);
this.EntityQuery = new breeze.EntityQuery();
}
Dataservice.prototype.getAllMovies = function () {
this.EntityQuery = breeze.EntityQuery.from("AllMovies");
return this.Manager.executeQuery(this.EntityQuery);
};
Dataservice.prototype.createMovie = function (initialValues) {
return this.Manager.createEntity('tblMovies', initialValues); //THis is where it goes wrong :(
};
Dataservice.prototype.saveChanges = function (suppressLogIfNothingToSave) {
if (this.Manager.hasChanges()) {
if (this._isSaving) {
setTimeout(this.saveChanges, 50);
return;
}
this.Manager.saveChanges().then(this.saveSucceeded).fail(this.saveFailed).fin(this.saveFinished);
} else if (!suppressLogIfNothingToSave) {
}
};
Dataservice.prototype.saveSucceeded = function (saveResult) {
this._isSaving = false;
};
Dataservice.prototype.saveFailed = function (error) {
};
Dataservice.prototype.saveFinished = function () {
this._isSaving = false;
};
return Dataservice;
})();
return Dataservice;
})
I do have a model tblMovie
using System;
using System.ComponentModel.DataAnnotations;
namespace DurandalMovieApp.Models
{
public class tblMovie
{
[Key]
public int MovieID { get; set; }
public string Title { get; set; }
public string Director { get; set; }
}
}
Hope someone can help!
I think that the problem is that your entity is: tblMovie, not tblMovies.
Try replacing:
return this.Manager.createEntity('tblMovies', initialValues);
With:
return this.Manager.createEntity('tblMovie', initialValues);