2011-03-23 1 views
1

다음 모델 주석, viewmodel 및 사용자 정의 편집기 템플릿을 사용하여 드롭 다운을 채울 수 있으며 표시 할 수 있습니다. 이상) 아래 그에 : 자동으로 아래 템플릿을 사용MVC EditFor 템플릿 및 주석 (.net 4, mvc3)을 사용하여 SelectList ID를 저장하는 중 문제가 발생했습니다.

//Domain 
Public class Person 
{ 
    public string FirstName {get;set;} 
    public string LastName {get;set;} 
    public string ClassName {get;set;} 
    public int ClassId {get;set;} 
    ... 
} 

//ViewModel 
Public class PersonViewModel 
{ 
    public Person Person {get;set;} 
    [UIHint("SelectList")] 
    public IEnumerable<SelectListItems> Classes {get;set;} 

    public PersonViewModel 
    { 
    Person = new Person(); 
    } 
} 

//Controller 
public ActionResult Create() 
{ 
    var PersonViewModel = new PersonViewModel(); 
    _PersonService.PopulateClassList(PersonViewModel); 
    return View("Create", PersonViewModel); 
} 

[HttpPost] 
public ActionResult Create(PersonViewModel PersonViewModel) 
{ 
try 
    { 
    if (!ModelState.IsValid) 
    { 
    _PersonService.PopulateClassList(PersonViewModel); 
    return View("Create", PersonViewModel); 
    } 

    var Person = new Person(); 
    Person.InjectFrom(PersonViewModel.Person);  
    _PersonService.Save(Person); 

    return RedirectToAction("Index"); 
} 
catch 
{ 
    return View(); 
} 
} 

//View (just the pertinent part) 
@Html.EditorFor(m => m.Classes)<br /> 
@Html.EditorFor(m => m.Person) 

-으로 인해 위의 잘 작동

내가 언급처럼

// Views/Shared/EditorTemplates/SelectList.cshtml 
@model IEnumerable<SelectListItem> 
@Html.DropDownListFor(m => m, Model) 
, 뷰 모델 주석에 - 아래로 제대로 표시되는 드롭으로, 하지만 분명히 현재 상태에서 클래스 엔티티 (또는 이름)를 Person 엔티티에 저장하지 않으면 문제를 이해할 수 있지만 어떻게 수정해야할지 모르겠습니다.

