/// <summary> /// Occurs when the content was just reloaded. This occurs lazily /// when <see cref="Content"/> property is touched. /// </summary> protected override void OnContentChange() { // Call the base base.OnContentChange(); // Each view is dependant on the code-behind script, with the same name this.Dependancies.Clear(); // Soft requirement, components don't really need to have a code-behind // as it might be purely visual components. if(this.CodeBehind != null) this.Dependancies.AddCodeBehind(this.CodeBehind); // Read the stream and write a string using (var stream = new MemoryStream(this.Content)) using (var reader = new StreamReader(stream)) using (var body = new StringWriter()) { // Read line by line and render the body which is not dependancy string line; while ((line = reader.ReadLine()) != null) { if (!this.Dependancies.TryAddParsed(line)) body.WriteLine(line); } // Create a final text version this.Text = body.ToString(); // Minify HTML this.Text = Regex.Replace(this.Text, @"\n|\t", " "); this.Text = Regex.Replace(this.Text, @">\s+<", "><").Trim(); } // Create a writer for javascript using(var writer = new MetaScriptWriter()) { writer.WriteLine("app.lazy.directive('{0}', ['$parse', function($parse)", this.Name.AsDirectiveName()); writer.WriteLine("{"); writer.WriteLine( "return {"); writer.WriteLine( "restrict: 'E',"); writer.WriteLine( "transclude: true,"); // If we have a code behind for the element, we need to set up the // controller and a linking function. if (this.CodeBehind != null) { // Get the type name of the surrogate controller var type = this.CodeBehind.Surrogate.Type; // Set the controller writer.WriteLine("controller: ['$scope', '$server', '$parse', {0}Ctrl],", type); // Linking function that executes the handhshake for each element writer.WriteLine("link: function(scope, element, attrs)"); writer.WriteLine("{"); //writer.WriteLine("scope.$apply(function(){"); foreach (var property in this.CodeBehind.Surrogate.Properties) { // We only handle settable properties if (!property.HasSetter) continue; writer.WriteLine("scope.{0} = $parse(attrs['{0}'])(scope.$parent);", property.Name); } //writer.WriteLine("});"); //writer.WriteLine("console.debug(scope);"); //writer.WriteLine("console.debug(attrs);"); //writer.WriteLine("console.log('handshake: " + type + " for parent ' + scope.$parent.$i);"); writer.WriteLine("},"); } // Create a completely isolate scope without any bindings writer.WriteLine("scope: {},"); writer.WriteLine( "templateUrl: 'element/{0}',", this.Key); writer.WriteLine( "replace: true"); writer.WriteLine( "};"); writer.WriteLine("}]);"); this.Code = writer.ToString(); } }
/// <summary> /// Compiles several proxies and returns a javascript string. /// </summary> /// <param name="proxies">The proxies to compile in a single file.</param> /// <param name="type">The type of the surrogate to compile.</param> /// <returns>The compiled output.</returns> public static string Compile(SurrogateType type, Surrogate proxy) { using (var writer = new MetaScriptWriter()) { //writer.WriteLine("app.controllerProvider.register('{0}', function ($scope, $server)", proxy.Type); writer.WriteLine("function {0}Ctrl ($scope, $server, $parse)", proxy.Type); writer.WriteLine("{"); // For each property: foreach (var prop in proxy.Properties.Where(p => p.Modifier == SurrogateMemberModifier.Public)) { // A function that attaches a property value to the scope writer.WriteLine("var attach_{0} = function(value)", prop.Name, proxy.Type, proxy.Name); writer.WriteLine("{"); writer.WriteLine( "$scope.{0} = value;", prop.Name); writer.WriteLine("};"); // If we have a getter, add the querying part if (prop.HasGetter) { // Make the getter writer.WriteLine("$scope.get{0} = function()", prop.Name.UppercaseFirst()); writer.WriteLine("{"); writer.WriteLine( "$server.query($scope.$i, '{0}', null, attach_{0});", prop.Name); writer.WriteLine("};"); } // If we have a setter, allow the setter to notify the server if (prop.HasSetter) { // Make the getter and setter writer.WriteLine("$scope.set{0} = function()", prop.Name.UppercaseFirst()); writer.WriteLine("{"); //writer.WriteLine("$server.onPropertySet($scope.$i, '{0}', null, attach_{0});", prop.Name); writer.WriteLine("};"); } // Insert an empty property writer.WriteLine("$scope.{0} = null;", prop.Name); writer.WriteLine(); } // For each function: foreach (var func in proxy.Methods.Where(p => p.Modifier == SurrogateMemberModifier.Public)) { writer.Write("var set_{0} = function(o)", func.Name, proxy.Type, proxy.Name); writer.WriteLine("{"); writer.WriteLine("$scope.result.{0} = o;", func.Name); writer.WriteLine("};"); // Make the function writer.Write("$scope.{0} = function()", func.Name); writer.WriteLine("{"); writer.WriteLine("$server.query($scope.$i, '{0}', $server.makeArgs(arguments), set_{0});", func.Name); writer.WriteLine("};"); } // Gets the handhsake and populates the oid scope writer.WriteLine("$scope.$$w = angular.watchObject;"); writer.WriteLine("var onConstruct = function(oid){"); writer.WriteLine("$scope.$i = oid;"); writer.WriteLine("$scope.result = new Object();"); foreach (var prop in proxy.Properties.Where(p => p.Modifier == SurrogateMemberModifier.Public)) { // For a code-behind of a view only: if (type == SurrogateType.ViewSurrogate) { // Call the getter once if (prop.HasGetter) writer.WriteLine("$scope.get{0}();", prop.Name.UppercaseFirst()); } // If there's a public setter, attach angular.watchObject if (prop.HasSetter) { // In any case, we need to be able to watch the property change and propagate it to // the server. writer.WriteLine("$scope.$$w($server, $parse, '{0}', $server.onPropertySet);", prop.Name); // The element should propagate all properties set originally via its // attributes back to the server, as they might have been bound to some // UI elements and values. if (type == SurrogateType.ElementSurrogate) { // set the new value arguments writer.WriteLine("var propertyValue = new Object();"); writer.WriteLine("propertyValue.target = oid;"); writer.WriteLine("propertyValue.name = '{0}';", prop.Name); writer.WriteLine("propertyValue.value = $scope['{0}'];", prop.Name); // Notify the server writer.WriteLine("$server.onPropertySet(propertyValue);"); } } } writer.WriteLine("};"); // Add a property to the scope with the name writer.WriteLine("$scope.$type = '{0}';", proxy.Type); // Call the view constructor if (type == SurrogateType.ViewSurrogate) writer.WriteLine("$server.view('{0}', onConstruct);", proxy.Type); // Call the element constructor if (type == SurrogateType.ElementSurrogate) writer.WriteLine("$server.element('{0}', $scope.$parent.$type, onConstruct);", proxy.Type); // Handles the property change event writer.WriteLine("$scope.$on('e:property', function (e, args) {"); writer.WriteLine("if(typeof($scope.$i) === 'undefined'){return;}"); writer.WriteLine("if($scope.$i == args.target && $scope.hasOwnProperty(args.name))"); writer.WriteLine( "$scope[args.name] = $server.deserialize(args.value);"); writer.WriteLine("});"); writer.WriteLine("};"); writer.WriteLine(); writer.WriteLine("{0}Ctrl.$inject = ['$scope', '$server', '$parse'];", proxy.Type); // Register as a controller writer.WriteLine("app.lazy.controller('{0}', {0}Ctrl);", proxy.Type); return writer.ToString(); } }