formly-js / angular-formly-templates-lumx

LumX Templates for Angular-Formly
https://af-lumx.herokuapp.com/
MIT License
35 stars 14 forks source link

File Input #12

Closed SagiMedina closed 9 years ago

SagiMedina commented 9 years ago

Hi, Did you consider to add lx-file-input as a field option?

ShMcK commented 9 years ago

I tried lx-file-input quite a while back, it seemed a bit undercooked. I've been resorting to using a raw javascript file-inputer.

Have you been able to get a working demo with lx-file-input?

SagiMedina commented 9 years ago

well, It seems fine here: http://ui.lumapps.com/directives/file-inputs I wonder way not use it in your template...

ShMcK commented 9 years ago

The file-input looks pretty, but how do you manipulate/save the loaded data? There's no example for the javascript behind the scenes.

SagiMedina commented 9 years ago

This is what i had in mind: A watcher will call someMethod in the controller where one can manipulate/save the loaded data. The model will be fill with the url that returned from someMethod^

There are two missing pieces, One - once you choose a file it will automatically upload it - maybe we should consider a button? Two - we will need to add a small circular progress untill the model will be fill with the url - http://ui.lumapps.com/services/progress

ShMcK commented 9 years ago

Would you like to make a demo? You can create your own custom templates using Angular Formly.

Templates should be as flexible as possible, as users may want to make their own design choices.

kentcdodds commented 9 years ago

In addition to the example on the website, you can also watch the newly released lesson about custom templates http://docs.angular-formly.com/v6.1.0/docs/learn-angular-formly#-custom-templates-https-egghead-io-lessons-angularjs-angular-formly-custom-templates- from egghead.io!

-Kent C. Dodds

On Tue, Apr 28, 2015 at 6:29 AM, ShMcK notifications@github.com wrote:

Would you like to make a demo? You can create your own custom templates http://angular-formly.com/#/example/custom-types/custom-templates using Angular Formly.

Templates should be as flexible as possible, as users may want to make their own design choices.

— Reply to this email directly or view it on GitHub https://github.com/formly-js/angular-formly-templates-lumx/issues/12#issuecomment-97045862 .

SagiMedina commented 9 years ago

thanks... this is what i want to do:

<lx-file-input model="::model[options.key]"
               ng-change="::to.uploadFunction(::model[options.key])"
               label="{{to.label}} {{::to.required ? '*' : ''}}">
</lx-file-input>
ShMcK commented 9 years ago

Wow, great work @kentcdodds ! Angular-Formly is really coming along.

@SagiMedina <lx-file-input> has a built-in attribute for change you can use.

See this LumX issue. <lx-file-input change="to.uploadFunction"></lx-file-input>

$scope.uploadFunction(element) {
    console.log(element);
}
kentcdodds commented 9 years ago

FYI, you don't need to add an ng-change directive. Simply add templateOptions.onChange and angular-formly will add the ng-change directive for you :-)

-Kent C. Dodds

On Tue, Apr 28, 2015 at 6:46 AM, SagiMedina notifications@github.com wrote:

thanks... this is what i want to do:

<lx-file-input model="::model[options.key]" ng-change="::to.uploadFunction(::model[options.key])" label="{{to.label}} {{::to.required ? '*' : ''}}">

— Reply to this email directly or view it on GitHub https://github.com/formly-js/angular-formly-templates-lumx/issues/12#issuecomment-97049572 .

kentcdodds commented 9 years ago

Ah hah! If you have a custom attribute, you could potentially add the ngModelAttrs to the defaultOptions of your type. See http://docs.angular-formly.com/v6.1.0/docs/ngmodelattrs

But it may be easier to just add the change directive yourself :-)

-Kent C. Dodds

On Tue, Apr 28, 2015 at 7:00 AM, ShMcK notifications@github.com wrote:

Wow, great work @kentcdodds https://github.com/kentcdodds ! Angular-Formly is really coming along.

@SagiMedina https://github.com/SagiMedina has a built-in attribute for change you can use.

See this LumX issue https://github.com/lumapps/lumX/issues/25.

$scope.uploadFunction(element) { console.log(element); }

— Reply to this email directly or view it on GitHub https://github.com/formly-js/angular-formly-templates-lumx/issues/12#issuecomment-97053155 .

SagiMedina commented 9 years ago

@kentcdodds I think I will have to use the ngModelAttrs since I can't use braces in lx-file-input's change attribute. But I'm having a hard time with that, don't understand the example that you gave... Can you please help me?

