2014-04-14 4 views
5

Sto imparando come utilizzare la risorsa $ di AngularJS per chiamare un backend Web Api. Voglio passare una gerarchia di oggetti come criterio e recuperare un IEnumerable<Program>. Ecco un esempio dei criteri:

$scope.criteria = { 
    Categories:[ 
     { 
      Name: "Cat1", 
      Options: [ 
       {Text: "Opt1", Value: true}, 
       {Text: "Opt2", Value: false} 
      ] 
     }, 
     { 
      Name: "Cat2", 
      Options: [ 
       {Text: "Opt3", Value: true}, 
       {Text: "Opt4", Value: false} 
      ] 
     } 
    ] 
} 

Ho gli stessi oggetti definiti sul server in C#.

public class CriteriaModel 
{ 
    public IEnumerable<CriteriaCategory> Categories { get; set; } 
} 

public class CriteriaCategory 
{ 
    public string Name { get; set; } 
    public IEnumerable<CriteriaOption> Options { get; set; } 
} 

public class CriteriaOption 
{ 
    public string Text { get; set; } 
    public bool Value { get; set; } 
} 

Ecco come Sto configurando $ risorsa:

angular.module('my.services') 
    .factory('api', [ 
     '$resource', 
     function ($resource) { 
      return { 
       Profile: $resource('/api/profile/:id', { id: '@id' }), 
       Settings: $resource('/api/settings/:id', { id: '@id' }), 
       Program: $resource('/api/program/:id', { id: '@id' }) 
      }; 
     } 
    ]); 

E mi chiamano così:

api.Program.query({ criteria: $scope.criteria }, function (response) { 
    $scope.programs = response; 
}); 

Non importa quello che provo io o ottenere null come parametro di criteri o l'azione non viene eseguita affatto. Non so se il problema è in angolare, web api o entrambi. Ecco l'azione:

public class ProgramController : ApiController 
{ 
    public IEnumerable<Program> GetByCriteria([FromUri]CriteriaModel criteria) 
    { 
     // Either criteria is null or this action doesn't even get 
     // executed depending on what I try. 
    } 
} 

Qualcuno può aiutarmi a ottenere un esempio di lavoro in corso per la ricerca e la restituzione dei prodotti che utilizzano AngularJS $ risorse e Web Api?

risposta

9

Avrai bisogno di un raccoglitore di modelli personalizzato. Da what I understandFromUri non gestirà tipi nidificati complessi o json che $resource inserirà la stringa di query.

Github Sample

Modello legante:

public class CriteriaModelBinder : IModelBinder 
{ 
    public bool BindModel(
     HttpActionContext actionContext, 
     ModelBindingContext bindingContext 
    ) 
    { 
     if (bindingContext.ModelType != typeof (CriteriaModel)) 
     { 
      return false; 
     } 

     var value = bindingContext.ValueProvider.GetValue("Categories"); 

     if (value == null) 
     { 
      return false; 
     } 

     var categoryJson = value.RawValue as IEnumerable<string>; 

     if (categoryJson == null) 
     { 
      bindingContext.ModelState.AddModelError(
       bindingContext.ModelName, "Categories cannot be null."); 
      return false; 
     } 

     var categories = categoryJson 
      .Select(JsonConvert.DeserializeObject<CriteriaCategory>) 
      .ToList(); 

     bindingContext.Model = new CriteriaModel {Categories = categories}; 
     return true; 
    } 
} 

Controller:

[RoutePrefix("api/program")] 
public class ProgramController : ApiController 
{ 
    [Route("getbycriteria")] 
    [HttpGet] 
    public HttpResponseMessage GetByCriteria(
     [ModelBinder(typeof(CriteriaModelBinder))]CriteriaModel criteria 
    ) 
    { 
     return new HttpResponseMessage(HttpStatusCode.OK); 
    } 
} 

controller angolare:

angular.module('myApp'). 
    controller('HomeController', function($scope, $resource) { 
     var Program = $resource('/api/program/:id', {}, { 
      getByCriteria: { 
       url: '/api/program/getbycriteria', 
       method: 'GET', 
       isArray: true 
      } 
     }); 

     var program = new Program(); 
     var criteria = { 
      Categories: [ 
       { 
        Name: "Cat1", 
        Options: [ 
         { Text: "Opt1", Value: true }, 
         { Text: "Opt2", Value: false } 
        ] 
       }, 
       { 
        Name: "Cat2", 
        Options: [ 
         { Text: "Opt3", Value: true }, 
         { Text: "Opt4", Value: false } 
        ] 
       } 
      ] 
     }; 

     $scope.submit = function() { 
      console.log(program); 
      program.$getByCriteria(criteria); 
     }; 
    }); 

Edit:

qui è per POST:

Controller:

[RoutePrefix("api/program")] 
public class ProgramController : ApiController 
{ 
    [Route("getbycriteria")] 
    [HttpPost] 
    public HttpResponseMessage GetByCriteria(CriteriaModel criteria) 
    { 
     return new HttpResponseMessage(HttpStatusCode.OK); 
    } 
} 

angolare:

angular.module('myApp'). 
    controller('HomeController', function($scope, $resource) { 
     var Program = $resource('/api/program/:id', {}, { 
      getByCriteria: { 
       url: '/api/program/getbycriteria', 
       method: 'POST', 
       isArray: true 
      } 
     }); 

     var program = new Program(); 
     program.Categories = [ 
       { 
        Name: "Cat1", 
        Options: [ 
         { Text: "Opt1", Value: true }, 
         { Text: "Opt2", Value: false } 
        ] 
       }, 
       { 
        Name: "Cat2", 
        Options: [ 
         { Text: "Opt3", Value: true }, 
         { Text: "Opt4", Value: false } 
        ] 
       } 
      ]; 

     $scope.submit = function() { 
      console.log(program); 
      program.$getByCriteria(); 
     }; 
    }); 
+0

Quindi, se ho creato una nuova azione a $ risorsa che fa un post e preso [FromUri] off il parametro, si prenderà cura di esso senza che io debba creare un modello personalizzato? – adam0101

+0

Il raccoglitore modello predefinito cerca JSON nel corpo della richiesta. '$ resource' è RESTful e invia JSON nella stringa di query. La mancata corrispondenza è il motivo per cui è necessario utilizzare un modello di rilegatura o utilizzare '$ http'. – Romoku

+0

Aspetta, sono confuso. È possibile specificare una nuova azione su $ resource con un metodo di 'POST' in base alla documentazione. Stai dicendo che lo ignora e lo inserisce comunque nel querystring? – adam0101

Problemi correlati