asp.net mvc - A well-defined structure for javascript object model and organization for javascript files in an ASP .NET MVC application -
when applying mvvm pattern using library knockout, kendo, angular... in mvc .net application have deal concerns:
how organize , render scripts efficiently when scripts needed view view (view-specific script)? how associate info between model @ server-side , view-model @ client-side without manually declaring view-model? how facilitate mvvm apis bind view-specific script html element in view?below approach concerns:
firstly, need core functions allow extending js objects in inheritance structure. based on jquery, can done in file app.core.js this:
(function ($) { appglobal = function () { }; appglobal.prototype = { dot: '.', init: appglobal, namespace: function (namespace, context) { context = context || window; var index = namespace.indexof(this.dot); if (index > 0) { var subnamespace = namespace.substring(index + 1); var subcontextname = namespace.substring(0, index); context[subcontextname] = context[subcontextname] || {}; homecoming this.namespace(subnamespace, context[subcontextname]); } context[namespace] = context[namespace] || {}; homecoming context[namespace]; }, extend: function (protobj, skipbaseconstructor) { protobj = protobj || {}; var subclass = null; var baseconstructor = this; if (typeof (baseconstructor) != "function") { baseconstructor = this.init; } if (protobj.init) { subclass = function () { if (!skipbaseconstructor) { baseconstructor.apply(this, arguments); } protobj.init.apply(this, arguments); }; } else { subclass = function () { if (!skipbaseconstructor) { baseconstructor.apply(this, arguments); } }; protobj.init = baseconstructor; } subclass.prototype = subclass.prototype || {}; $.extend(true, subclass.prototype, this.prototype, protobj); subclass.extend = this.extend; homecoming subclass; }, }; app = new appglobal(); app.core = app.extend({ }); })(jquery); place file in ~\scripts folder , register in bundling config @ server-side:
bundles.add(new scriptbundle("~/js/app/core").include("~/scripts/app*")); we can reference core script in layout re-used in views based on layout:
@scripts.render("~/bundles/jquery") @scripts.render("~/bundles/knockout") @scripts.render("~/js/app/core") @rendersection("scripts", required: false) secondly, need base of operations view-specific script objects. it, @ least, needs hold reference html element plays container in view. element bound view-model , can scope of import using jquery selector avoid mistakes duplicate id, unexpected matching element,... facilitating jquery , knockout, can this:
(function ($, ko) { app.namespace("app.view").viewbase = app.core.extend({ init: function (options) { this.element = options.element; } }); app.namespace("app.view").koview = app.view.viewbase.extend({ init: function (options) { var model = options.data; if (model) { (var prop in model) { model[prop] = ko.observable(model[prop]); } $.extend(this, model); } ko.applybindings(this, this.element); } }); })(jquery, ko); to rendered in core script bundle, script should named app.view.js , placed in ~\scripts folder. mechanism "data" , "element" beingness passed through parameter "options" of constructors covered later.
next, work markups of view applied mvvm like, example, view edit of model customer:
@model customermodel @{ viewbag.title = "edit"; } <h2> edit</h2> @using (html.beginform()) { <fieldset> <legend>customer</legend> <div class="editor-label"> @html.labelfor(model => model.name) </div> <div class="editor-field"> <input type="text" name="name" data-bind="value: name" /> @html.validationmessagefor(model => model.name) </div> <div class="editor-label"> @html.labelfor(model => model.phone) </div> <div class="editor-field"> <input type="text" name="phone" data-bind="value: phone"/> @html.validationmessagefor(model => model.phone) </div> <p> <input type="button" value="hello" data-bind="click: hello"/> <input type="submit" value="save" /> </p> </fieldset> } this view has html elements binding (using knockout) properties of customermodel returned edit action:
public class customermodel { public string name { get; set; } public string phone { get; set; } } public class customercontroller : controller { public actionresult edit(int id) { var model = new customermodel {name = "a name", phone = "a number"}; homecoming view(model); } } and event handler button "hello".
normally, have define view-model object @ client-side has respective properties , function handle button event. going approach, don't need to. accomplish that, add together more functions in app.core.js:
(function ($) { //... appglobal.prototype = { //... view_options: 'view-options', evaluate: function (expression) { look = expression.replace(/(\w+ *):/g, '"$1":'); if (!/^({.*})$/.test(expression)) { look = '({' + look + '})'; } homecoming eval(expression); }, initview: function (element, data) { var viewoptions = $(element).data(this.view_options); if (viewoptions) { viewoptions = viewoptions.replace(/data *: *model\.(.+)/i, 'data: this.$1'); var options = this.evaluate.apply(data, [viewoptions]); var settings = { element: element, data: options.data || info }; if (options.options) { $.extend(settings, options.options); } homecoming new options.type(settings); } } }; //... })(jquery); then extend jquery function (put in app.jquery.extensions.js , place in ~\script folder):
(function ($) { $.fn.extend({ initviews: function (data) { var viewoptionsattr = 'data-' + app.view_options var elements = this.find('div[' + viewoptionsattr + '], fieldset[' + viewoptionsattr + ']'); var views = []; $(elements).each(function (index, element) { var view = app.initview(element, data); if (view) { views.push(view); } }); homecoming views; } }); $.initviews = $.fn.initviews; })(jquery); before using "initviews" function, need view-specific script edit.js:
(function ($) { app.namespace("app.view.customer").edit = app.view.koview.extend({ init: function (options) { this.title = options.title; this.phone('12345'); }, hello: function (e) { alert('hello' + this.name()); } }); })(jquery); and using html extension:
public static ihtmlstring registerviewscripts(this htmlhelper helper) { var bundles = bundletable.bundles; const string virtualpath = "~/js/app/view"; var view = helper.viewcontext.view.getviewname(); var path = string.format("~/scripts/{0}/{1}.js", (string)helper.viewcontext.routedata.getrequiredstring("controller"), view); bundles.add(new scriptbundle(virtualpath).include(path)); homecoming scripts.render(virtualpath); } along placing edit.js in ~\script\customer, can render in view edit of client below:
@section scripts { @html.registerviewscripts() } after that, modify view markups bind "initviews":
<fieldset data-view-options="type: app.view.customer.edit, options: {title: '@viewbag.title'}"> finally, phone call "initviews" in layout:
@scripts.render("~/bundles/jquery") @scripts.render("~/bundles/knockout") @scripts.render("~/js/app/core") @rendersection("scripts", required: false) <script type="text/javascript"> $(function(){ $.initviews(@(html.raw(json.encode(model)))); }); </script> to bind container element in view ~\views\customer\edit.cshtml js object defined in ~\scripts\customer\edit.js without creating properties in client-side view-model object.
javascript asp.net-mvc mvvm knockout.js
No comments:
Post a Comment