Passing list from MVC ViewBag to JavaScript
Asked Answered
M

3

15

I have a list of users which I pass from my controller to my view using the view bag. Now I need to be able to pass the same list to the javascript on the page. I could reconstruct the list using a foreach loop:

    @foreach (var item in ViewBag.userList) //Gets list of users passed from controller and adds markers to the map
{
    var userLat = item.LastLatitude;
    var userLon = item.LastLongitude;
    var _userId = item.Id;

    <script>array.push({"userId":"'@_userId'","userLat":"'@userLat'","userLon":"'@userLon'"});</script>
}

However this seems like a messy approach and required a lot of reworking if a change is made. I know there are similar posts on Stack overflow, but a lot of them use previous version of MVC and the same syntax doesn't seem to apply. Any ideas?

Mildamilde answered 8/3, 2013 at 10:15 Comment(0)
W
26

You could do that in a single and safe line of code using a JSON parser. You should absolutely never manually build JSON with some string concatenations and stuff as you attempted to do in your example. No need to write any loops as well.

Here's the correct way to do that:

<script type="text/javascript">
    var array = @Html.Raw(
        Json.Encode(
            ((IEnumerable<UserModel>)ViewBag.userList).Select(user => new 
            { 
                userId = user.Id, 
                userLat = user.LastLatitude, 
                userLon = user.LastLongitude 
            })
        )
    );

    alert(array[0].userId);
</script>

The generated HTML will look exactly as you expect:

<script type="text/javascript">
    var array = [{"userId":1,"userLat":10,"userLon":15}, {"userId":2,"userLat":20,"userLon":30}, ...];
    alert(array[0].userId);
</script>

Of course the next level of improvement in this code is to get rid of the ViewCrap and use a strongly typed view model.

Wareing answered 8/3, 2013 at 10:17 Comment(3)
Thanks for the quick and detailed answer! But I get the following error 'Cannot use a lambda expression as an argument to a dynamically dispatched operation...' without the IEnumerable part. With that part added I also get an error 'The type arguments for the method...cannot be inferred from the usage'. Thank you for the help, and sorry if this is a rookie mistake. p.s. I'm using the view model for something else on this page, that's why I'm using the view bag.Mildamilde
But a view model is a class that you specifically design to meet the requirements of your view. In this case your view has a requirement to generate this dynamic javascript. So your view model should contain a collection property of type IEnumerable<UserModel>. And then simply replace the entire cast with Model.Users.Select(...). If you leave the weakly typed ViewBag you will need to cast it to the proper IEnumerable<T> type.Wareing
Ok I quickly modified the page and used the View model and it worked. I also had to refer to another one of your answers #12112229, didn't know about this bug in VS2012. Thanks for the great and informative answer!Mildamilde
D
2

Another option, could be to create a new action in your controller returning a JsonResult. This json result could return your list. In your page you can call the action with jquery and use it from there.

public ActionResult GetMyList()
{
    var list = GetMyUserList();

    return Json(new { userlist = list }, JsonRequestBehaviour.AllowGet);
}
Depravity answered 8/3, 2013 at 10:21 Comment(1)
Thanks for the tip, I'll try this out too :)Mildamilde
B
2

@Darin Dimitrov answer is spot on. I would just like to add to it if anyone is passing a model and not a viewbag.

<script type="text/javascript">
 var array = @Html.Raw(Json.Encode(
            Model.YourModel.Select(_ => new {
              id = _.Id,
              text = _.Name
            })
          ))

My use case was specific to select2 . One can then just pass the array to the data: attribute

 $("#storeSelect").select2({
        data: array,
        placeholder: "Select something"
 });
</script>

viewmodel

public class YourViewModel
  {
    public IEnumarable<YourPoco> YourPocos { get; set; }
  }

Controller

public class YourController : Controller
{
    public ActionResult Index()
    {
        YourViewModel vm = new YourViewModel{
            // Using Dependancy injection
            YourPocos = yourRepo.GetYourPocos();
        };
        return View("Index", "_Layout",vm);
    }
}

I realise this answer might be redundant, but it is the first time i am using Json.Encode and passing model values to a jquery extension. This for me is too cool. It does to a certain degree create massive extensibility on what would otherwise be an @htmlhelper

Blocky answered 19/6, 2014 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.