내 질문 : 나는이 사람 엔티티로 ID와 이름을 모두 절약 할 수 있습니다 어떻게 사람 엔티티

  • 에 드롭 다운에서 선택한 값을 저장할 수있는 방법

    1. - 유일한 방법은 추측하고있어 onchange 이벤트로 채워진 필드입니다 (이상하게 들리 겠지만 문서 DB에 저장되어 있으므로 정규화되지 않았습니다 - 이름을 끝에 저장할 수 있습니다.))

    미리 감사드립니다. 마이크

  • 답변

    3

    난 당신의 ViewModel에, 당신은 클래스가

    IEnumerable<SelectListItem> 
    

    하지 <SelectListItems>하고 선택한 항목에 대한 필드를 추가해야합니다 있도록 코드를 변경해야한다고 생각합니다.

    Public class PersonViewModel 
    { 
        public Person Person {get;set;} 
        public IEnumerable<SelectListItem> Classes {get;set;} 
        public string SelectedClassId {get; set;} 
    } 
    

    그러면보기에서 다음과 같은 코드를 사용하여 드롭 다운을 채울 수 있습니다.

    @Html.DropDownListFor(x => x.SelectedClassId, Model.Classes, new { id = "classSelect" }) 
    

    [편집]

    나는 당신이 실제로 무슨 일을하는지에 좀 더 가까이 보니 새로운 사람에 대한 정보를 입력 한 다음 그것을위한 클래스를 선택하는 양식을 만들 것 같습니다 그러면 새로운 Person을 생성하고 유지합니다.

    첫째, PersonViewModel을 서비스에 전달하면 안됩니다. 보기 모델은 도메인의 일부가 아닙니다. 뷰와 컨트롤러 간의 통신을 원활하게하기 위해 사용해야합니다.

    둘째, 아직 Person이 없기 때문에 _PersonService.PopulateClassList() 메소드가 더 비슷하게 보입니다.

    public IQueryable<Class> GetClasses() 
    { 
        //Data access logic 
        //return classes .AsQueryable() 
    } 
    

    이렇게하면 필요할 때마다 수업 목록을 얻을 수 있습니다. 컨트롤러가 다음과 같이 보일 것입니다.

    당신이 컨트롤러의 밖으로 selectList의 논리를 얻고 싶다면 [질문에 대한 응답]

    [HttpPost] 
    public ActionResult Create(PersonViewModel model) 
    { 
        try 
        { 
         if (!ModelState.IsValid) 
         { 
          model.Classes = _PersonService.GetClasses().Select(c => new SelectListItem 
                       { 
                        Text = c.Name, 
                        Value = "" + c.Id 
                       }; 
          return View("Create", PersonViewModel); 
         } 
          model.Person.ClassId = model.SelectedClassId; 
          //You can ditch ClassName since you'll just look it up by ID 
          _PersonService.Save(model.Person); 
          return RedirectToAction("Index"); 
    } 
    

    , 당신이 뭔가를 찾기 위해 모델의 속성을 수정할 수 있습니다.

    Public class PersonViewModel 
    { 
        public Person Person {get;set;} 
        public string SelectedClassId {get; set;} 
        public IEnumerable<SelectListItem> Classes 
        { 
         get 
         { return _PersonService.GetClasses().Select(c => new SelectListItem 
                      { 
                       Text = c.Name, 
                       Value = "" + c.Id 
                      }; 
         } 
        } 
    } 
    

    해당 속성을 읽을 때 서비스에 액세스합니다. 확실히 기억할 수는 없지만, 결국 .AsEnumerable()을 호출해야 할 수도 있습니다.

    현재 드롭 다운 상자를 사용하여 모델 내의 복잡한 개체에 바인딩 할 수 있는지 확인하고 있습니다. 앞으로 더 있습니다.

    [계속]

    는 예를 모델 내에서 사람의 ClassId를 속성에 직접 드롭 다운 값을 바인딩 할 수 있습니다. 내 모델/뷰에서 다음 코드를 사용하여 작업을 수행 할 수있었습니다. 내 Person 및 Class 객체는 사용자의 것과 동일하지 않을 수도 있지만 아이디어를 얻을 수 있습니다.

    또한 도메인에 대해 잘 모르겠지만 클래스가 사람없이 존재할 수 있다면 데이터 액세스 논리를 PersonRepository와 ClassRepository (또는 PersonService 등)로 구분하는 것이 더 바람직 할 수 있습니다.),하지만 나는 도메인 유형 생각을 너에게 맡길 것이다.

    모델

    using System; 
        using System.Collections.Generic; 
        using System.Linq; 
        using System.Web; 
        using DropDownTest.Classes; 
        using System.Web.Mvc; 
    
        namespace DropDownTest.Models 
        { 
         public class PersonViewModel 
         { 
          private ClassRepository _classRepository = new ClassRepository(); 
    
          public Person Person { get; set; } 
          public IEnumerable<SelectListItem> Classes 
          { 
           get 
           { 
            return _classRepository.GetClasses().Select(c => new SelectListItem 
                       { 
                        Text = c.Name, 
                        Value = "" + c.ClassId 
                       }); 
           } 
          } 
         } 
        } 
    

    보기

    @model DropDownTest.Models.PersonViewModel 
    
    @{ 
        ViewBag.Title = "Create"; 
    } 
    
    <h2>Create</h2> 
    
    @using (Html.BeginForm("Create", "Person", FormMethod.Post)) 
    { 
        <fieldset> 
         <legend>New Person</legend> 
         @Html.LabelFor(c => c.Person.FirstName) 
         @Html.TextBoxFor(c => c.Person.FirstName) 
         <br /> 
    
         @Html.LabelFor(c => c.Person.LastName) 
         @Html.TextBoxFor(c => c.Person.LastName) 
         <br /> 
    
         @Html.LabelFor(c => c.Person.ClassId) 
         @Html.DropDownListFor(c => c.Person.ClassId, Model.Classes, new { id = "classSelect" }) 
         <input type="submit" value="submit" /> 
        </fieldset> 
    } 
    

    클래스 저장소 (이 경우 서비스로 생각) 이 분명히 가짜하지만, 사용 된 경우 추상 저장소, 당신은 단위 테스트에서 이와 같은 것을 사용할 수 있고 프로덕션 용도로 실제 저장소를 사용할 수 있습니다. 오래 IClassRepository를 구현하고 Ninject와 같은 일종의 Dependency Injection (DI)을 사용하기 만하면됩니다. 그러나 이것은 완전히 다른 주제입니다. :)

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Web; 
    
    namespace DropDownTest.Classes 
    { 
        public class ClassRepository 
        { 
         private static IQueryable<Class> _classes = new List<Class> 
         { 
          new Class { ClassId = 0, Name = "Class 0" }, 
          new Class { ClassId = 1, Name = "Class 1" }, 
          new Class { ClassId = 2, Name = "Class 2" } 
         }.AsQueryable(); 
    
         public IQueryable<Class> GetClasses() 
         { 
          return _classes; 
         } 
        } 
    } 
    
    +0

    @GregB 조언을 주신 데 대해 감사드립니다. 올바른 일을하고 있는지 확실하지 않았습니다. 즉, 나는 꽤 많은 드롭 다운과 다른 로직을 가지고 있다고 말했고, 만약 컨트롤러에 그것들을 모두 배치한다면, 나는 부피가 커지고, 나는 피하고 싶다. 그래서 나는 서비스 계층에서 모든 것을 수행하는 것이 좋은 생각이라고 생각했지만, 그렇다고 말하는 것이 아닙니다. 컨트롤러에서 뷰 모델을 인스턴스화하고 전달할 수있는 '빌더'클래스를 만드는 것이 좋습니다. – Mikalee

    +0

    @ GregB 또한, "@ Html.DropDownListFor (x => x.SelectedClassId, Model.Classes, new {id ="classSelect "})"라는 드롭 다운 코드와 관련하여 Person에 값을 삽입하고 싶습니다. .viewModel에 ClassId를 만드는 것과 달리, viewModel에 포함 된 Person의 .ClassId가 가능합니까? 다시 한번 감사드립니다. – Mikalee

    +0

    @Mikalee 드롭 다운 목록은보기의 컨텍스트에서만 의미가 있으므로 서비스 레이어에서 드롭 다운 목록을 만드는 것이 좋다고 생각하지 않습니다. SelectListItems를 반환하는 서비스 레이어에서 메소드를 구현했지만 다른 클래스의 모든 클래스를 나열하려면 간단하게 새로운 메소드를 작성해야합니다. 서비스 레이어를 데이터 액세스에 집중하고 컨트롤러에서 서식을 지정하는 것이 더 바람직합니다. 컨트롤러에서 로직을 실제로 꺼내고 싶다면 모델에 넣을 수 있습니다. 방법을 보여주기 위해 내 대답을 편집 할 것입니다. – GregB

    관련 문제