<lx-file-input label="someLabel" change="uploadFunction(element)"></lx-file-input>

All I need is to add the change attribute, with ngModelAttrs and bind it to a uploadFunction(element) that is implemented in the controller.

SagiMedina commented 9 years ago

Sorry for the delay. So, Here is my solution: (you get lumx file input with linear progress while the file is uploading and two buttons once its finish, one for download and one for delete or change file).

app.config.js:

(function () {
    'use strict';

    angular
        .module('app')
        .config(config);

    config.$inject = ['formlyConfigProvider'];

    function config(formlyConfigProvider){

        formlyConfigProvider.setType({
            name: 'lx-file-input',
            templateUrl: '/static/app/blocks/file_input_formly/file_input_formly.html'
        });
    }
})();

file_input_formly.html:

<div flex-item>
    <div ng-hide="model[options.key].hasFile">
        <file-upload controller="{{::to.controller}}"
                     controller-as="{{::to.controllerAs}}"
                     upload-function="{{::to.uploadFunction}}"
                     label="{{::to.label}} {{::to.required ? '*' : ''}}">
        </file-upload>
    </div>
    <div id="{{ this.options.key }}"></div>
    <div ng-if="model[options.key].hasFile" style="padding: 25px 0 15px">
        <a href="{{ ::model[options.key].url }}" target="_blank" class="btn btn--m btn--blue btn--raised" lx-ripple> {{::to.downloadBtn}} {{::to.label}}</a>
        <button ng-click="model[options.key].hasFile = null" class="btn btn--m btn--blue btn--flat" lx-ripple>{{::to.replaceBtn}}</button>
    </div>
</div>

file_upload_directive.js:

(function () {
    'use strict';

    angular
        .module('app')
        .directive('fileUpload', fileUpload);

    fileUpload.$inject = ['$compile'];

    /* @ngInject */
    function fileUpload($compile) {

        var directive = {
            restrict: 'E',
            link: link
        };
        return directive;

        function link(scope, element, attrs) {
            directive.controller = attrs.controller;
            directive.controllerAs = attrs.controllerAs;
            var htmlText = '<lx-file-input ' +
                'change="'+attrs.controllerAs+'.'+attrs.uploadFunction+'" ' +
                'label="'+attrs.label+'"></lx-file-input>';
            element.replaceWith($compile(htmlText)(scope));
        }
    }
})();

formly input:

      {
        "key": "someFile",
        "type": "lx-file-input",
        "templateOptions": {
          "controller": "worker",
          "controllerAs": "workerCtrl",
          "uploadFunction": "fileUpload(e, this.options)",
          "downloadBtn": "Download",
          "replaceBtn": "Delete/Replace",
          "label": "This is some file input"
        }
      }

controller.js:

(function () {
    'use strict';

    angular
        .module('app.worker')
        .controller('worker', worker);

    worker.$inject = ['$q', 'LxProgressService', '$scope', 's3upload'];

    function worker($q, LxProgressService, $scope, 's3upload') {
        var vm = this;
        vm.worker = {};
        vm.formData = {};
        vm.fileUpload = fileUpload;

        activate();

        ////////////////

        function activate() {

            $scope.$on('fileChange', function(event, data){
                LxProgressService.linear.hide('#'+data.model);
                vm.worker[data.model] = {};
                vm.worker[data.model].url = data.value.replace(/%2F/g, '/');
                vm.worker[data.model].hasFile = true;
            });
        }

        function fileUpload(file, options){
            var model = options.key;
            LxProgressService.linear.show('#5fa2db', '#'+model);
            $q.when(s3upload.uploadFile(file, vm.worker.id)).then(function(url){
                var data = { model: model, value: url.data};
                $scope.$emit('fileChange', data);
            });
        }
    }
})();

I will provide screen shots later.

ShMcK commented 9 years ago

Great! Could I add this to the example demos? If so, I'll post credit underneath such as a link to your twitter, etc.

SagiMedina commented 9 years ago

Sure! I hope it will come in use. You can add my mail for those who want help with this. SagiMedina@Gmail.com My twitter account is inactive ☺

ShMcK commented 9 years ago

Thanks again SagiMedina, I've posted your quite thorough example in the demos section, you can see it here: https://af-lumx.herokuapp.com/#/fileInput

Let me know if you'd rather I remove the email.

I had to comment out parts of the code in the demo, otherwise I would be saving random files to my server. Thanks again! If you come up with any more demos, send them this way.

