Редактировать:
function ready(callback){
// in case the document is already rendered
if (document.readyState!='loading') callback();
// modern browsers
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') callback();
});
}
ready(function(){
// do something
});
Взято из https://plainjs.com/javascript/events/running-code-when -the-document-is-ready-15 /
Еще одна хорошая функция domReady здесь взята из https://stackoverflow.com/a/9899701/175071
Поскольку принятый ответ был очень далек от завершения, я соединил «готовую» функцию, подобную jQuery.ready()
, основанную на источнике jQuery 1.6.2:
var ready = (function(){
var readyList,
DOMContentLoaded,
class2type = {};
class2type["[object Boolean]"] = "boolean";
class2type["[object Number]"] = "number";
class2type["[object String]"] = "string";
class2type["[object Function]"] = "function";
class2type["[object Array]"] = "array";
class2type["[object Date]"] = "date";
class2type["[object RegExp]"] = "regexp";
class2type["[object Object]"] = "object";
var ReadyObj = {
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
ReadyObj.readyWait++;
} else {
ReadyObj.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( ReadyObj.ready, 1 );
}
// Remember that the DOM is ready
ReadyObj.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --ReadyObj.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ ReadyObj ] );
// Trigger any bound ready events
//if ( ReadyObj.fn.trigger ) {
// ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
//}
}
},
bindReady: function() {
if ( readyList ) {
return;
}
readyList = ReadyObj._Deferred();
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( ReadyObj.ready, 1 );
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", ReadyObj.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", ReadyObj.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
_Deferred: function() {
var // callbacks list
callbacks = [],
// stored [ context , args ]
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
cancelled,
// the deferred itself
deferred = {
// done( f1, f2, ...)
done: function() {
if ( !cancelled ) {
var args = arguments,
i,
length,
elem,
type,
_fired;
if ( fired ) {
_fired = fired;
fired = 0;
}
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = ReadyObj.type( elem );
if ( type === "array" ) {
deferred.done.apply( deferred, elem );
} else if ( type === "function" ) {
callbacks.push( elem );
}
}
if ( _fired ) {
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
}
}
return this;
},
// resolve with given context and args
resolveWith: function( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || [];
firing = 1;
try {
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
}
}
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
resolve: function() {
deferred.resolveWith( this, arguments );
return this;
},
// Has this deferred been resolved?
isResolved: function() {
return !!( firing || fired );
},
// Cancel
cancel: function() {
cancelled = 1;
callbacks = [];
return this;
}
};
return deferred;
},
type: function( obj ) {
return obj == null ?
String( obj ) :
class2type[ Object.prototype.toString.call(obj) ] || "object";
}
}
// The DOM ready check for Internet Explorer
function doScrollCheck() {
if ( ReadyObj.isReady ) {
return;
}
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
// and execute any waiting functions
ReadyObj.ready();
}
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
ReadyObj.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
ReadyObj.ready();
}
};
}
function ready( fn ) {
// Attach the listeners
ReadyObj.bindReady();
var type = ReadyObj.type( fn );
// Add the callback
readyList.done( fn );//readyList is result of _Deferred()
}
return ready;
})();
Как использовать:
Я не уверен, насколько функционален этот код, но он хорошо работал с моими поверхностными тестами. Это заняло довольно много времени, поэтому я надеюсь, что вы и другие можете извлечь из этого пользу.
PS .: Я предлагаю скомпилировать его.
Или вы можете использовать http://dustindiaz.com/smallest-domready-ever :
function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});
или встроенную функцию, если вам требуется только поддержка новых браузеров ( В отличие от готового jQuery, он не запустится, если вы добавите его после загрузки страницы)
document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})
I wrote a class library that basically does exactly what my psuedocode in my question describes. It works great.
I finally got around to cleaning up my class library. I have added some new features and created a fairly well documented demo web application.
All of this is hosted here on CodePlex. I hope this helps someone.
Я ни в коем случае не эксперт, но если вы являются новичком в ASP.NET MVC, поэтому я рекомендую вам начать со встроенных функций, прежде чем создавать собственные. Он выполняет большую часть того, что вы описали, за исключением того, что не поощряет определение / построение пользовательского интерфейса в контроллере, поскольку это задача представления.
С помощью ModelStateDictionary вы можете добавлять ошибки модели и устанавливать значения модели, которые затем будут привязываться к входным данным формы при неудачной проверке.
Обновление: Другой способ взглянуть на это: спросите себя, почему вы используете MVC вместо классических методов построения ASP.NET, а затем посмотрите, совпадает ли предложенный вами подход с этими причины. Для меня разделение интересов было огромной причиной, Шаг 4, хорошо. Шаги с 5 по 7 в значительной степени являются стандартной практикой MVC и полностью поддерживаются платформой. Например, Выполнение простой проверки (C #) показывает пример проверки и представления сообщений об ошибках.
Насколько динамичны определения ваших полей? Если они не меняются очень часто, вы можете использовать code dom для создания модели и контроллера после создания определения. Я не пробовал это в ASP.NET MVC, но это может быть хорошим способом.
http://msdn.microsoft.com/en-us/library/y2k85ax6.aspx
В этой статье используется код dom для ActionLink. поколения.
Хотя я и не был экспертом, мне пришлось создать решение, в котором у моего основного объекта был список значений. Назовем это объектом A, у которого есть список ApplicationValues, отображаемый в базе данных. ApplicationValues имеет Key (поле формы, например PhoneNumber) и Value.
Поскольку ApplicationValues были EntitySet, мне пришлось создать методы get и set для правильной обработки установки определенного ApplicationValue. У меня также был список ApplicationRules в моей базе данных, который определял, какие значения могут принимать эти значения приложения.
Вот фрагмент кода, который может помочь вам разработать решение для ваших нужд.
public partial ApplicationValue
{
public string Key;
public string Value;
}
public partial ApplicationRule
{
public string ValidationFormat;
public string ValidationError;
public bool Required;
}
public partial class A
{
public void SetValue(string key, string value)
{
//ApplicationValues is the list of values associated to object A
ApplicationValue v = ApplicationValues.SingleOrDefault
(k => k.Key == key);
//if we already have this value
if (v != null)
{ //...then we can simply set and return
v.Value = value;
return;
}
//else we need to create a new ApplicationValue
v = new ApplicationValue
{
AffinityID = this.ID,
Key = key,
Value = value
};
ApplicationValues.Add(v);
}
public string GetValue(ApplicationField key)
{
return GetValue(key, String.Empty);
}
public string GetValue(ApplicationField key, string defaultValue)
{
if (ApplicationValues == null)
return defaultValue;
ApplicationValue value = ApplicationValues.SingleOrDefault
(f => f.Key == key.ToString());
return (value != null) ? value.Value : defaultValue;
}
А затем выполнить проверку формы я пропустите ApplicationRules (который определяет, требуется ли поле, содержит ли регулярное выражение и т.д.) и сопоставьте его с FormCollection.
public ActionResult Details(FormCollection form)
{
IList<ApplicationRule> applicationRules = //get my rules from the DB
if (!(ValidateApplication(applicationRules, form, a)))
{
ModelState.AddModelError("message", "Please review the errors below.");
return View(a);
}
...
}
private bool ValidateApplication(IList<ApplicationRule> applicationRules,
FormCollection form, A a)
{
//loop through the application rules
foreach (ApplicationRule ar in applicationRules)
{
//try and retrieve the specific form field value using the key
string value = form[ar.Key];
if (value == null)
continue;
//set the model value just in case there is an error
//so we can show error messages on our form
ModelState.SetModelValue(ar.Key, ValueProvider[ar.Key]);
//if this rule is required
if (ar.Required)
{ //...then check if the field has a value
if (String.IsNullOrEmpty(value))
{
ModelState.AddModelError(ar.Key, "Field is required");
continue;
}
}
//if this rule has a validation format
if (!String.IsNullOrEmpty(ar.ValidationFormat))
{ //...then check the value is of the correct format
Regex re = new Regex(ar.ValidationFormat);
if (!re.IsMatch(value))
{
ModelState.AddModelError(ar.Key, ar.ValidationError);
continue;
}
}
a.SetValue(ar.Key, value);
}
return ModelState.IsValid;
}