LanFeusT
LanFeusT

Reputation: 2432

GitHub Tree Slider implementation on MVC3

I'm trying to implement the same tree slide in my MVC3 app and I'm not entirely sure I'm doing it right:

Index page:

<script type="text/javascript">
    $(document).ready(function () {
        $('#slider a').click(function () {
            history.pushState({ path: this.path }, '', this.href);
            $.get(this.href, function (data) {
                $('#slider').slideTo(data);
            });
            return false;
        });
    });
</script>

@{Html.RenderAction("MainTableTracker", "Release", new { releaseId = Model });}

MainTableTracker partial view:

<script type="text/javascript">
    $(document).ready(function () {
        $('#slider a').click(function () {
            history.pushState({ path: this.path }, '', this.href);
            $.get(this.href, function (data) {
                $('#slider').slideTo(data);
            });
            return false;
        });
    });
</script>

<div id="slider">
    @Html.RouteLink(Model.Brand.Name, "Brand", new { BrandId = Model.Brand.Id }) /
    <ul>
        <li><a href="#">Overview</a></li>
        @foreach(var model in Model.ModelExts)
        {
            <li>@Html.RouteLink(model.Model.Name, new { modelId = model.Model.Id })</li>
        }
    </ul>
</div>

VersionTable partial view:

<script type="text/javascript">
    $(document).ready(function () {
        $('#slider a').click(function () {
            history.pushState({ path: this.path }, '', this.href);
            $.get(this.href, function (data) {
                $('#slider').slideTo(data);
            });
            return false;
        });
    });
</script>


<div id="slider">
    @Html.RouteLink(Model.Brand.Name, "Brand", new { BrandId = Model.Brand.Id }) / @Html.RouteLink(Model.Name, "Model", new { modelId = Model.Id }) /
    <ul>
        <li><a href="#">Overview</a></li>
        @foreach(var model in Model.Brand.Model)
        {
            <li>@Html.RouteLink(model.Name, new { modelId = model.Id })</li>
        }
    </ul>

    <div class="versionWraper">
        @foreach(var version in Model.Version)
        {
            <div>
                @version.Name
            </div>
        }
    </div>
</div>

This works, however I'm pretty sure I'm doing it wrong, I'm guessing I shouldn't have 3 different views copying the code of the parent view everytime.

I'm wondering if I should just have one viewmodel and one action and let the view decide what to display everytime? But I'm not really happy about using just one action to display different types of data (Release, Brand and Feature).

Also, the layout has:

 $(window).bind('popstate', function (e) {
        $.get(e.originalEvent.state.path, function (data) {
            $('#slider').slideTo(data);
        });
    });

That works, but when I go back to the Brand level, it will reload all my scripts (being that it's a View not a Partial for the default one).

Is there any better way of implementing this?

Thanks

Upvotes: 4

Views: 581

Answers (1)

Troy Alford
Troy Alford

Reputation: 27236

When designing something like this, I would encourage you not to target the objects by ID. It puts you in odd situations like the one you're facing, where you want to guarantee that the item is on the page before the script fires and that sort of thing - which can be inherently problematic when dealing with partial loading and such.

Consider, instead, this sort of approach:

<style>
    .slider { /* Styles here */ }
    .slider > ul { /* Styles here */ }
    .slider > ul > li { /* Styles here */ }
    .slider > .versionWrapper { /* Styles here */ }
    .slider > .versionWrapper > .versionName { /* Styles here */ }
</style>

<div class="slider">
    @Html.RouteLink(Model.Brand.Name, "Brand", new { BrandId = Model.Brand.Id })
    / @Html.RouteLink(Model.Name, "Model", new { modelId = Model.Id }) /
    <ul>
     <li><a href="#">Overview</a></li>

    @foreach(var model in Model.Brand.Model) {
     <li>@Html.RouteLink(model.Name, new { modelId = model.Id })</li>
    }

    </ul>
    <div class="versionWrapper">

    @foreach(var version in Model.Version) {
     <div class="versionName">@version.Name</div>
    }

    </div>
</div>

<script type="text/javascript" language="javascript">
$(document).ready(function () {
    $('.slider > ul > li > a').live('click', function (ev) {
        ev.preventDefault();
        ev.stopPropagation();

        var $a = $(ev.target);
        var $slider = $a.closest('.slider');

        history.pushState({ path: $a.path }, '', $a.href);
        $.get($a.href, function (data) {
            $slider.slideTo(data);
        });
    });
});
</script>

At this point, you are effectively targeting ANY slider you put on the page, regardless of what partial it is wrapped in - and the small amount of jQuery logic at the end can be easily put in a site-wide or page-wide JS file, decoupled from the specific implementation which outputs your HTML.

Next, simply create a toggle for which mode you're in. I like doing this with ViewData["SettingName"], but that's just me.

If you want your "MainTable" style view to display, set something like:

ViewData["Mode"] = "Default";

If you want your "Version" style view to display, set:

ViewData["Mode"] = "Version";

Then alter your partial to render as:

<div class="slider">
@{
    bool versionMode = (ViewData["Mode"] as String == "Version");

    string breadCrumb = Html.RouteLink(
        Model.Brand.Name, 
        "Brand", 
        new { BrandId = Model.Brand.Id }
    ).ToString();

    if (versionMode) {
        breadCrumb = String.Format("{0} / {1}", 
            breadCrumb, 
            Html.RouteLink(
                Model.Name, 
                "Model", 
                new { modelId = Model.Id }
            ).ToString()
        );
    }
}

    @breadCrumb /
    <ul>
        <li><a href="#">Overview</a></li>
@{
    if (versionMode)
        foreach(var model in Model.ModelExts) {

        <li>@Html.RouteLink(model.Model.Name, new { modelId = model.Model.Id })</li>

        }
    } else {

        @foreach(var model in Model.Brand.Model) {
            <li>@Html.RouteLink(model.Name, new { modelId = model.Id })</li>
        }

    }
}
    </ul>

@{
    if (versionMode) {

    <div class="versionWrapper">
        @foreach(var version in Model.Version) {
        <div>@version.Name</div>
        }
    </div>

    }
}

</div>

Upvotes: 2

Related Questions