Ошибка означает, что значение CategoryList
равно нулю (и в результате метод DropDownListFor()
ожидает, что первый параметр имеет тип IEnumerable
).
Вы не генерируете вход для каждого свойства каждого SelectListItem
в CategoryList
(и не должен), поэтому никакие значения для SelectList
не отправляются в метод контроллера, и поэтому значение model.CategoryList
в методе POST равно null
, Если вы вернете представление, вы должны сначала переназначить значение CategoryList
, как и в методе GET.
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
return View(model);
}
// Save and redirect
}
Чтобы объяснить внутреннюю работу (исходный код может быть см. здесь )
Каждая перегрузка DropDownList()
и DropDownListFor()
в конечном итоге вызывает следующий метод
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable selectList, bool allowMultiple,
IDictionary htmlAttributes)
, который проверяет, есть ли selectList
(второй параметр из @Html.DropDownListFor()
) является null
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
, который, в свою очередь, вызывает
private static IEnumerable GetSelectData(this HtmlHelper htmlHelper, string name)
, который оценивает первый параметр @Html.DropDownListFor()
(в данном случае CategoryID
)
....
o = htmlHelper.ViewData.Eval(name);
....
IEnumerable selectList = o as IEnumerable;
if (selectList == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name, o.GetType().FullName, "IEnumerable"));
}
Поскольку свойство CategoryID
является typeof int
, оно не может быть передано в IEnumerable
, и генерируется исключение (которое определено в файле MvcResources.resx
как)
The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.
ECMAScript 2018 вводит группы захвата в регулярные выражения JavaScript.
Если вам нужно поддерживать старые браузеры, вы можете делать все с помощью обычных (нумерованных) групп захвата, которые вы можете сделать с именованными группами захвата, вам просто нужно отслеживать числа, которые могут быть громоздкими, если порядок захвата группы в вашем регулярном выражении изменяется.
Есть только два «структурных» преимущества названных групп захвата, о которых я могу думать:
(\d)
на $10
. В JavaScript это будет работать (пока у вас будет менее 10 групп захвата в вашем регулярном выражении), но Perl подумает, что вы ищете номер обратной ссылки 10
вместо номера 1
, а затем 0
. В Perl вы можете использовать ${1}0
в этом случае. Кроме этого, группы захвата называются «синтаксическим сахаром». Это помогает использовать группы захвата только тогда, когда они вам действительно нужны, и использовать не захватывающие группы (?:...)
во всех других обстоятельствах.
Большая проблема (на мой взгляд) с JavaScript заключается в том, что она не поддерживает многословные регулярные выражения, которые значительно упростили бы создание понятных и сложных регулярных выражений.
Библиотека XRegExp Стив Левитана решает эти проблемы.
Вы можете использовать XRegExp , расширенную, расширяемую кросс-браузерную реализацию регулярных выражений, включая поддержку дополнительного синтаксиса, флагов и методов:
s
, чтобы совместить точки со всеми символами (так называемый dotall или singleline mode ) и x
, для свободного пробела и комментариев (aka extended mode). В ES6 вы можете использовать деструктурирование массива для захвата ваших групп:
let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = text.match(regex) || [];
// count === '27'
// unit === 'months'
Примечание:
let
пропускает первое значение результирующего массива, который представляет собой всю согласованную строку || []
после .match()
предотвратит ошибку разрушения, если совпадений нет (поскольку .match()
вернет null
) String.prototype.match
возвращает массив с: всей согласованной строкой в позиции 0, затем после каждой группы. Первая запятая говорит «пропустить элемент в положении 0»,
– bfred.it
31 July 2016 в 06:34
RegExp.prototype.exec
над String.prototype.match
в местах, где строка может быть null
или undefined
.
– Mike Hill
31 July 2017 в 15:29
Существует библиотека node.js, названная named-regexp , которую вы можете использовать в проектах node.js (в браузере, упаковывая библиотеку с помощью браузера или других сценариев упаковки). Тем не менее, библиотека не может использоваться с регулярными выражениями, которые содержат неименованные группы захвата.
Если вы считаете открывающие скобки для захвата в своем регулярном выражении, вы можете создать сопоставление между именованными группами захвата и пронумерованными группами захвата в вашем регулярном выражении и может свободно смешиваться и сочетаться. Вам просто нужно удалить имена групп, прежде чем использовать регулярное выражение. Я написал три функции, которые демонстрируют это. См. Этот пункт: https://gist.github.com/gbirke/2cc2370135b665eee3ef
Именованные группы захвата могут быстро перейти на JavaScript. Предложение для него уже на этапе 3.
Группе захвата может быть присвоено имя с использованием синтаксиса (? ...) для любого имени идентификатора. Регулярное выражение для даты тогда может быть записано как / (? \ D {4}) - (? \ D {2}) - (? \ D {2}) / u. Каждое имя должно быть уникальным и следовать грамматике для имени идентификатора ECMAScript.
Именованные группы могут быть доступны из свойств свойства групп результата регулярного выражения. Также создаются нумерованные ссылки на группы, как и для неименованных групп. Например:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
Хотя вы не можете сделать это с помощью ванильного JavaScript, возможно, вы можете использовать некоторую функцию Array.prototype
, например Array.prototype.reduce
, чтобы превратить индексированные совпадения в именованные, используя некоторую магию .
Очевидно, что следующее решение потребует, чтобы совпадения выполнялись по порядку:
// @text Contains the text to match
// @regex A regular expression object (f.e. /.+/)
// @matchNames An array of literal strings where each item
// is the name of each group
function namedRegexMatch(text, regex, matchNames) {
var matches = regex.exec(text);
return matches.reduce(function(result, match, index) {
if (index > 0)
// This substraction is required because we count
// match indexes from 1, because 0 is the entire matched string
result[matchNames[index - 1]] = match;
return result;
}, {});
}
var myString = "Hello Alex, I am John";
var namedMatches = namedRegexMatch(
myString,
/Hello ([a-z]+), I am ([a-z]+)/i,
["firstPersonName", "secondPersonName"]
);
alert(JSON.stringify(namedMatches));
var assocArray = Regex("hello alex, I am dennis", "hello ({hisName}.+), I am ({yourName}.+)");
– Forivin
29 August 2015 в 16:46
RegExp
, добавив функцию в свой прототип.
– Mr. TA
16 February 2016 в 20:27
Другое возможное решение: создать объект, содержащий имена групп и индексы.
var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };
Затем используйте клавиши объектов для ссылки на группы:
var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];
Это улучшает читаемость / качество кода с использованием результатов регулярного выражения, но не читаемость самого регулярного выражения.
Именование захваченных групп дает одну вещь: меньше путаницы со сложными регулярными выражениями.
Это действительно зависит от вашего прецедента, но, может быть, довольно-печатать ваше регулярное выражение может помочь.
Или вы можете попытаться определить константы для ссылки на ваши захваченные группы.
Комментарии могут также помочь показать другим, кто читает ваш код, что вы сделали.
В остальном я должен согласиться с ответом Тимов.