SagiMedina commented 9 years ago

@ShMcK I should be thanking you! Your template is awesome. Let me know if you want me to write another example, one that will fit your examples.

ShMcK commented 9 years ago

Thanks! Anything you come across that you wasn't clear or easy to do, an example would always help others. I'll close this issue.

ciclick commented 8 years ago

Hi everyone,

I'm tryng to implement this solution using requirejs and it seems that the neither the "activate" and "fileUpload" functions inside de controller.js file are called. The value of the input field is not loaded into the model too.

Also the demo in https://af-lumx.herokuapp.com/#/fileInput seems to have the same problems.

Are you sure this solution works? Can you provide an example using requirejs?

Thank you very much!!

ShMcK commented 8 years ago

The solution provided requires an Amazon S3 account, which is not activated here. Any working example would require an Amazon account & some data fees, neither of which I'm currently paying for.

Take a look again at the code behind the example for an idea of how you might be able to implement it on your own server account. Actually, this solution was thankfully contributed by another user, but I've been told that it works.

The example using Webpack, but the working code should be the same as require.js.

SagiMedina commented 8 years ago

@ciclick First you need to add your custom directive to formly:

(function () {
    'use strict';

    angular
        .module('YOUR_MODULE')
        .config(config);

    config.$inject = ['formlyConfigProvider'];

    function config(formlyConfigProvider){

        formlyConfigProvider.setType({
            name: 'lx-file-input',
            templateUrl: '/static/app/blocks/file_input_formly/file_input_formly.html'
        });
    }
})();

Looks something like this:

<div flex-item>
        <file-upload upload-function="{{::to.uploadFunction}}"
                     label="{{::to.label}} {{::to.required ? '*' : ''}}">
        </file-upload>
</div>

The custom directive will use lumx directive and it will emit function in your controller once file has been chosen:

(function () {
    'use strict';

    angular
        .module('YOUR_MODULE')
        .directive('fileUpload', fileUpload);

    fileUpload.$inject = ['$compile'];

    /* @ngInject */
    function fileUpload($compile) {

        var directive = {
            restrict: 'E',
            scope: {
                uploadFunction: '@',
                label:'@'
            },
            link: link,
            controller:controller
        };
        return directive;

        function link(scope, element, attrs) {
            var htmlText = '<lx-file-input ' +
                'change="fileUpload(e)" ' +
                'label="'+attrs.label+'"></lx-file-input>';
            element.replaceWith($compile(htmlText)(scope));
        }

        function controller($scope){
            $scope.fileUpload = function(file){
                var functionToEmit = $scope.uploadFunction.toString();
                var data = { file: file, scope: $scope};
                $scope.$emit(functionToEmit , data);
            }
        }
    }
})();

And in your controller you need to 'wait' for that event:

(function () {
    'use strict';

    angular
        .module('YOUR_MODULE')
        .controller('YOUR_CONTROLLER', YOUR_CONTROLLER);

    YOUR_CONTROLLER.$inject = ['$scope''];

    function YOUR_CONTROLLER($scope) {
        var vm = this;
        vm.activate = activate;

        activate();

        ////////////////

        function activate() {
            $scope.$on('YOUR_CONTROLLER_EVENT', function(event, data){
                fileUpload(data.file);
            });
        }

        function fileUpload(file, options){
            YOUR_UPLOAD_CODE_HERE
        }
})();

Of course in your json you will need label and uploadFunction which is the YOUR_CONTROLLER_EVENT

Hope it is clear enough...

ciclick commented 8 years ago

You explained it very well and it works perfect! I just have to do some little changes to adapt it to requirejs.

Thank you very much SagiMedina!!!

ciclick commented 8 years ago

Hi everyone,

I've another problem now. I want to generate formly-forms dinamically with a file-upload in this forms using ng-repeat. Each form is multiplied depending on an array (in my example vm.idioms). What I want is when the user fills the input fields the model become filled like this:

vm.model = { blocks: { block0: { ca: {Image: xxx.jpg}, es: {Image: xxx.jpg}, en: {Image: xxx.jpg} }, block1: { ca: {Image: xxx.jpg}, es: {Image: xxx.jpg}, en: {Image: xxx.jpg} } }

The problem is the function fileUpload() that assigns the return value to the model doesn't have the correct path (vm.model.blocks.blockx.xx) and I can't figure out how to pass it to the function.

Can you help me?

Here a working jsbin example: http://jsbin.com/muqaqezavo/1/edit?html,js,console,output

Thank you very much!